diff --git a/src/js/contentscript.js b/src/js/contentscript.js
index c57e64d..a8d7033 100644
--- a/src/js/contentscript.js
+++ b/src/js/contentscript.js
@@ -16,6 +16,7 @@ import App from './components/App.vue';
RR.logInfo('contentscript loaded');
chrome.runtime.sendMessage({ 'switchIconOn': true });
+const POLL_ADDRESS_TIMEOUT = 500;
const initVueTranslations = () => {
return new Promise((resolve, reject) => {
@@ -66,7 +67,7 @@ const loadPanel = function(address) {
address,
details: {
price: {
- perSquareMeter: pageDataExtractor.getPrices(window.location.host)
+ perSquareMeter: pageDataExtractor.extractSquarePrice()
}
}
}
@@ -78,7 +79,7 @@ const loadPanel = function(address) {
let addressOfProperty;
function initApp() {
RR.logInfo('Initializing app widget');
- addressOfProperty = pageDataExtractor.getAddress(window.location.host);
+ addressOfProperty = pageDataExtractor.getAddress();
RR.logDebug('Address parsed: ', addressOfProperty);
if (RR.String.isNotBlank(addressOfProperty)) {
@@ -94,13 +95,13 @@ function initApp() {
let pollAddressTimerId;
function pollAddress() {
//RR.logDebug('Polling address...'); // you can filter it out in console with regexp filter ^(?=.*?\b.*\b)((?!Poll).)*$ (match all except lines with 'Poll' match)
- const currentAddressOfProperty = pageDataExtractor.getAddress(window.location.host);
+ const currentAddressOfProperty = pageDataExtractor.getAddress();
//RR.logDebug('Polled address:', currentAddressOfProperty);
if (currentAddressOfProperty !== addressOfProperty) {
$(document).trigger(RR.ADDRESS_CHANGED_EVENT);
clearTimeout(pollAddressTimerId);
}
- pollAddressTimerId = setTimeout(pollAddress, 500);
+ pollAddressTimerId = setTimeout(pollAddress, POLL_ADDRESS_TIMEOUT);
}
$(document).on(RR.ADDRESS_CHANGED_EVENT, (event) => {
diff --git a/src/js/sites/index.js b/src/js/sites/index.js
index 43ddb1a..c211303 100644
--- a/src/js/sites/index.js
+++ b/src/js/sites/index.js
@@ -1,3 +1,6 @@
+const RENT = 'rent';
+const SALE = 'sale';
+
const textOrNull = textElement => {
if (textElement === null) {
return null;
@@ -6,57 +9,91 @@ const textOrNull = textElement => {
}
};
-export const siteHosts = {
+const sites = {
SREALITY: {
- hostString: 'sreality.cz'
+ id: 'sreality',
},
BEZREALITKY: {
- hostString: 'bezrealitky.cz'
+ id: 'bezrealitky',
},
MAXIREALITY: {
- hostString: 'maxirealitypraha.cz'
+ id: 'maxirealitypraha',
},
REALITY_IDNES: {
- hostString: 'reality.idnes.cz'
+ id: 'idnes',
}
};
-/**
- *
- * @param {{ hostString: String }} hostId
- * @param {String} host
- * @return {Number|undefined}
- */
-export const isCurrentHost = (hostId, host) => host.includes(hostId.hostString);
-
const priceAreaGuard = (price, area) => (area && !isNaN(area) && (price && !isNaN(price))) && price / area;
+const getHostPredicate = (locationHost) => (siteHost) => locationHost.includes(siteHost);
+
+const containsBoxWords = (selector, words) => {
+ const containsNodeWord = (node, word) => node.textContent.includes(word);
+
+ const node = document.querySelector(selector);
+ if (!node || !words.length) {
+ return false;
+ }
+
+ const mapWords = (word) => containsNodeWord(node, word);
+ // ['foo'] => [true]
+ // ['foo', 'bar', 'baz'] => [true, false, true] => false
+ return words.map(mapWords).filter(Boolean).length === words.length;
+};
+
+// this is business logic, so it may contain site specific settings/params
+// underneath it should only call some generic functions
+const extractAdType = (locationHost) => {
+ const verify = getHostPredicate(locationHost);
+
+ if (verify(sites.SREALITY.id) || verify(sites.SREALITY.id) || verify(sites.MAXIREALITY.id)) {
+ if (/pronajem/i.test(location.pathname)) {
+ return RENT;
+ }
+ return SALE;
+ }
+
+ if (verify('bezrealitky')) {
+ const selector = '.box-params.col-1';
+ return containsBoxWords(selector, ['typ', 'nabídky', 'Pronájem']) ? RENT : SALE;
+ }
+};
+
// TODO add extractor's methods for sites dynamically
export const extractors = {
- getAddress(host) {
- if (isCurrentHost(siteHosts.SREALITY, host)) {
+ getAddress() {
+ const verify = getHostPredicate(window.location.host);
+ if (verify(sites.SREALITY.id)) {
return textOrNull(document.querySelector('.location-text'));
}
- if (isCurrentHost(siteHosts.BEZREALITKY, host)) {
+ if (verify(sites.BEZREALITKY.id)) {
return textOrNull(document.querySelector('header h2'));
}
- if (isCurrentHost(siteHosts.MAXIREALITY, host)) {
+ if (verify(sites.MAXIREALITY.id)) {
const addressRow = Array.from(document.querySelectorAll('tr'))
.filter(node => node.textContent.includes('Adresa'))[0];
return addressRow && addressRow.querySelector('td').innerHTML.replace(/
/g, ' ').trim();
}
- if (isCurrentHost(siteHosts.REALITY_IDNES, host)) {
+ if (verify(sites.REALITY_IDNES.id)) {
return textOrNull(document.querySelector('.realAddress'));
}
RR.logError('cannot parse address on page: ', window.location);
return null;
},
- getPrices(host) {
- if (isCurrentHost(siteHosts.SREALITY, host)) {
+ extractSquarePrice() {
+ const adType = extractAdType(window.location.host);
+ const verify = getHostPredicate(window.location.host);
+
+ if (adType === RENT) {
+ return;
+ }
+
+ if (verify(sites.SREALITY.id)) {
const propertyParams = Array.from(document.querySelectorAll('.params li'));
const priceRow = propertyParams.filter(p => p.innerHTML.includes('Celková cena'))[0];
const areaRow = propertyParams.filter(p => p.innerHTML.includes('Užitná'))[0];
@@ -66,7 +103,7 @@ export const extractors = {
return priceAreaGuard(price, area);
}
- if (isCurrentHost(siteHosts.BEZREALITKY, host)) {
+ if (verify(sites.BEZREALITKY.id)) {
const propertyParams = Array.from(document.querySelectorAll('.box-params .row'));
const areaRow = propertyParams.filter(item => item.innerHTML.includes('plocha'))[0]; // returns DOM node
const priceRow = propertyParams.filter(item => item.innerHTML.includes('cena'))[0]; // returns DOM node
@@ -80,7 +117,7 @@ export const extractors = {
return priceAreaGuard(price, area);
}
- if (isCurrentHost(siteHosts.MAXIREALITY, host)) {
+ if (verify(sites.MAXIREALITY.id)) {
const areaRow = Array.from(document.querySelectorAll('#makler_zaklad > table tr'))
.filter(node => node.innerHTML.includes('Užitná plocha'))[0];
const priceNode = document.querySelector('.two.price');
@@ -90,12 +127,12 @@ export const extractors = {
return priceAreaGuard(price, area);
}
- if (isCurrentHost(siteHosts.REALITY_IDNES, host)) {
+ if (verify(sites.REALITY_IDNES.id)) {
const areaText = $('.parameters .leftCol dt:contains("Užitná plocha")').next().text();
const area = Number.parseInt(areaText); // eg. when text is "34 m2" Number.parseInt can strip text parts and parse it as just 34
- const priceText = document.querySelectorAll('.priceBox strong')[0].innerHTML;
- const price = Number.parseInt(priceText.replace(/ /gi,''));
+ const priceText = document.querySelectorAll('.priceBox strong')[0].innerHTML;
+ const price = Number.parseInt(priceText.replace(/ /gi, ''));
return priceAreaGuard(price, area);
}
diff --git a/src/js/sites/index.spec.js b/src/js/sites/index.spec.js
index 7705ef0..03c7e61 100644
--- a/src/js/sites/index.spec.js
+++ b/src/js/sites/index.spec.js
@@ -25,11 +25,11 @@ describe('extractors', () => {
});
it('should return price per m2', () => {
- expect(extractors.getPrices(window.location.host)).to.equal(22222.222222222223);
+ expect(extractors.extractSquarePrice()).to.equal(22222.222222222223);
});
it('should return address', () => {
- expect(extractors.getAddress(window.location.host)).to.equal('Komenského, Vlašim, Středočeský kraj');
+ expect(extractors.getAddress()).to.equal('Komenského, Vlašim, Středočeský kraj');
});
});
@@ -43,11 +43,11 @@ describe('extractors', () => {
});
it('should return price per m2', () => {
- expect(extractors.getPrices(window.location.host)).to.equal(57042.25352112676);
+ expect(extractors.extractSquarePrice()).to.equal(57042.25352112676);
});
it('should return address', () => {
- expect(extractors.getAddress(window.location.host)).to.equal('Ortenovo náměstí, Praha 7 - Holešovice');
+ expect(extractors.getAddress()).to.equal('Ortenovo náměstí, Praha 7 - Holešovice');
});
});
@@ -61,11 +61,11 @@ describe('extractors', () => {
});
it('should return price per m2', () => {
- expect(extractors.getPrices(window.location.host)).to.equal(64805.194805194806);
+ expect(extractors.extractSquarePrice()).to.equal(64805.194805194806);
});
it('should return address', () => {
- expect(extractors.getAddress(window.location.host)).to.equal('Praha - Smíchov Vrázova');
+ expect(extractors.getAddress()).to.equal('Praha - Smíchov Vrázova');
});
});
@@ -83,11 +83,11 @@ describe('extractors', () => {
});
it('should return price per m2', () => {
- expect(extractors.getPrices(window.location.host)).to.equal(31888.88888888889);
+ expect(extractors.extractSquarePrice()).to.equal(31888.88888888889);
});
it('should return address', () => {
- expect(extractors.getAddress(window.location.host)).to.equal('Praha 5, Hlubočepy, Machatého');
+ expect(extractors.getAddress()).to.equal('Praha 5, Hlubočepy, Machatého');
});
});
});
diff --git a/src/js/utils.js b/src/js/utils.js
index bc60988..fd9b16f 100644
--- a/src/js/utils.js
+++ b/src/js/utils.js
@@ -7,7 +7,14 @@ export const streetNamePredicate = (address) => {
return address;
};
-export const formatPrice = price => Math.round(price) + ' Kč';
+export const formatPrice = price => {
+ const formatter = new Intl.NumberFormat('cs', {
+ style: 'currency',
+ currency: 'CZK',
+ minimumFractionDigits: 0,
+ });
+ return formatter.format(Math.round(price));
+};
/**
* Call ga (google analytics) in context of current page - we cannot directly call page functions here
diff --git a/src/templates/panel.html b/src/templates/panel.html
index 38929f7..4e203aa 100644
--- a/src/templates/panel.html
+++ b/src/templates/panel.html
@@ -4,7 +4,6 @@