diff --git a/app.js b/app.js index 802aa5f..dac32ba 100644 --- a/app.js +++ b/app.js @@ -1,187 +1,7 @@ -import Configuration from "./src/Configuration.js"; -import Item from "./src/Item.js"; -import $ from 'jquery'; -import ItemHTML from "./src/ItemHTML.js"; - -// Idealista selectors. -const ITEM_SELECTOR = 'article.item'; - -// Own selectors. -const CONFIG_CLASS = 'midefos-idealista-config' - -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; - } -`; - +import App from "./src/App.js"; function init() { - Configuration.init(); - addStyles(); - - createMenu(); - createInformation(); + App.init(); } -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, - lift: configNode.querySelector('#lift').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 item = new Item(_item); - const currentInformation = _item.querySelector('.midefos-idealista-container'); - if (currentInformation) currentInformation.remove(); - _item.innerHTML += ItemHTML.createInformation(item); - } -} - -function createConfigHTML() { - return ` -
-

Midefos Idealista

- -

Configuración global:

- - -

Busqueda:

- - -
- - -
- - -
- - -
- - -
- - -
- - - -
` -} - -function createMenuHTML() { - return ` -
- - -
`; -} - -init(); - -function addStyles() { - const style = document.createElement('style'); - style.textContent = STYLES; - document.head.appendChild(style); -} \ No newline at end of file +init(); \ No newline at end of file diff --git a/package.json b/package.json index 808db02..fda0be8 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "@babel/preset-env": "^7.18.6", "babelify": "^10.0.0", "browserify": "^17.0.0", - "browserify-banner": "^2.0.4", - "jquery": "^3.6.0" + "browserify-banner": "^2.0.4" } } diff --git a/src/App.js b/src/App.js new file mode 100644 index 0000000..db3c2a9 --- /dev/null +++ b/src/App.js @@ -0,0 +1,20 @@ +import Preferences from './Preferences.js'; +import Styles from './Styles.js'; + +import Menu from './Menu.js'; +import Configuration from './Configuration.js'; +import Information from './Information.js'; + + +export default class App { + + static init() { + Preferences.init(); + Styles.add(); + + new Menu(); + new Configuration(); + Information.create(); + } + +} \ No newline at end of file diff --git a/src/Configuration.js b/src/Configuration.js index 5da66c6..d915815 100644 --- a/src/Configuration.js +++ b/src/Configuration.js @@ -1,44 +1,46 @@ +import ConfigurationHTML from "./ConfigurationHTML.js"; +import Event from "./Event.js"; +import Information from "./Information.js"; +import Log from "./Log.js"; +import Preferences from "./Preferences.js"; + export default class Configuration { - static NAME = 'midefos-idealista'; - - static _current; - - static init() { - this._current = this._getConfig(); + constructor() { + document.querySelector('#main-header').innerHTML += ConfigurationHTML.create(); + + this._initEvents(); } - static get(key) { - const config = this._getConfig(); - return config[key]; + _initEvents() { + Event.click(ConfigurationHTML.SAVE_CONFIG_SELECTOR, () => { + Preferences.save(this._extractConfiguration()); + Information.create(); + Configuration.toggle(); + }); } - static save(config) { - window.localStorage.setItem(this.NAME, JSON.stringify(config)); - this._current = config; - } - - static _getConfig() { - const storageConfig = this._getFromLocalStorage(); - if (!storageConfig) return this._default(); - return storageConfig; - } - - static _getFromLocalStorage() { - const storageConfig = window.localStorage.getItem(this.NAME); - if (!storageConfig) return null; - return JSON.parse(storageConfig); - } - - static _default() { + _extractConfiguration() { + const container = document.querySelector(ConfigurationHTML.CONTAINER_SELECTOR); return { - enabled: true, - percentages: true, - garage: false, - exterior: true, - lift: true, - 'max-price': 120_000, - 'max-price-per-meter': 1_500 + enabled: container.querySelector('#enabled').checked, + percentages: container.querySelector('#percentages').checked, + garage: container.querySelector('#garage').checked, + exterior: container.querySelector('#exterior').checked, + lift: container.querySelector('#lift').checked, + 'max-price': container.querySelector('#max-price').value, + 'max-price-per-meter': container.querySelector('#max-price-per-meter').value + } + } + + static toggle() { + const container = document.querySelector(ConfigurationHTML.CONTAINER_SELECTOR); + if (getComputedStyle(container).display === 'none') { + container.style.display = 'block'; + Log.debug(`Opened configuration`); + } else { + container.style.display = 'none'; + Log.debug(`Closed configuration`); } } diff --git a/src/ConfigurationHTML.js b/src/ConfigurationHTML.js new file mode 100644 index 0000000..6b126f3 --- /dev/null +++ b/src/ConfigurationHTML.js @@ -0,0 +1,74 @@ +import Preferences from "./Preferences.js"; + +export default class ConfigurationHTML { + + static CONTAINER_CLASS_NAME = 'midefos-idealista-config'; + static get CONTAINER_SELECTOR() { + return `.${this.CONTAINER_CLASS_NAME}`; + } + + static OPEN_CONFIG_CLASS_NAME = 'open-config'; + static get OPEN_CONFIG_SELECTOR() { + return `.${this.OPEN_CONFIG_CLASS_NAME}`; + } + + static SAVE_CONFIG_CLASS_NAME = 'save-config'; + static get SAVE_CONFIG_SELECTOR() { + return `.${this.SAVE_CONFIG_CLASS_NAME}`; + } + + static create() { + return ` +
+

