Adding border depending on the item state and preferences.

Some cleaning and refactor.
This commit is contained in:
Jorge Bolois Guerrero 2022-08-21 13:51:30 +02:00
parent 6de714b445
commit 88f50657c2
6 changed files with 176 additions and 68 deletions

View File

@ -5,13 +5,12 @@ import banner from 'browserify-banner';
const STATIC_BANNER = "// ==UserScript==\n" + const STATIC_BANNER = "// ==UserScript==\n" +
"// @name Idealista Enhancer\n" + "// @name Idealista Enhancer\n" +
"// @description Just some information for idealista.com\n" + "// @description Just some information for idealista.com\n" +
"// @version 0.1.0\n" + "// @version 0.2.0\n" +
"// @author Midefos\n" + "// @author Midefos\n" +
"// @namespace https://github.com/Midefos\n" + "// @namespace https://github.com/Midefos\n" +
"// @match https://www.idealista.com/*\n" + "// @match https://www.idealista.com/*\n" +
"// @license MIT\n" + "// @license MIT\n" +
"// ==/UserScript==\n" + "// ==/UserScript==\n\n";
"/* jshint esversion: 6 */\n\n";
browserify("./app.js") browserify("./app.js")
.transform("babelify", { presets: ["@babel/preset-env"] }) .transform("babelify", { presets: ["@babel/preset-env"] })

17
src/CheckboxHTML.js Normal file
View File

@ -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;
}
}

View File

