/** * 掲載中一覧(list.js createBuildingCard)と同じ building-card HTML を共有。 * TOP 新着・条件検索・view=all で同一の画像集約・カード構造を使う。 */ (function (root, factory) { if (typeof module === 'object' && module.exports) { module.exports = factory(); } else { root.BuildingListCard = factory(); } })(typeof globalThis !== 'undefined' ? globalThis : this, function () { 'use strict'; var LCI = typeof ListCardImages !== 'undefined' ? ListCardImages : null; var PPI = typeof PropertyPublicImageUrl !== 'undefined' ? PropertyPublicImageUrl : null; function text(v, fb) { var s = v == null ? '' : String(v).trim(); return s || (fb == null ? '' : fb); } function escAttr(s) { return String(s == null ? '' : s) .replace(/&/g, '&') .replace(/"/g, '"') .replace(//g, '>'); } function sanitizeUrl(u) { u = text(u, ''); if (!u) return ''; if (PPI && PPI.sanitize) return PPI.sanitize(u); return u; } function enrichRow(row) { if (LCI && LCI.enrichListRowForCard) return LCI.enrichListRowForCard(row); return row; } function enrichGroup(group) { group = group || { representative: {}, rooms: [] }; if (group.representative) group.representative = enrichRow(group.representative); if (group.rooms) { group.rooms = group.rooms.map(function (r) { return enrichRow(r); }); } return group; } function normalizeImageKind(kindText) { var t = text(kindText, '').toLowerCase(); if (!t) return 'other'; if (LCI && LCI.classifyListImageKind && kindText && typeof kindText === 'object') { return LCI.classifyListImageKind(kindText); } var known = ['exterior', 'building', 'facade', 'appearance', 'room', 'living', 'interior', 'common', 'other', 'floorplan']; if (known.indexOf(t) >= 0) return t; if (t.indexOf('外観') >= 0 || t.indexOf('exterior') >= 0) return 'exterior'; if (t.indexOf('建物') >= 0 || t.indexOf('building') >= 0) return 'building'; if (t.indexOf('間取') >= 0 || t.indexOf('floorplan') >= 0 || t.indexOf('madori') >= 0) return 'floorplan'; return 'other'; } function collectCardImages(row) { var out = []; var seen = Object.create(null); var items = LCI && LCI.collectCardImageItemsForRow ? LCI.collectCardImageItemsForRow(row) : []; items.forEach(function (item) { var src = sanitizeUrl(text(item && item.src, '')); if (!src || seen[src]) return; seen[src] = true; var kind = LCI && LCI.classifyListImageKind && item.raw ? LCI.classifyListImageKind(item.raw) : normalizeImageKind(text(item && item.kind, '')); out.push({ src: src, kind: kind, raw: item.raw }); }); return out; } function collectOrderedUrlsForBuildingGroup(group) { if (LCI && LCI.collectOrderedUrlsForBuildingGroup) { return LCI.collectOrderedUrlsForBuildingGroup(group); } var ordered = []; var seen = Object.create(null); function pushUrl(u) { u = sanitizeUrl(text(u, '')); if (!u || seen[u]) return; seen[u] = true; ordered.push(u); } var rep = group && group.representative; if (rep && LCI && LCI.collectOrderedUrlsForRow) { LCI.collectOrderedUrlsForRow(rep).forEach(pushUrl); } else if (rep) { collectCardImages(rep).forEach(function (it) { pushUrl(it.src); }); } (group && group.rooms ? group.rooms : []).forEach(function (r) { if (LCI && LCI.collectOrderedUrlsForRow) { LCI.collectOrderedUrlsForRow(r).forEach(pushUrl); } else { collectCardImages(r).forEach(function (it) { pushUrl(it.src); }); } }); return ordered; } function urlKindOverlayForBuildingGroup(group) { if (LCI && LCI.urlKindOverlayForBuildingGroup) { return LCI.urlKindOverlayForBuildingGroup(group); } var o = Object.create(null); collectCardImages(group.representative).forEach(function (it) { if (it.src) o[it.src] = it.kind || 'other'; }); (group.rooms || []).forEach(function (r) { collectCardImages(r).forEach(function (it) { if (it.src) o[it.src] = it.kind || 'other'; }); }); return o; } function urlKindOverlayFromRow(row) { if (LCI && LCI.urlKindOverlayFromRow) return LCI.urlKindOverlayFromRow(row); var o = Object.create(null); collectCardImages(row).forEach(function (it) { if (it.src) o[it.src] = it.kind || 'other'; }); return o; } function collectOrderedUrlsForRow(row) { if (LCI && LCI.collectOrderedUrlsForRow) return LCI.collectOrderedUrlsForRow(row); return collectCardImages(row) .map(function (it) { return it.src; }) .filter(Boolean); } function buildUrlKindMapFromRow(row) { if (LCI && LCI.urlKindOverlayFromRow) return LCI.urlKindOverlayFromRow(row); return urlKindOverlayFromRow(row); } function mergeCardSliderSlides(mainImage, orderedUrls, row, urlKindOverlay) { var urls = []; var kinds = []; var kindMap = Object.assign(Object.create(null), buildUrlKindMapFromRow(row), urlKindOverlay || {}); var seen = Object.create(null); function kindOf(u) { return kindMap[u] || 'other'; } function push(u) { u = sanitizeUrl(text(u, '')); if (!u || seen[u]) return; seen[u] = true; urls.push(u); kinds.push(kindOf(u)); } push(mainImage); if (Array.isArray(orderedUrls)) { for (var j = 0; j < orderedUrls.length; j++) push(orderedUrls[j]); } collectCardImages(row).forEach(function (it) { push(it.src); }); return { urls: urls, kinds: kinds }; } function rotateSlides(urls, kinds) { if (LCI && LCI.rotateSlidesByPreferredKind) { return LCI.rotateSlidesByPreferredKind(urls, kinds); } return { urls: urls, kinds: kinds }; } var BUILDING_KIND_PREF = [ 'exterior', 'building', 'facade', 'appearance', 'room', 'living', 'interior', 'common', 'other', 'floorplan', ]; function pickByKind(items, preferred) { if (!Array.isArray(items) || !items.length) return ''; var i; for (i = 0; i < preferred.length; i++) { var k = preferred[i]; var hit = items.find(function (x) { return normalizeImageKind(text(x && x.kind, '')) === k && text(x && x.src, ''); }); if (hit) return sanitizeUrl(hit.src); } var any = items.find(function (x) { return text(x && x.src, ''); }); return any ? sanitizeUrl(any.src) : ''; } function pickBuildingImageForGroup(group) { var all = []; if (group && group.representative) { all = all.concat(collectCardImages(group.representative)); } (group.rooms || []).forEach(function (r) { all = all.concat(collectCardImages(r)); }); return pickByKind(all, BUILDING_KIND_PREF); } function pickRoomFloorplanImage(row, fallback) { var items = collectCardImages(row); var src = pickByKind(items, ['floorplan', 'madori', '間取り', 'other', 'interior']); return src || fallback || ''; } function cardPhotoSliderInnerClass(wrapperBase) { var w = text(wrapperBase, 'building-card__photo'); if (w.indexOf('building-card__photo') >= 0) { return 'building-card__photo-inner card-photo-slider-inner'; } if (w.indexOf('room-row__thumb') >= 0) { return 'list-card-thumb-inner card-photo-slider-inner'; } return 'card-photo-slider-inner'; } function renderCardImageSliderHtml(mainImage, title, mergedUrls, opts) { opts = opts || {}; var roomThumbNoSlider = !!opts.roomThumbNoSlider; var kinds = Array.isArray(opts.imageKinds) && opts.imageKinds.length === mergedUrls.length ? opts.imageKinds : mergedUrls.map(function () { return 'photo'; }); var firstImage = sanitizeUrl(text(mainImage, '')); if (!firstImage && mergedUrls.length) firstImage = mergedUrls[0]; var hasMany = mergedUrls.length > 1 && !roomThumbNoSlider; var wrapperBase = text(opts.wrapperBase, 'building-card__photo'); var firstKind = kinds[0] === 'floorplan' ? 'floorplan' : 'photo'; var wrapCls = wrapperBase + (firstKind === 'floorplan' ? ' is-floorplan' : '') + (hasMany ? ' js-card-slider' : ''); var alt = text(title, '物件画像'); var inner = ''; if (firstImage) { var imgHtml = '' +
        escAttr(alt) +
        ''; if (roomThumbNoSlider) { inner = imgHtml; } else if (hasMany) { inner = '
' + '' + imgHtml + '' + '
'; } else { inner = imgHtml; } } else { inner = ''; } return '
' + inner + '
'; } function renderHtml(group, ctx) { ctx = ctx || {}; var h = ctx.escHtml || escHtml; var a = ctx.escAttr || escAttr; var t = ctx.text || text; group = enrichGroup(group); var rep = group.representative || {}; var rooms = group.rooms || []; var buildingName = t(rep.buildingName || rep['建物名'] || rep.title, '建物名未設定'); var address = (ctx.formatCardAddress ? ctx.formatCardAddress(rep) : '') || '—'; var access = ctx.firstTransport ? t(ctx.firstTransport(rep), '—') : '—'; var built = t(rep.built_ym || rep.builtYm || rep.built_year, '—'); var floorSummary = ctx.formatFloorSummaryForGroup ? ctx.formatFloorSummaryForGroup(rep, rooms) : '—'; var typeRaw = rep.property_type || rep.propertyType || rep.type || rep.structureType || rep['物件種別'] || rep['建物種別'] || rep['種別'] || rep.building_type; var typeDisp = ctx.formatPropertyType ? ctx.formatPropertyType(typeRaw) : ''; var typeMetaLine = typeDisp ? '

種別' + h(typeDisp) + '

' : ''; var extraArticleClass = t(ctx.extraArticleClass, ''); var roomRowClass = t(ctx.roomRowClass, ''); var ctaDetailClass = t(ctx.ctaDetailClass, 'list-cta list-cta--line'); var ctaInquiryClass = t(ctx.ctaInquiryClass, 'list-cta list-cta--go'); var ctaDetailLabel = t(ctx.ctaDetailLabel, '詳細を見る'); var ctaInquiryLabel = t(ctx.ctaInquiryLabel, '空室確認・内見予約'); var buildingImg = pickBuildingImageForGroup(group); var mergedBuildingSlides = mergeCardSliderSlides( buildingImg, collectOrderedUrlsForBuildingGroup(group), rep, urlKindOverlayForBuildingGroup(group), ); var mergedBuilding = mergedBuildingSlides.urls; var mergedBuildingKinds = mergedBuildingSlides.kinds; var rotB = rotateSlides(mergedBuilding, mergedBuildingKinds); mergedBuilding = rotB.urls; mergedBuildingKinds = rotB.kinds; var buildingImgShown = mergedBuilding.length ? mergedBuilding[0] : buildingImg; var buildingPhotoHtml = renderCardImageSliderHtml(buildingImgShown, buildingName, mergedBuilding, { wrapperBase: 'building-card__photo', imageKinds: mergedBuildingKinds, }); var roomRows = rooms .map(function (r) { var id = t(r.id, ''); var baseForRoom = t(r.buildingName || r['建物名'] || buildingName, buildingName); var displayTitle = ctx.formatPropertyHeadline ? ctx.formatPropertyHeadline(baseForRoom, ctx.roomNoRawFromRow ? ctx.roomNoRawFromRow(r) : '') : baseForRoom; var roomImg = pickRoomFloorplanImage(r, buildingImg); var detailUrl = ctx.propertyUrlById ? ctx.propertyUrlById(id) : '/test/properties/' + encodeURIComponent(id) + '/'; var inquiryUrl = detailUrl + '#pd-inquiry'; var roomOrdered = collectOrderedUrlsForRow(r); var roomOverlay = urlKindOverlayFromRow(r); var mergedRoomSlides = mergeCardSliderSlides(roomImg, roomOrdered, r, roomOverlay); var roomThumbHtml = renderCardImageSliderHtml(roomImg, displayTitle, mergedRoomSlides.urls, { wrapperBase: 'room-row__thumb', imageKinds: mergedRoomSlides.kinds, roomThumbNoSlider: true, }); var rent = ctx.formatRent ? ctx.formatRent(r.rent) : t(r.rent, '—'); var layout = t(r.layout, '—'); var area = r.area && Number.isFinite(Number(r.area)) ? String(r.area) + '㎡' : '—'; var floorSummaryRoom = ctx.formatFloorSummary ? ctx.formatFloorSummary(r) : '—'; var mgmt = ctx.formatManagementFeeLabel ? ctx.formatManagementFeeLabel(r) : ''; var dep = ctx.formatDepositLabel ? ctx.formatDepositLabel(r) : ''; var key = ctx.formatKeyMoneyLabel ? ctx.formatKeyMoneyLabel(r) : ''; var rowCls = 'room-row' + (roomRowClass ? ' ' + roomRowClass : ''); var tagsHtml = ctx.roomTagsHtml ? ctx.roomTagsHtml(r, rep) : ''; var rentHtml = ctx.formatRentBlock ? ctx.formatRentBlock(r) : '

' + h(rent) + '

'; return ( '
' + roomThumbHtml + '
' + '

' + h(displayTitle) + '

' + tagsHtml + rentHtml + '

' + h(mgmt) + ' / ' + h(dep) + ' / ' + h(key) + '

' + '

' + h(layout) + ' ・ ' + h(area) + ' ・ ' + h(floorSummaryRoom) + '

' + '
' + '
' + h(ctaDetailLabel) + '' + '' + h(ctaInquiryLabel) + '
' + '
' ); }) .join(''); var articleCls = 'building-card' + (extraArticleClass ? ' ' + extraArticleClass : ''); return ( '
' + buildingPhotoHtml + '
' + '

' + h(buildingName) + '

' + '

所在地' + h(address) + '

' + '

交通' + h(access) + '

' + '

築年' + h(built) + ' · ' + h(floorSummary) + '

' + typeMetaLine + '
' + roomRows + '
' ); } /** * TOP 新着 property-card 用: 掲載中一覧と同じ URL 集約(1 行 = 1 棟グループ) * @returns {{ html: string, urls: string[], kinds: string[] }} */ function buildListingPhotoForRow(row, title, opts) { opts = opts || {}; row = enrichRow(row); var group = { representative: row, rooms: [row] }; var buildingImg = pickBuildingImageForGroup(group); var mergedBuildingSlides = mergeCardSliderSlides( buildingImg, collectOrderedUrlsForBuildingGroup(group), row, urlKindOverlayForBuildingGroup(group), ); var mergedList = mergedBuildingSlides.urls; var slideKinds = mergedBuildingSlides.kinds; var rotDeck = rotateSlides(mergedList, slideKinds); mergedList = rotDeck.urls; slideKinds = rotDeck.kinds; var img = mergedList.length ? mergedList[0] : buildingImg; var wrapperBase = text(opts.wrapperBase, 'thumb-wrap list-card-thumb'); if (!img) wrapperBase += ' list-card-thumb--empty'; return { html: renderCardImageSliderHtml(img, title, mergedList, { wrapperBase: wrapperBase, imgClass: text(opts.imgClass, 'list-card-image'), decoding: !!opts.decoding, imageKinds: slideKinds, }), urls: mergedList, kinds: slideKinds, }; } return { renderHtml: renderHtml, enrichGroup: enrichGroup, buildListingPhotoForRow: buildListingPhotoForRow, }; });