// ==UserScript== // @name Idealista Enhancer // @description Just some information for idealista.com // @version 0.1.0 // @author Midefos // @namespace https://github.com/Midefos // @match https://www.idealista.com/* // @require https://code.jquery.com/jquery-3.6.0.slim.min.js // @license MIT // ==/UserScript== /* jshint esversion: 6 */ this.$ = this.jQuery = jQuery.noConflict(true); // Idealista selectors. const ITEM_SELECTOR = 'article.item'; const NAME_SELECTOR = '.item-link'; const PRICE_SELECTOR = '.item-price'; const ROOM_SELECTOR = '.item-detail-char .item-detail:nth-child(1)'; const METERS_SELECTOR = '.item-detail-char .item-detail:nth-child(2)'; const ADDITIONAL_INFORMATION_SELECTOR = '.item-detail-char .item-detail:nth-child(3)'; // Own selectors. const CONFIG_CLASS = 'midefos-idealista-config' const DEFAULT_CONFIG = { enabled: true, percentages: true, garage: false, exterior: true, lever: true, 'max-price': 120_000, 'max-price-per-meter': 1_500 } const STYLES = ` .midefos-idealista-container { display: flex; align-items: center; justify-content: space-around; width: 100%; height: 55px; margin-top: 5px; background-color: white; box-shadow: 0 3px 6px rgba(225, 245, 110, 0.16), 0 3px 6px rgba(225, 245, 110, 0.23); } .midefos-idealista-menu { display: flex; align-items: center; justify-content: space-around; width: 100%; height: 40px; background-color: white; box-shadow: 0 3px 6px rgba(225, 245, 110, 0.16), 0 3px 6px rgba(225, 245, 110, 0.23); } .${CONFIG_CLASS} { display: none; position: fixed; z-index: 3; background-color: rgba(225, 245, 110, 0.90); width: 95%; height: 95%; top: 2.5%; left: 2.5%; padding: 2rem; } .success { color: darkgreen; } .warning { color: darkorange; } .error { color: darkred; } `; let currentConfig; function init() { currentConfig = getConfig() addStyles(); createMenu(); createInformation(); } function getConfig() { const storageConfig = localStorage.getItem('midefos-idealista'); if (!storageConfig) return DEFAULT_CONFIG; return JSON.parse(storageConfig); } function setConfig(config) { localStorage.setItem('midefos-idealista', JSON.stringify(config)); currentConfig = config; } function createMenu() { if (window.location.href.includes('mapa-google')) return; $('body').on('click', '#reload-midefos-idealista', () => { createInformation(); }); $('body').on('click', '.config-open', () => { $(`.${CONFIG_CLASS}`).toggle(); }); $('body').on('click', '.config-save', () => { const $configNode = $(`.${CONFIG_CLASS}`); const configNode = $configNode[0]; const newConfig = { enabled: configNode.querySelector('#enabled').checked, percentages: configNode.querySelector('#percentages').checked, garage: configNode.querySelector('#garage').checked, exterior: configNode.querySelector('#exterior').checked, lever: configNode.querySelector('#lever').checked, 'max-price': configNode.querySelector('#max-price').value, 'max-price-per-meter': configNode.querySelector('#max-price-per-meter').value } setConfig(newConfig); createInformation(); $configNode.toggle(); }); document.querySelector('#main-header').innerHTML += createConfigHTML(); const mainContent = document.querySelector('#main-content'); mainContent.innerHTML = createMenuHTML() + mainContent.innerHTML; } function createInformation() { const items = document.querySelectorAll(ITEM_SELECTOR); for (const item of items) { const currentInformation = item.querySelector('.midefos-idealista-container'); if (currentInformation) currentInformation.remove(); item.innerHTML += createItemInformationHTML(item); } } function createConfigHTML() { return `

Midefos Idealista

Configuración global:

Busqueda:







` } function createMenuHTML() { return `
`; } function createItemInformationHTML(item) { return `
${createPercentagePriceHTML(item)} ${createPriceHTML(item)} ${createPriceMeterHTML(item)} ${createLeverHTML(item)} ${createInteriorHTML(item)}
` } function findPrice(item) { const priceText = item.querySelector(PRICE_SELECTOR).textContent; return Number(priceText.replace('€', '').replace('.', '').replace(',', '')); } function findMeters(item) { let metersText = item.querySelector(METERS_SELECTOR).textContent; if (!metersText.includes('m²')) metersText = item.querySelector(ROOM_SELECTOR).textContent; return Number(metersText.replace('m²', '')); } function findPriceMeter(item) { const meters = findMeters(item); const price = findPrice(item); return Math.round(price / meters); } function createPercentagePriceHTML(item) { if (!currentConfig.percentages) return ``; const price = findPrice(item); const twentyPercent = Math.round(price * 20 / 100); const thirtyPercent = Math.round(price * 30 / 100); return ` 20%: ${twentyPercent}€
30%: ${thirtyPercent}€
`; } function createPriceHTML(item) { const price = findPrice(item); const desiredPrice = currentConfig['max-price']; const desiredTwentyFivePercentMore = Math.round(desiredPrice * 1.25); if (price <= desiredPrice) { return ` Precio`; } else if (price <= desiredTwentyFivePercentMore) { return ` Precio`; } return `X Precio`; } function createPriceMeterHTML(item) { const priceMeter = findPriceMeter(item); const desiredPricePerMeter = currentConfig['max-price-per-meter']; const desiredTwentyFivePercentMore = Math.round(desiredPricePerMeter * 1.25); if (priceMeter <= desiredPricePerMeter) { return `m²: ${priceMeter}€`; } else if (priceMeter <= desiredTwentyFivePercentMore) { return `m²: ${priceMeter}€`; } return `m²: ${priceMeter}€`; } function createLeverHTML(item) { if (!currentConfig.lever) return ``; const additionalInfo = item.querySelector(ADDITIONAL_INFORMATION_SELECTOR); if (!additionalInfo) { if (!isFlat(item)) return ``; return `? Ascensor` } const leverText = additionalInfo.textContent; const hasLever = leverText.includes('con ascensor'); if (hasLever) { return ` Ascensor` } return `X Ascensor`; } function createInteriorHTML(item) { if (!currentConfig.exterior) return ``; const additionalInfo = item.querySelector(ADDITIONAL_INFORMATION_SELECTOR); if (!additionalInfo) { if (!isFlat(item)) return ``; return `? Exterior` } const interiorText = additionalInfo.textContent; const hasLever = interiorText.includes('exterior'); if (hasLever) { return ` Exterior` } return `X Exterior`; } function isFlat(item) { return item.querySelector(NAME_SELECTOR) .textContent.includes('Piso'); } init(); function addStyles() { const style = document.createElement('style'); style.textContent = STYLES; document.head.appendChild(style); }