@ -1,4 +1,5 @@
import ButtonClass from "./ButtonClass.js"; import ButtonClass from "./ButtonClass.js";
import CheckboxHTML from "./CheckboxHTML.js";
import Preferences from "./Preferences.js"; import Preferences from "./Preferences.js";
export default class ConfigurationHTML { export default class ConfigurationHTML {
@ -25,32 +26,32 @@ export default class ConfigurationHTML {
<h3>Configuración global:</h3> <h3>Configuración global:</h3>
<label> <label>
<input type='checkbox' id='enabled' checked='${Preferences.get('enabled')}'> ${CheckboxHTML.create('enabled', Preferences.get('enabled'))}
<span>Habilitado</span> <span>Habilitado</span>
</label> </label>
<h3>Busqueda:</h3> <h3>Busqueda:</h3>
<label> <label>
<input type='checkbox' id='percentages' checked='${Preferences.get('percentages')}'> ${CheckboxHTML.create('percentages', Preferences.get('percentages'))}
<span>Porcentajes</span> <span>Porcentajes</span>
</label> </label>
<br> <br>
<label> <label>
<input type='checkbox' id='garage' checked='${Preferences.get('garage')}'> ${CheckboxHTML.create('garage', Preferences.get('garage'))}
<span>Garaje</span> <span>Garaje</span>
</label> </label>
<br> <br>
<label> <label>
<input type='checkbox' id='exterior' checked='${Preferences.get('exterior')}'> ${CheckboxHTML.create('exterior', Preferences.get('exterior'))}
<span>Exterior</span> <span>Exterior</span>
</label> </label>
<br> <br>
<label> <label>
<input type='checkbox' id='lift' checked='${Preferences.get('lift')}'> ${CheckboxHTML.create('lift', Preferences.get('lift'))}
<span>Ascensor</span> <span>Ascensor</span>
</label> </label>
<br> <br>

View File

@ -4,6 +4,7 @@ export default class Item {
static NAME_SELECTOR = '.item-link'; static NAME_SELECTOR = '.item-link';
static PRICE_SELECTOR = '.item-price'; static PRICE_SELECTOR = '.item-price';
static ROOM_SELECTOR = '.item-detail-char .item-detail:nth-child(1)'; static ROOM_SELECTOR = '.item-detail-char .item-detail:nth-child(1)';
static METERS_SELECTOR = '.item-detail-char .item-detail:nth-child(2)'; static METERS_SELECTOR = '.item-detail-char .item-detail:nth-child(2)';
static ADDITIONAL_INFORMATION_SELECTOR = '.item-detail-char .item-detail:nth-child(3)'; 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.additionalInfo = this._extractAdditionalInfo();
this.hasLift = this._extractLift(); this.hasLift = this._extractLift();
this.isExterior = this._extractExterior(); this.isExterior = this._extractExterior();
this.isInterior = this._extractInterior();
} }
get _data() { get _data() {
@ -36,25 +38,38 @@ export default class Item {
&& nextElement.className.includes(ItemHTML.CONTAINER_CLASS_NAME); && nextElement.className.includes(ItemHTML.CONTAINER_CLASS_NAME);
} }
isProperPrice(price) {
return this.price <= price;
}
isProperPriceMeter(priceMeter) {
return this.priceMeter <= priceMeter;
}
removeData() { removeData() {
if (!this.isDataRendered()) return; if (!this.isDataRendered()) return;
this._data.remove(); this._data.remove();
} }
addData() { addData() {
this._node.outerHTML += ItemHTML.createInformation(this); this._node.insertAdjacentHTML('afterend', ItemHTML.createInformation(this));
} }
isFlat() { isFlat() {
return this.name.includes('Piso'); return this._nameIncludes('Piso');
} }
isHouse() { isHouse() {
return this.name.includes('Casa'); return this._nameIncludes('Casa')
|| this._nameIncludes('Chalet');
} }
isGround() { isGround() {
return this.name.includes('Bajo'); return this._nameIncludes('Bajo');
}
_nameIncludes(name) {
return this.name.toLowerCase().includes(name.toLowerCase());
} }
_extractName() { _extractName() {
@ -79,8 +94,10 @@ export default class Item {
} }
_extractAdditionalInfo() { _extractAdditionalInfo() {
const additionalInfo = this._node.querySelector(Item.ADDITIONAL_INFORMATION_SELECTOR); let additionalInfo = this._node.querySelector(Item.ADDITIONAL_INFORMATION_SELECTOR);
if (!additionalInfo) return null; if (!additionalInfo) {
additionalInfo = this._node.querySelector(Item.METERS_SELECTOR);
}
return additionalInfo.textContent; return additionalInfo.textContent;
} }
@ -94,4 +111,9 @@ export default class Item {
return this.additionalInfo.includes('exterior'); return this.additionalInfo.includes('exterior');
} }
_extractInterior() {
if (!this.additionalInfo) return false;
return this.additionalInfo.includes('interior');
}
} }

View File

@ -2,19 +2,49 @@ import Preferences from "./Preferences.js";
export default class ItemHTML { 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 CONTAINER_CLASS_NAME = 'midefos-idealista-container';
static get CONTAINER_SELECTOR() { static get CONTAINER_SELECTOR() {
return `.${this.CONTAINER_CLASS_NAME}`; 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) { static createInformation(item) {
return ` return `
<div class='${this.CONTAINER_CLASS_NAME}'> <div class='${this.CONTAINER_CLASS_NAME} ${this.ITEM_STATE_CLASS_NAME(item)}'>
${this._createPercentagePriceHTML(item)} ${this._createPercentagePriceHTML(item)}
${this._createPriceHTML(item)} ${this._createPriceHTML(item)}
${this._createPriceMeterHTML(item)} ${this._createPriceMeterHTML(item)}
${this._createLiftHTML(item)} ${this._createLiftHTML(item)}
${this._createInteriorHTML(item)} ${this._createExteriorHTML(item)}
</div>` </div>`
} }
@ -25,92 +55,115 @@ export default class ItemHTML {
const thirtyPercent = Math.round(item.price * 30 / 100); const thirtyPercent = Math.round(item.price * 30 / 100);
const fiftyPercent = Math.round(item.price * 50 / 100); const fiftyPercent = Math.round(item.price * 50 / 100);
return ` return `
<div> <div class='${this.INFORMATION_CLASS_NAME}'>
<span><strong>20%:</strong> ${twentyPercent}</span> ${this._createIndividual('20%', twentyPercent)}
<span><strong>30%:</strong> ${thirtyPercent}</span> ${this._createIndividual('30%', thirtyPercent)}
<span><strong>50%:</strong> ${fiftyPercent}</span> ${this._createIndividual('50%', fiftyPercent)}
</div>`; </div>`;
} }
static _createPriceHTML(item) { static _createPriceHTML(item) {
const desiredPrice = Preferences.get('max-price'); const desiredPrice = Preferences.get('max-price');
if (!desiredPrice) return ``; if (!desiredPrice) return ``;
const desiredTwentyFivePercentMore = Math.round(desiredPrice * 1.25); const desiredTwentyFivePercentMore = Math.round(desiredPrice * 1.25);
if (item.price <= desiredPrice) { if (item.isProperPrice(desiredPrice)) {
return this._createSuccess('Precio'); return this._createSuccess('Precio');
} else if (item.price <= desiredTwentyFivePercentMore) { } else if (item.isProperPrice(desiredTwentyFivePercentMore)) {
return this._createWarning('Precio'); return this._createWarning('Precio');
} }
return this._createError('Precio'); return this._createError('Precio');
} }
static _createPriceMeterHTML(item) { static _createPriceMeterHTML(item) {
const desiredPricePerMeter = Preferences.get('max-price-per-meter'); const desiredPricePerMeter = Preferences.get('max-price-per-meter');
if (!desiredPricePerMeter) return `<span><strong>m²:</strong> ${item.priceMeter}€</span>`; if (!desiredPricePerMeter) return this._createIndividual('m²', item.priceMeter);
const desiredTwentyFivePercentMore = Math.round(desiredPricePerMeter * 1.25); const desiredTwentyFivePercentMore = Math.round(desiredPricePerMeter * 1.25);
if (item.priceMeter <= desiredPricePerMeter) { if (item.priceMeter <= desiredPricePerMeter) {
return `<span class='success'><strong>m²:</strong> ${item.priceMeter}€</span>`; return this._createTextSuccess('m²', item.priceMeter)
} else if (item.priceMeter <= desiredTwentyFivePercentMore) { } else if (item.priceMeter <= desiredTwentyFivePercentMore) {
return `<span class='warning'><strong>m²:</strong> ${item.priceMeter}€</span>`; return this._createTextWarning('m²', item.priceMeter)
} }
return `<span class='error'><strong>m²:</strong> ${item.priceMeter}€</span>`; return this._createTextError('m²', item.priceMeter)
} }
static _createLiftHTML(item) { static _createLiftHTML(item) {
if (!Preferences.get('lift')) return ``; if (!this._shouldCheckLift(item)) {
return ``;
}
if (!item.additionalInfo) { if (!item.additionalInfo) {
if (item.isFlat()) return ``;
return this._createMissing('Ascensor'); return this._createMissing('Ascensor');
} }
if (item.hasLift) { if (item.hasLift) {
return this._createSuccess('Ascensor',); return this._createSuccess('Ascensor');
} }
return this._createError('Ascensor'); return this._createError('Ascensor');
} }
static _createInteriorHTML(item) { static _shouldCheckLift(item) {
if (!Preferences.get('exterior')) return ``; return Preferences.get('lift')
&& !item.isHouse()
&& !item.isGround();
}
if (!item.additionalInfo) { static _createExteriorHTML(item) {
if (!item.isFlat()) return ``; if (!this._shouldCheckExterior(item)) {
return this._createMissing('Exterior'); return ``;
} }
if (item.isExterior) { if (item.isExterior) {
return this._createSuccess('Exterior'); return this._createSuccess('Exterior');
} }
if (item.isInterior) {
return this._createError('Exterior'); return this._createError('Exterior');
} }
return this._createMissing('Exterior');
}
static _shouldCheckExterior(item) {
return Preferences.get('exterior')
&& !item.isHouse();
}
static _createSuccess(infoText) { 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) { static _createWarning(infoText) {
return this.__createIndividual('✓', infoText, 'warning'); return this._createTextWarning('⚠️', infoText);
} }
static _createMissing(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) { 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) { static _createNeutral(infoText) {
return this.__createIndividual('ⓘ', infoText) return this._createIndividual('ⓘ', infoText)
} }
static __createIndividual(strongText, infoText, className = '') { static _createIndividual(strongText, infoText, className = '') {
return `<span class='${className}'><strong>${strongText}</strong> ${infoText}</span>`; return `<span class='${className}'><strong>${strongText}</strong> ${infoText}</span>`;
} }

View File

@ -17,8 +17,34 @@ export default class Styles {
background-color: white; background-color: white;
box-shadow: 0 3px 6px rgba(225, 245, 110, 0.16), 0 3px 6px rgba(225, 245, 110, 0.23); 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} { .${MenuHTML.CONTAINER_CLASS_NAME} {
display: flex; display: flex;
align-items: center; align-items: center;
@ -46,16 +72,6 @@ export default class Styles {
border: 5px solid rgb(225, 245, 110); border: 5px solid rgb(225, 245, 110);
border-radius: 8px; border-radius: 8px;
} }
.success {
color: darkgreen;
}
.warning {
color: darkorange;
}
.error {
color: darkred;
}
`; `;
static add(style = null) { static add(style = null) {