Midefos Idealista

+ +

Configuración global:

+ + +

Busqueda:

+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + +
` + } + +} \ No newline at end of file diff --git a/src/Event.js b/src/Event.js new file mode 100644 index 0000000..00512ae --- /dev/null +++ b/src/Event.js @@ -0,0 +1,13 @@ +import Log from "./Log.js"; + +export default class Event { + + static click(selector, callback) { + document.addEventListener('click', (event) => { + if (!event.target.matches(selector)) return; + Log.debug(`Click on: ${selector}`) + callback(event.target); + }); + } + +} \ No newline at end of file diff --git a/src/Information.js b/src/Information.js new file mode 100644 index 0000000..583900b --- /dev/null +++ b/src/Information.js @@ -0,0 +1,21 @@ +import Item from './Item.js'; +import ItemHTML from './ItemHTML.js'; +import Log from './Log.js'; + +export default class Information { + + static CLASS_NAME = 'midefos-idealista-container'; + static ITEM_SELECTOR = 'article.item'; + + static create() { + Log.debug(`Creating information...`) + const items = document.querySelectorAll(this.ITEM_SELECTOR); + for (const _item of items) { + const item = new Item(_item); + const currentInformation = _item.querySelector(`.${this.CLASS_NAME}`); + if (currentInformation) currentInformation.remove(); + _item.innerHTML += ItemHTML.createInformation(item); + } + } + +} \ No newline at end of file diff --git a/src/ItemHTML.js b/src/ItemHTML.js index 2758227..d42c489 100644 --- a/src/ItemHTML.js +++ b/src/ItemHTML.js @@ -1,11 +1,15 @@ -import Configuration from "./Configuration.js"; +import Preferences from "./Preferences.js"; export default class ItemHTML { + static CONTAINER_CLASS_NAME = 'midefos-idealista-container'; + static get CONTAINER_SELECTOR() { + return `.${this.CONTAINER_CLASS_NAME}`; + } static createInformation(item) { return ` -
+
${this._createPercentagePriceHTML(item)} ${this._createPriceHTML(item)} ${this._createPriceMeterHTML(item)} @@ -15,7 +19,7 @@ export default class ItemHTML { } static _createPercentagePriceHTML(item) { - if (!Configuration.get('percentages')) return ``; + if (!Preferences.get('percentages')) return ``; const twentyPercent = Math.round(item.price * 20 / 100); const thirtyPercent = Math.round(item.price * 30 / 100); @@ -28,20 +32,20 @@ export default class ItemHTML { static _createPriceHTML(item) { - const desiredPrice = Configuration.get('max-price'); + const desiredPrice = Preferences.get('max-price'); const desiredTwentyFivePercentMore = Math.round(desiredPrice * 1.25); if (item.price <= desiredPrice) { - return ` Precio`; + return this._createSuccess('Precio'); } else if (item.price <= desiredTwentyFivePercentMore) { - return ` Precio`; + return this._createWarning('Precio'); } - return `X Precio`; + return this._createError('Precio'); } static _createPriceMeterHTML(item) { - const desiredPricePerMeter = Configuration.get('max-price-per-meter'); + const desiredPricePerMeter = Preferences.get('max-price-per-meter'); const desiredTwentyFivePercentMore = Math.round(desiredPricePerMeter * 1.25); if (item.priceMeter <= desiredPricePerMeter) { @@ -53,31 +57,48 @@ export default class ItemHTML { } static _createLiftHTML(item) { - if (!Configuration.get('lift')) return ``; + if (!Preferences.get('lift')) return ``; if (!item.additionalInfo) { if (item.isFlat()) return ``; - return this.__createIndividual('?', 'Ascensor', 'warning'); + return this._createMissing('Ascensor'); } if (item.hasLift) { - return this.__createIndividual('✓', 'Ascensor', 'warning'); + return this._createSuccess('Ascensor',); } - return this.__createIndividual('X', 'Ascensor', 'error'); + return this._createError('Ascensor'); } static _createInteriorHTML(item) { - if (!Configuration.get('exterior')) return ``; + if (!Preferences.get('exterior')) return ``; if (!item.additionalInfo) { if (!item.isFlat()) return ``; - return `? Exterior` + return this._createMissing('Exterior'); } if (item.isExterior) { - return ` Exterior` + return this._createSuccess('Exterior'); } - return `X Exterior`; + return this._createError('Exterior'); + } + + static _createSuccess(infoText) { + return this.__createIndividual('✓', infoText, 'success'); + } + + + static _createWarning(infoText) { + return this.__createIndividual('✓', infoText, 'warning'); + } + + static _createMissing(infoText) { + return this.__createIndividual('?', infoText, 'warning'); + } + + static _createError(infoText) { + return this.__createIndividual('✓', infoText, 'error'); } static __createIndividual(strongText, infoText, className = '') { diff --git a/src/Log.js b/src/Log.js new file mode 100644 index 0000000..0b82f64 --- /dev/null +++ b/src/Log.js @@ -0,0 +1,7 @@ +export default class Log { + + static debug(message) { + console.log(`%c [DEBUG] ${message} `, 'background: blue; color: white; font-size: 13px;'); + } + +} \ No newline at end of file diff --git a/src/Menu.js b/src/Menu.js new file mode 100644 index 0000000..5d69cc2 --- /dev/null +++ b/src/Menu.js @@ -0,0 +1,32 @@ +import Configuration from "./Configuration.js"; +import ConfigurationHTML from "./ConfigurationHTML.js"; +import Event from "./Event.js"; +import Information from "./Information.js"; +import MenuHTML from "./MenuHTML.js"; + +export default class Menu { + + constructor() { + if (this._shouldNotLoad()) return; + + const mainContent = document.querySelector('#main-content'); + mainContent.innerHTML = MenuHTML.create() + mainContent.innerHTML; + + this._initEvents(); + } + + _initEvents() { + Event.click(MenuHTML.RELOAD_INFORMATION_SELECTOR, () => { + Information.create(); + }) + + Event.click(ConfigurationHTML.OPEN_CONFIG_SELECTOR, () => { + Configuration.toggle(); + }); + } + + _shouldNotLoad() { + return window.location.href.includes('mapa-google'); + } + +} \ No newline at end of file diff --git a/src/MenuHTML.js b/src/MenuHTML.js new file mode 100644 index 0000000..820411b --- /dev/null +++ b/src/MenuHTML.js @@ -0,0 +1,23 @@ +import ConfigurationHTML from "./ConfigurationHTML.js"; + +export default class MenuHTML { + + static CONTAINER_CLASS_NAME = 'midefos-idealista-menu'; + static get CONTAINER_SELECTOR() { + return `.${this.CONTAINER_CLASS_NAME}`; + } + + static RELOAD_INFORMATION_CLASS_NAME = 'reload-information'; + static get RELOAD_INFORMATION_SELECTOR() { + return `.${this.RELOAD_INFORMATION_CLASS_NAME}`; + } + + static create() { + return ` +
+ + +
`; + } + +} \ No newline at end of file diff --git a/src/Preferences.js b/src/Preferences.js new file mode 100644 index 0000000..efab382 --- /dev/null +++ b/src/Preferences.js @@ -0,0 +1,48 @@ +import Log from "./Log.js"; + +export default class Preferences { + + static NAME = 'midefos-idealista'; + + static _current; + + static init() { + this._current = this._getConfig(); + } + + static get(key) { + const config = this._getConfig(); + return config[key]; + } + + static save(config) { + window.localStorage.setItem(this.NAME, JSON.stringify(config)); + this._current = config; + Log.debug('Saved preferences') + } + + static _getConfig() { + const storageConfig = this._getFromLocalStorage(); + if (!storageConfig) return this._default(); + return storageConfig; + } + + static _getFromLocalStorage() { + const storageConfig = window.localStorage.getItem(this.NAME); + if (!storageConfig) return null; + return JSON.parse(storageConfig); + } + + static _default() { + return { + enabled: true, + percentages: true, + garage: false, + exterior: true, + lift: true, + 'max-price': 120_000, + 'max-price-per-meter': 1_500 + } + } + +} \ No newline at end of file diff --git a/src/Styles.js b/src/Styles.js new file mode 100644 index 0000000..9c704e4 --- /dev/null +++ b/src/Styles.js @@ -0,0 +1,64 @@ +import ConfigurationHTML from "./ConfigurationHTML.js"; +import ItemHTML from "./ItemHTML.js"; +import MenuHTML from "./MenuHTML.js"; + +export default class Styles { + + static APP_STYLES = ` + .${ItemHTML.CONTAINER_CLASS_NAME} { + 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); + + } + .${MenuHTML.CONTAINER_CLASS_NAME} { + 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); + } + .${ConfigurationHTML.CONTAINER_CLASS_NAME} { + 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; + } + `; + + static add(style = null) { + if (!style) style = this.APP_STYLES; + const styleNode = document.createElement('style'); + styleNode.textContent = style; + document.head.appendChild(styleNode); + } + +} \ No newline at end of file