diff --git a/bundler.js b/bundler.js index feb32d5..4c362b0 100644 --- a/bundler.js +++ b/bundler.js @@ -5,13 +5,12 @@ import banner from 'browserify-banner'; const STATIC_BANNER = "// ==UserScript==\n" + "// @name Idealista Enhancer\n" + "// @description Just some information for idealista.com\n" + - "// @version 0.1.0\n" + + "// @version 0.2.0\n" + "// @author Midefos\n" + "// @namespace https://github.com/Midefos\n" + "// @match https://www.idealista.com/*\n" + "// @license MIT\n" + - "// ==/UserScript==\n" + - "/* jshint esversion: 6 */\n\n"; + "// ==/UserScript==\n\n"; browserify("./app.js") .transform("babelify", { presets: ["@babel/preset-env"] }) diff --git a/src/CheckboxHTML.js b/src/CheckboxHTML.js new file mode 100644 index 0000000..69cb3e1 --- /dev/null +++ b/src/CheckboxHTML.js @@ -0,0 +1,17 @@ +import Log from "./Log.js"; + +export default class CheckboxHTML { + + static create(id, value) { + const inputElement = document.createElement('input'); + if (value) { + Log.debug(`Creating checkbox ${id}, value: ${value}`) + inputElement.setAttribute('checked', true); + } + + inputElement.id = id; + inputElement.type = 'checkbox'; + return inputElement.outerHTML; + } + +} \ No newline at end of file diff --git a/src/ConfigurationHTML.js b/src/ConfigurationHTML.js index ed941c2..9bbe594 100644 --- a/src/ConfigurationHTML.js +++ b/src/ConfigurationHTML.js @@ -1,4 +1,5 @@ import ButtonClass from "./ButtonClass.js"; +import CheckboxHTML from "./CheckboxHTML.js"; import Preferences from "./Preferences.js"; export default class ConfigurationHTML { @@ -25,32 +26,32 @@ export default class ConfigurationHTML {

Configuración global:

Busqueda:





@@ -71,7 +72,7 @@ export default class ConfigurationHTML { - ` + ` } } \ No newline at end of file diff --git a/src/Item.js b/src/Item.js index 2280f78..2735f8b 100644 --- a/src/Item.js +++ b/src/Item.js @@ -4,6 +4,7 @@ export default class Item { static NAME_SELECTOR = '.item-link'; static PRICE_SELECTOR = '.item-price'; + static ROOM_SELECTOR = '.item-detail-char .item-detail:nth-child(1)'; static METERS_SELECTOR = '.item-detail-char .item-detail:nth-child(2)'; static ADDITIONAL_INFORMATION_SELECTOR = '.item-detail-char .item-detail:nth-child(3)'; @@ -19,6 +20,7 @@ export default class Item { this.additionalInfo = this._extractAdditionalInfo(); this.hasLift = this._extractLift(); this.isExterior = this._extractExterior(); + this.isInterior = this._extractInterior(); } get _data() { @@ -31,30 +33,43 @@ export default class Item { } isDataRendered() { - const nextElement = this._data; + const nextElement = this._data; return nextElement.className && nextElement.className.includes(ItemHTML.CONTAINER_CLASS_NAME); } + isProperPrice(price) { + return this.price <= price; + } + + isProperPriceMeter(priceMeter) { + return this.priceMeter <= priceMeter; + } + removeData() { if (!this.isDataRendered()) return; this._data.remove(); } addData() { - this._node.outerHTML += ItemHTML.createInformation(this); + this._node.insertAdjacentHTML('afterend', ItemHTML.createInformation(this)); } isFlat() { - return this.name.includes('Piso'); + return this._nameIncludes('Piso'); } isHouse() { - return this.name.includes('Casa'); + return this._nameIncludes('Casa') + || this._nameIncludes('Chalet'); } isGround() { - return this.name.includes('Bajo'); + return this._nameIncludes('Bajo'); + } + + _nameIncludes(name) { + return this.name.toLowerCase().includes(name.toLowerCase()); } _extractName() { @@ -79,8 +94,10 @@ export default class Item { } _extractAdditionalInfo() { - const additionalInfo = this._node.querySelector(Item.ADDITIONAL_INFORMATION_SELECTOR); - if (!additionalInfo) return null; + let additionalInfo = this._node.querySelector(Item.ADDITIONAL_INFORMATION_SELECTOR); + if (!additionalInfo) { + additionalInfo = this._node.querySelector(Item.METERS_SELECTOR); + } return additionalInfo.textContent; } @@ -94,4 +111,9 @@ export default class Item { return this.additionalInfo.includes('exterior'); } + _extractInterior() { + if (!this.additionalInfo) return false; + return this.additionalInfo.includes('interior'); + } + } \ No newline at end of file diff --git a/src/ItemHTML.js b/src/ItemHTML.js index 8eab843..53073af 100644 --- a/src/ItemHTML.js +++ b/src/ItemHTML.js @@ -2,115 +2,168 @@ import Preferences from "./Preferences.js"; export default class ItemHTML { + static SUCCESS_CLASS_NAME = 'success'; + static WARNING_CLASS_NAME = 'warning'; + static ERROR_CLASS_NAME = 'error'; + static CONTAINER_CLASS_NAME = 'midefos-idealista-container'; static get CONTAINER_SELECTOR() { return `.${this.CONTAINER_CLASS_NAME}`; } + static INFORMATION_CLASS_NAME = 'information' + static get INFORMATION_SELECTOR() { + return `.${this.INFORMATION_CLASS_NAME}`; + } + + static ITEM_STATE_CLASS_NAME(item) { + const desiredPrice = Preferences.get('max-price'); + if (desiredPrice && !item.isProperPrice(desiredPrice)) { + return this.ERROR_CLASS_NAME; + } + + const desiredPricePerMeter = Preferences.get('max-price-per-meter'); + if (desiredPricePerMeter && !item.isProperPriceMeter(desiredPricePerMeter)) { + return this.ERROR_CLASS_NAME; + } + + if (this._shouldCheckLift(item) && !item.hasLift) { + return this.ERROR_CLASS_NAME; + } + + if (this._shouldCheckExterior(item) && !item.isExterior) { + return this.ERROR_CLASS_NAME; + } + return this.SUCCESS_CLASS_NAME; + } + static createInformation(item) { return ` -
+
${this._createPercentagePriceHTML(item)} ${this._createPriceHTML(item)} ${this._createPriceMeterHTML(item)} ${this._createLiftHTML(item)} - ${this._createInteriorHTML(item)} + ${this._createExteriorHTML(item)}
` } - + static _createPercentagePriceHTML(item) { if (!Preferences.get('percentages')) return ``; - + const twentyPercent = Math.round(item.price * 20 / 100); const thirtyPercent = Math.round(item.price * 30 / 100); const fiftyPercent = Math.round(item.price * 50 / 100); return ` -
- 20%: ${twentyPercent}€ - 30%: ${thirtyPercent}€ - 50%: ${fiftyPercent}€ +
+ ${this._createIndividual('20%', twentyPercent)} + ${this._createIndividual('30%', thirtyPercent)} + ${this._createIndividual('50%', fiftyPercent)}
`; } - - + static _createPriceHTML(item) { const desiredPrice = Preferences.get('max-price'); if (!desiredPrice) return ``; const desiredTwentyFivePercentMore = Math.round(desiredPrice * 1.25); - - if (item.price <= desiredPrice) { + + if (item.isProperPrice(desiredPrice)) { return this._createSuccess('Precio'); - } else if (item.price <= desiredTwentyFivePercentMore) { + } else if (item.isProperPrice(desiredTwentyFivePercentMore)) { return this._createWarning('Precio'); } return this._createError('Precio'); } - - + static _createPriceMeterHTML(item) { const desiredPricePerMeter = Preferences.get('max-price-per-meter'); - if (!desiredPricePerMeter) return `m²: ${item.priceMeter}€`; + if (!desiredPricePerMeter) return this._createIndividual('m²', item.priceMeter); const desiredTwentyFivePercentMore = Math.round(desiredPricePerMeter * 1.25); - + if (item.priceMeter <= desiredPricePerMeter) { - return `m²: ${item.priceMeter}€`; + return this._createTextSuccess('m²', item.priceMeter) } else if (item.priceMeter <= desiredTwentyFivePercentMore) { - return `m²: ${item.priceMeter}€`; + return this._createTextWarning('m²', item.priceMeter) } - return `m²: ${item.priceMeter}€`; + return this._createTextError('m²', item.priceMeter) } - + static _createLiftHTML(item) { - if (!Preferences.get('lift')) return ``; - + if (!this._shouldCheckLift(item)) { + return ``; + } + if (!item.additionalInfo) { - if (item.isFlat()) return ``; return this._createMissing('Ascensor'); } - + if (item.hasLift) { - return this._createSuccess('Ascensor',); + return this._createSuccess('Ascensor'); } return this._createError('Ascensor'); } - - static _createInteriorHTML(item) { - if (!Preferences.get('exterior')) return ``; - - if (!item.additionalInfo) { - if (!item.isFlat()) return ``; - return this._createMissing('Exterior'); + + static _shouldCheckLift(item) { + return Preferences.get('lift') + && !item.isHouse() + && !item.isGround(); + } + + static _createExteriorHTML(item) { + if (!this._shouldCheckExterior(item)) { + return ``; } - + if (item.isExterior) { return this._createSuccess('Exterior'); } - return this._createError('Exterior'); + + if (item.isInterior) { + return this._createError('Exterior'); + } + return this._createMissing('Exterior'); + } + + static _shouldCheckExterior(item) { + return Preferences.get('exterior') + && !item.isHouse(); } static _createSuccess(infoText) { - return this.__createIndividual('✓', infoText, 'success'); + return this._createTextSuccess('✔️', infoText); + } + + static _createTextSuccess(strongText, infoText) { + return this._createIndividual(strongText, infoText, this.SUCCESS_CLASS_NAME); } static _createWarning(infoText) { - return this.__createIndividual('✓', infoText, 'warning'); + return this._createTextWarning('⚠️', infoText); } static _createMissing(infoText) { - return this.__createIndividual('?', infoText, 'warning'); + return this._createTextWarning('?', infoText); + } + + static _createTextWarning(strongText, infoText) { + return this._createIndividual(strongText, infoText, this.WARNING_CLASS_NAME); } static _createError(infoText) { - return this.__createIndividual('✓', infoText, 'error'); + return this._createTextError('🚫', infoText); + } + + static _createTextError(strongText, infoText) { + return this._createIndividual(strongText, infoText, this.ERROR_CLASS_NAME); } static _createNeutral(infoText) { - return this.__createIndividual('ⓘ', infoText) + return this._createIndividual('ⓘ', infoText) } - static __createIndividual(strongText, infoText, className = '') { + static _createIndividual(strongText, infoText, className = '') { return `${strongText} ${infoText}`; } diff --git a/src/Styles.js b/src/Styles.js index dc15473..1ca6804 100644 --- a/src/Styles.js +++ b/src/Styles.js @@ -17,8 +17,34 @@ export default class Styles { background-color: white; box-shadow: 0 3px 6px rgba(225, 245, 110, 0.16), 0 3px 6px rgba(225, 245, 110, 0.23); - + border: 3px solid transparent; } + + .${ItemHTML.INFORMATION_CLASS_NAME} { + display: flex; + flex-direction: column; + } + + .${ItemHTML.SUCCESS_CLASS_NAME} { + color: darkgreen; + } + + .${ItemHTML.CONTAINER_CLASS_NAME}.${ItemHTML.SUCCESS_CLASS_NAME} { + border-color: rgba(61, 217, 61, 0.3); + } + + .${ItemHTML.WARNING_CLASS_NAME} { + color: darkorange; + } + + .${ItemHTML.ERROR_CLASS_NAME} { + color: darkred; + } + + .${ItemHTML.CONTAINER_CLASS_NAME}.${ItemHTML.ERROR_CLASS_NAME} { + border-color: rgba(217, 61, 61, 0.3); + } + .${MenuHTML.CONTAINER_CLASS_NAME} { display: flex; align-items: center; @@ -46,16 +72,6 @@ export default class Styles { border: 5px solid rgb(225, 245, 110); border-radius: 8px; } - - .success { - color: darkgreen; - } - .warning { - color: darkorange; - } - .error { - color: darkred; - } `; static add(style = null) {