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" +
"// @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"] })

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 CheckboxHTML from "./CheckboxHTML.js";
import Preferences from "./Preferences.js";
export default class ConfigurationHTML {
@ -25,32 +26,32 @@ export default class ConfigurationHTML {
<h3>Configuración global:</h3>
<label>
<input type='checkbox' id='enabled' checked='${Preferences.get('enabled')}'>
${CheckboxHTML.create('enabled', Preferences.get('enabled'))}
<span>Habilitado</span>
</label>
<h3>Busqueda:</h3>
<label>
<input type='checkbox' id='percentages' checked='${Preferences.get('percentages')}'>
${CheckboxHTML.create('percentages', Preferences.get('percentages'))}
<span>Porcentajes</span>
</label>
<br>
<label>
<input type='checkbox' id='garage' checked='${Preferences.get('garage')}'>
${CheckboxHTML.create('garage', Preferences.get('garage'))}
<span>Garaje</span>
</label>
<br>
<label>
<input type='checkbox' id='exterior' checked='${Preferences.get('exterior')}'>
${CheckboxHTML.create('exterior', Preferences.get('exterior'))}
<span>Exterior</span>
</label>
<br>
<label>
<input type='checkbox' id='lift' checked='${Preferences.get('lift')}'>
${CheckboxHTML.create('lift', Preferences.get('lift'))}
<span>Ascensor</span>
</label>
<br>
@ -71,7 +72,7 @@ export default class ConfigurationHTML {
<button class='${ButtonClass.IDEALISTA_BUTTON_CLASS} ${this.SAVE_CONFIG_CLASS_NAME}'>Guardar</button>
<button class='${ButtonClass.IDEALISTA_BUTTON_CLASS} ${this.OPEN_CONFIG_CLASS_NAME}'>Cerrar</button>
</div>
</div>`
</div>`
}
}

View File

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

View File

@ -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 `
<div class='${this.CONTAINER_CLASS_NAME}'>
<div class='${this.CONTAINER_CLASS_NAME} ${this.ITEM_STATE_CLASS_NAME(item)}'>
${this._createPercentagePriceHTML(item)}
${this._createPriceHTML(item)}
${this._createPriceMeterHTML(item)}
${this._createLiftHTML(item)}
${this._createInteriorHTML(item)}
${this._createExteriorHTML(item)}
</div>`
}
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 `
<div>
<span><strong>20%:</strong> ${twentyPercent}</span>
<span><strong>30%:</strong> ${thirtyPercent}</span>
<span><strong>50%:</strong> ${fiftyPercent}</span>
<div class='${this.INFORMATION_CLASS_NAME}'>
${this._createIndividual('20%', twentyPercent)}
${this._createIndividual('30%', thirtyPercent)}
${this._createIndividual('50%', fiftyPercent)}
</div>`;
}
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 `<span><strong>m²:</strong> ${item.priceMeter}€</span>`;
if (!desiredPricePerMeter) return this._createIndividual('m²', item.priceMeter);
const desiredTwentyFivePercentMore = Math.round(desiredPricePerMeter * 1.25);
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) {
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) {
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 `<span class='${className}'><strong>${strongText}</strong> ${infoText}</span>`;
}

View File

@ -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) {