1(function () { 2 'use strict'; 3 4 var cornerInset = 5; 5 var cornerColorTolerance = 8; 6 var classificationCache = new Map(); 7 8 function rentfetchColorsMatch(first, second) { 9 return first.slice(0, 4).every(function (channel, index) { 10 return Math.abs(channel - second[index]) <= cornerColorTolerance; 11 }); 12 } 13 14 function rentfetchFindMatchingCornerColor(colors) { 15 for (var index = 0; index < colors.length; index++) { 16 var matches = colors.filter(function (color) { 17 return rentfetchColorsMatch(colors[index], color); 18 }); 19 20 if (matches.length >= 3) { 21 return colors[index]; 22 } 23 } 24 25 return null; 26 } 27 28 function rentfetchSampleImageCorners(image) { 29 var canvas = document.createElement('canvas'); 30 var context = canvas.getContext('2d', { willReadFrequently: true }); 31 var width = image.naturalWidth; 32 var height = image.naturalHeight; 33 34 if (!context || width <= cornerInset * 2 || height <= cornerInset * 2) { 35 return null; 36 } 37 38 canvas.width = 4; 39 canvas.height = 1; 40 context.fillStyle = '#fff'; 41 context.fillRect(0, 0, 4, 1); 42 43 var points = [ 44 [cornerInset, cornerInset], 45 [width - cornerInset - 1, cornerInset], 46 [cornerInset, height - cornerInset - 1], 47 [width - cornerInset - 1, height - cornerInset - 1], 48 ]; 49 var colors = points.map(function (point, index) { 50 context.drawImage(image, point[0], point[1], 1, 1, index, 0, 1, 1); 51 return Array.from(context.getImageData(index, 0, 1, 1).data); 52 }); 53 var matchingColor = rentfetchFindMatchingCornerColor(colors); 54 55 return matchingColor 56 ? { contained: true, color: 'rgb(' + matchingColor.slice(0, 3).join(', ') + ')' } 57 : { contained: false, color: null }; 58 } 59 60 function rentfetchClassifyFloorplanImage(source) { 61 return new Promise(function (resolve) { 62 var image = new Image(); 63 64 try { 65 if (new URL(source, window.location.href).origin !== window.location.origin) { 66 image.crossOrigin = 'anonymous'; 67 } 68 } catch (error) { 69 resolve(null); 70 return; 71 } 72 73 image.onload = function () { 74 try { 75 resolve(rentfetchSampleImageCorners(image)); 76 } catch (error) { 77 resolve(null); 78 } 79 }; 80 image.onerror = function () { 81 resolve(null); 82 }; 83 image.src = source; 84 }); 85 } 86 87 function rentfetchGetFloorplanImageClass(classification) { 88 if (!classification) { 89 return ''; 90 } 91 92 return classification.contained ? 'is-contained-image' : 'is-photo-image'; 93 } 94 95 function rentfetchApplyFloorplanImageClassification(sliderElement, index, classification) { 96 var elements = sliderElement.querySelectorAll('[data-floorplan-image-index="' + index + '"]'); 97 var className = rentfetchGetFloorplanImageClass(classification); 98 99 if (!className) { 100 return; 101 } 102 103 elements.forEach(function (element) { 104 element.classList.remove('is-contained-image', 'is-photo-image'); 105 element.classList.add(className); 106 if (classification.color) { 107 element.style.setProperty('--rentfetch-floorplan-image-background', classification.color); 108 } else { 109 element.style.removeProperty('--rentfetch-floorplan-image-background'); 110 } 111 }); 112 } 113 114 function rentfetchGetFloorplanImageClassification(source) { 115 if (!classificationCache.has(source)) { 116 classificationCache.set(source, rentfetchClassifyFloorplanImage(source)); 117 } 118 119 return classificationCache.get(source); 120 } 121 122 function rentfetchInitializeFloorplanSingleImage(container) { 123 var image = container.querySelector('img'); 124 if (!image) { 125 return; 126 } 127 128 rentfetchGetFloorplanImageClassification(image.currentSrc || image.src).then(function (classification) { 129 var className = rentfetchGetFloorplanImageClass(classification); 130 if (!className) { 131 return; 132 } 133 134 container.classList.remove('is-contained-image', 'is-photo-image'); 135 container.classList.add(className); 136 if (classification.color) { 137 container.style.setProperty('--rentfetch-floorplan-image-background', classification.color); 138 } else { 139 container.style.removeProperty('--rentfetch-floorplan-image-background'); 140 } 141 }); 142 } 143 144 function rentfetchClassifyFloorplanSingleImages(root) { 145 (root || document).querySelectorAll('.floorplan-single-image-wrap').forEach(rentfetchInitializeFloorplanSingleImage); 146 } 147 148 function rentfetchImageLoads(source) { 149 return new Promise(function (resolve) { 150 var image = new Image(); 151 image.onload = function () { 152 resolve(true); 153 }; 154 image.onerror = function () { 155 resolve(false); 156 }; 157 image.src = source; 158 }); 159 } 160 161 function rentfetchReindexFloorplanSlider(element) { 162 element.querySelectorAll('.floorplan-image-slide').forEach(function (slide, index) { 163 slide.dataset.floorplanImageIndex = index; 164 }); 165 element.querySelectorAll('.floorplan-image-thumbnail').forEach(function (thumbnail, index) { 166 thumbnail.dataset.floorplanImageIndex = index; 167 thumbnail.setAttribute('aria-label', 'View photo ' + (index + 1)); 168 thumbnail.classList.toggle('is-active', index === 0); 169 if (index === 0) { 170 thumbnail.setAttribute('aria-current', 'true'); 171 } else { 172 thumbnail.removeAttribute('aria-current'); 173 } 174 }); 175 176 var buttons = element.querySelector('.blaze-buttons'); 177 if (buttons) { 178 buttons.hidden = element.querySelectorAll('.floorplan-image-slide').length < 2; 179 } 180 } 181 182 function rentfetchRemoveFloorplanSliderImage(element, index) { 183 element.querySelectorAll('[data-floorplan-image-index="' + index + '"]').forEach(function (item) { 184 item.remove(); 185 }); 186 rentfetchReindexFloorplanSlider(element); 187 } 188 189 function rentfetchPrepareFloorplanSlider(element) { 190 var thumbnails = Array.from(element.querySelectorAll('.floorplan-image-thumbnail')); 191 var candidates = thumbnails.length ? thumbnails : Array.from(element.querySelectorAll('.floorplan-image-slide')); 192 193 return Promise.all(candidates.map(function (candidate) { 194 var image = candidate.querySelector('img'); 195 var source = candidate.dataset.floorplanSampleSrc || (image ? image.currentSrc || image.src : ''); 196 var index = Number(candidate.dataset.floorplanImageIndex); 197 198 return source ? rentfetchImageLoads(source).then(function (loaded) { 199 return { index: index, loaded: loaded }; 200 }) : Promise.resolve({ index: index, loaded: false }); 201 })).then(function (results) { 202 results.filter(function (result) { 203 return !result.loaded; 204 }).sort(function (first, second) { 205 return second.index - first.index; 206 }
206).forEach(function (result) { 207 rentfetchRemoveFloorplanSliderImage(element, result.index); 208 }); 209 210 rentfetchReindexFloorplanSlider(element); 211 if (element.querySelector('.floorplan-image-slide')) { 212 rentfetchInitializeFloorplanSlider(element); 213 } 214 }); 215 } 216 217 function rentfetchSetActiveFloorplanThumbnail(sliderElement, index) { 218 var thumbnails = sliderElement.querySelectorAll('.floorplan-image-thumbnail'); 219 220 thumbnails.forEach(function (thumbnail) { 221 var active = Number(thumbnail.dataset.floorplanImageIndex) === index; 222 thumbnail.classList.toggle('is-active', active); 223 if (active) { 224 thumbnail.setAttribute('aria-current', 'true'); 225 var rail = thumbnail.parentElement; 226 rail.scrollTo({ 227 left: thumbnail.offsetLeft - (rail.clientWidth - thumbnail.offsetWidth) / 2, 228 behavior: 'smooth', 229 }); 230 } else { 231 thumbnail.removeAttribute('aria-current'); 232 } 233 }); 234 } 235 236 function rentfetchInitializeFloorplanSlider(element) { 237 var slider = new BlazeSlider(element, { 238 all: { 239 enableAutoplay: false, 240 slidesToShow: 1, 241 }, 242 }); 243 244 if (!element.classList.contains('has-floorplan-image-thumbnails')) { 245 return; 246 } 247 248 var thumbnails = Array.from(element.querySelectorAll('.floorplan-image-thumbnail')); 249 var currentIndex = 0; 250 if (!thumbnails.length) { 251 element.querySelectorAll('.floorplan-image-slide').forEach(function (slide) { 252 var image = slide.querySelector('img'); 253 var index = Number(slide.dataset.floorplanImageIndex); 254 if (image) { 255 rentfetchGetFloorplanImageClassification(image.currentSrc || image.src).then(function (classification) { 256 rentfetchApplyFloorplanImageClassification(element, index, classification); 257 }); 258 } 259 }); 260 return; 261 } 262 263 thumbnails.forEach(function (thumbnail) { 264 var index = Number(thumbnail.dataset.floorplanImageIndex); 265 var source = thumbnail.dataset.floorplanSampleSrc; 266 267 rentfetchGetFloorplanImageClassification(source).then(function (classification) { 268 rentfetchApplyFloorplanImageClassification(element, index, classification); 269 }); 270 271 thumbnail.addEventListener('click', function () { 272 var currentThumbnails = Array.from(element.querySelectorAll('.floorplan-image-thumbnail')); 273 var total = currentThumbnails.length; 274 index = currentThumbnails.indexOf(thumbnail); 275 var forward = (index - currentIndex + total) % total; 276 var backward = (currentIndex - index + total) % total; 277 278 currentIndex = index; 279 rentfetchSetActiveFloorplanThumbnail(element, currentIndex); 280 if (forward <= backward) { 281 slider.next(forward); 282 } else { 283 slider.prev(backward); 284 } 285 }); 286 }); 287 288 slider.onSlide(function (stateIndex, firstVisibleSlide) { 289 currentIndex = firstVisibleSlide; 290 rentfetchSetActiveFloorplanThumbnail(element, currentIndex); 291 }); 292 293 var previous = element.querySelector('.blaze-prev'); 294 var next = element.querySelector('.blaze-next'); 295 if (previous) { 296 previous.addEventListener('click', function () { 297 var total = element.querySelectorAll('.floorplan-image-slide').length; 298 currentIndex = (currentIndex - 1 + total) % total; 299 rentfetchSetActiveFloorplanThumbnail(element, currentIndex); 300 }); 301 } 302 if (next) { 303 next.addEventListener('click', function () { 304 var total = element.querySelectorAll('.floorplan-image-slide').length; 305 currentIndex = (currentIndex + 1) % total; 306 rentfetchSetActiveFloorplanThumbnail(element, currentIndex); 307 }); 308 } 309 } 310 311 function rentfetchInitializeFloorplanGallery(gallery) { 312 gallery.querySelectorAll('.floorplan-gallery-image').forEach(function (container) { 313 var source = container.dataset.floorplanSampleSrc; 314 rentfetchGetFloorplanImageClassification(source).then(function (classification) { 315 var className = rentfetchGetFloorplanImageClass(classification); 316 if (!className) { 317 return; 318 } 319 320 container.classList.add(className); 321 if (classification.color) { 322 container.style.setProperty('--rentfetch-floorplan-image-background', classification.color); 323 } 324 }); 325 }); 326 } 327 328 function rentfetchPrepareFloorplanGallery(gallery) { 329 var containers = Array.from(gallery.querySelectorAll('.floorplan-gallery-image')); 330 331 return Promise.all(containers.map(function (container) { 332 return rentfetchImageLoads(container.dataset.floorplanSampleSrc).then(function (loaded) { 333 if (!loaded) { 334 container.remove(); 335 } 336 }); 337 })).then(function () { 338 var remaining = Array.from(gallery.querySelectorAll('.floorplan-gallery-image')); 339 if (remaining.length < 3) { 340 gallery.closest('.container-floorplan-gallery').remove(); 341 return; 342 } 343 344 remaining.forEach(function (container, index) { 345 container.classList.toggle('is-lightbox-only', index > 2); 346 }); 347 gallery.querySelectorAll('.floorplan-gallery-view-all').forEach(function (label) { 348 label.remove(); 349 }); 350 var label = document.createElement('span'); 351 label.className = 'floorplan-gallery-view-all'; 352 label.textContent = 'View all ' + remaining.length + ' photos'; 353 remaining[2].querySelector('.floorplan-gallery-link').appendChild(label); 354 rentfetchInitializeFloorplanGallery(gallery); 355 }); 356 } 357 358 if (typeof module !== 'undefined' && module.exports) { 359 module.exports = { 360 rentfetchColorsMatch: rentfetchColorsMatch, 361 rentfetchFindMatchingCornerColor: rentfetchFindMatchingCornerColor, 362 rentfetchGetFloorplanImageClass: rentfetchGetFloorplanImageClass, 363 }; 364 } 365 366 if (typeof document !== 'undefined') { 367 var preparationTasks = []; 368 window.rentfetchClassifyFloorplanSingleImages = rentfetchClassifyFloorplanSingleImages; 369 rentfetchClassifyFloorplanSingleImages(); 370 if (typeof BlazeSlider !== 'undefined') { 371 document.querySelectorAll('.floorplan-images-slider').forEach(function (slider) { 372 preparationTasks.push(rentfetchPrepareFloorplanSlider(slider)); 373 }); 374 } 375 document.querySelectorAll('.floorplan-gallery-grid').forEach(function (gallery) { 376 preparationTasks.push(rentfetchPrepareFloorplanGallery(gallery)); 377 }); 378 window.rentfetchFloorplanImagesReady = Promise.all(preparationTasks); 379 } 380})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.