NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Rightmove Map Enhanced // @namespace http://tampermonkey.net/ // @version 0.2 // @description Extra info in property map view // @author You // @license MIT // @match https://www.rightmove.co.uk/property-*/map.html* // @icon https://www.google.com/s2/favicons?domain=rightmove.co.uk // @require http://code.jquery.com/jquery-latest.js // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.core.js // @connect localhost // @connect bestmove.loophole.site // @grant GM_xmlhttpRequest // @grant GM_getResourceText // @grant GM_addStyle // @resource MATERIAL_ICONS https://fonts.googleapis.com/icon?family=Material+Icons // ==/UserScript== /* globals $ _ */ const propertyCoordinatePair = (property) => `${property.location.latitude},${property.location.longitude}`; const state = { currentPropertyId: null, propertiesById: {}, scoresById: {}, waitingPropertyInfo: false }; const renderScore = (property) => { const propertyScore = state.scoresById[property.id]; const coordinatePair = propertyCoordinatePair(property); const groceryListView = _.compact(propertyScore.scores?.groceries).map((grocery) => { return ` <div class="grocery-item"> <span class="material-icons">shopping_cart</span> <a href="https://www.google.com/maps/dir/${coordinatePair}/${grocery.latitude},${grocery.longitude}/data=!4m2!4m1!3e2" target="_blank"> <span class="score-text">${grocery.store_name} (${grocery.postcode}, ${grocery.size_band})</span> </a> </div>`; }).join(''); $('.score-section').remove(); $( ` <div class="score-section"> <div class="imd-list"> <span class="material-icons">paid</span><span class="score-text">${propertyScore.scores?.income_score?.toFixed(1)}</span> <span class="material-icons">work</span><span class="score-text">${propertyScore.scores?.employment_score?.toFixed(1)}</span> <span class="material-icons">school</span><span class="score-text">${propertyScore.scores?.skill_score?.toFixed(1)}</span> <span class="material-icons">security</span><span class="score-text">${propertyScore.scores?.crime_score?.toFixed(1)}</span> <span class="material-icons">apartment</span><span class="score-text">${propertyScore.scores?.housing_barrier_score?.toFixed(1)}</span> <span class="material-icons">park</span><span class="score-text">${propertyScore.scores?.environment_score?.toFixed(1)}</span> IMD<span class="score-text">${propertyScore.scores?.imd_score?.toFixed(1)}</span> </div> <div class="grocery-list">${groceryListView}</div> <a href="https://www.google.com/maps/dir/${coordinatePair}/birmingham+new+street+station/data=!4m2!4m1!3e3" target="_blank"> <span class="material-icons">directions_bus_filled</span><span class="score-text">${propertyScore.scores?.distance_to_town_center?.toFixed(1)}</span> </a> <a href="https://www.google.com/maps/search/${coordinatePair}" target="_blank"><span class="material-icons">map</span><span class="score-text">${propertyScore.postcode}</span></a> <a href="https://epc.opendatacommunities.org/domestic/search?postcode=${propertyScore.postcode}" target="_blank"><span class="material-icons">bolt</span><span class="score-text">Cert</span></a> <a href="https://www.streetcheck.co.uk/postcode/${propertyScore.postcode.split(' ').join('')}" target="_blank"><span class="material-icons">groups</span></a> </div> ` ).insertAfter('.expPropCardDescription'); }; const loadScore = function(propertyId) { if (!propertyId) { return; } state.currentPropertyId = propertyId; const property = state.propertiesById[propertyId]; if (!property) { // propertyInfo is not ready, waiting for ajax state.waitingPropertyInfo = true; return; } state.waitinfForPropertyInfo = false; const scoreParams = { coordinate_pairs: [property].map(propertyCoordinatePair), expand: ['groceries'], }; if (state.scoresById[property.id]) { renderScore(property); return; } GM_xmlhttpRequest({ method: 'GET', url: `http://bestmove.loophole.site/scores?${$.param(scoreParams)}`, responseType: 'json', onload: function(res) { const payload = res.response; const coordinatePair = propertyCoordinatePair(property); const propertyScore = payload[coordinatePair]; state.scoresById[property.id] = propertyScore; renderScore(property); } }); }; (function() { 'use strict'; GM_addStyle(GM_getResourceText("MATERIAL_ICONS")); GM_addStyle( ` .score-section .material-icons { font-size: 18px; } .score-section .score-text { vertical-align: top; margin-right: 5px; } .score-section a { color: inherit; } ` ); $('.mapSidePanel').bind('DOMSubtreeModified', function(e) { if (e.target.innerHTML.length === 0) { return; } const propertyId = $('.expPropCardPropertyLinkButton a').attr('href')?.split('/')[2]; if (propertyId && propertyId !== state.currentPropertyId) { loadScore(propertyId); } }); })(); (function(open) { XMLHttpRequest.prototype.open = function() { this.addEventListener("readystatechange", function() { if(this.readyState !== 4 || !Array.isArray(this.response) || !this.response[0].id) { return; } this.response.forEach((property) => { state.propertiesById[property.id.toString()] = property; if (state.waitingPropertyInfo && property.id == state.currentPropertyId) { loadScore(property.id.toString()); } }); }, false); open.apply(this, arguments); }; })(XMLHttpRequest.prototype.open);