← back to Rentv V1

public/versions/render.js

82 lines

/* rentv-v1 version renderer — one file, 10 distinct homepage layouts. Reads
   document.body.dataset.ver and renders that layout from the live /api/news feed. */
(function () {
  var ver = document.body.dataset.ver || 'wire';
  var esc = function (s) { return (s || '').replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); };
  var href = function (x) { return '/news/' + encodeURIComponent(x.id); };
  var img = function (x, cls) { return x && x.image ? '<div class="' + (cls || 'imgph') + '" style="background:#12161a url(\'' + esc(x.image) + '\') center/cover"></div>' : '<div class="' + (cls || 'imgph') + '"></div>'; };
  var css = function (t) { var s = document.createElement('style'); s.textContent = t; document.head.appendChild(s); };

  var L = {
    // 1. THE WIRE — lead + 3-col cards + rail
    wire: function (a) {
      css('.g{display:grid;grid-template-columns:1fr 300px;gap:34px}.lead .imgph{aspect-ratio:16/8;margin-bottom:12px}.lead h1{font-size:34px;margin:8px 0}.cards{display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;margin-top:24px}.cards .imgph{aspect-ratio:16/10}.cards h4{font-size:16px;margin:8px 0 4px}.mr{list-style:none;counter-reset:n}.mr li{counter-increment:n;display:flex;gap:10px;padding:10px 0;border-bottom:1px solid var(--ln)}.mr li:before{content:counter(n);color:var(--a);font-family:var(--se);font-size:24px}.mr a{font-size:14px;font-weight:600}@media(max-width:820px){.g{grid-template-columns:1fr}.cards{grid-template-columns:1fr 1fr}}');
      var lead = a[0], rest = a.slice(1, 13);
      return '<div class="g"><div><a class="lead" href="' + href(lead) + '">' + img(lead) + '<span class="chip">' + esc(lead.cat) + '</span><h1>' + esc(lead.title) + '</h1><div class="byl">RENTV Staff · live</div></a><div class="cards">' +
        rest.map(function (x) { return '<a href="' + href(x) + '">' + img(x) + '<span class="chip">' + esc(x.cat) + '</span><h4>' + esc(x.title) + '</h4></a>'; }).join('') + '</div></div>' +
        '<aside><h3 style="border-top:3px solid var(--ink);padding-top:8px">Most Read</h3><ol class="mr">' + a.slice(0, 6).map(function (x) { return '<li><a href="' + href(x) + '">' + esc(x.title) + '</a></li>'; }).join('') + '</ol></aside></div>';
    },
    // 2. BROADSHEET — dense two-column serif
    broadsheet: function (a) {
      css('.bs{column-count:2;column-gap:40px}.bs .it{break-inside:avoid;padding:14px 0;border-bottom:1px solid var(--ln)}.bs h3{font-size:21px;margin-bottom:4px}.bs .imgph{aspect-ratio:16/9;margin-bottom:8px}.bs .it:first-child{column-span:all;text-align:center;border-bottom:3px double var(--ink);padding-bottom:20px}.bs .it:first-child h3{font-size:36px}@media(max-width:720px){.bs{column-count:1}}');
      return '<div class="bs">' + a.slice(0, 16).map(function (x, i) { return '<div class="it"><span class="chip">' + esc(x.cat) + '</span><a href="' + href(x) + '"><h3>' + esc(x.title) + '</h3></a>' + (i < 4 ? img(x) : '') + '<div class="byl">RENTV Staff</div></div>'; }).join('') + '</div>';
    },
    // 3. TERMINAL — dark data rows
    terminal: function (a) {
      css('.tm{font-family:var(--sa)}.tr{display:grid;grid-template-columns:90px 1fr 120px;gap:14px;align-items:center;padding:12px 10px;border-bottom:1px solid var(--ln)}.tr:hover{background:var(--wa)}.tr .c{font-size:11px;font-weight:800;text-transform:uppercase;color:var(--a);letter-spacing:.05em}.tr h4{font-size:15px;font-weight:600;font-family:var(--sa)}.tr .id{text-align:right;color:var(--sub);font-size:12px}.tmh{display:grid;grid-template-columns:90px 1fr 120px;gap:14px;padding:8px 10px;border-bottom:2px solid var(--a);font-size:11px;text-transform:uppercase;letter-spacing:.1em;color:var(--sub)}');
      return '<div class="tm"><div class="tmh"><span>Sector</span><span>Headline</span><span style="text-align:right">Ref</span></div>' +
        a.slice(0, 22).map(function (x) { return '<a class="tr" href="' + href(x) + '"><span class="c">' + esc(x.cat) + '</span><h4>' + esc(x.title) + '</h4><span class="id">#' + esc(x.id) + '</span></a>'; }).join('') + '</div>';
    },
    // 4. MAGAZINE — giant hero + 2 secondaries
    magazine: function (a) {
      css('.mg .hero{position:relative;border-radius:10px;overflow:hidden;aspect-ratio:16/7}.mg .hero .imgph{position:absolute;inset:0;border-radius:0}.mg .hero .ov{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:flex-end;padding:34px;background:linear-gradient(0deg,rgba(0,0,0,.72),transparent 65%);color:#fff}.mg .hero h1{font-size:clamp(28px,4vw,48px);max-width:20ch}.mg .two{display:grid;grid-template-columns:1fr 1fr;gap:26px;margin:26px 0}.mg .two .imgph{aspect-ratio:16/9;margin-bottom:10px}.mg .two h3{font-size:22px}.mg .rest{columns:3;column-gap:26px}.mg .rest a{display:block;break-inside:avoid;padding:8px 0;border-bottom:1px solid var(--ln);font-family:var(--se);font-size:16px}@media(max-width:720px){.mg .two,.mg .rest{grid-template-columns:1fr;columns:1}}');
      var h = a[0];
      return '<div class="mg"><a class="hero" href="' + href(h) + '">' + img(h) + '<div class="ov"><span class="chip" style="color:#fff">' + esc(h.cat) + '</span><h1>' + esc(h.title) + '</h1></div></a>' +
        '<div class="two">' + a.slice(1, 3).map(function (x) { return '<a href="' + href(x) + '">' + img(x) + '<span class="chip">' + esc(x.cat) + '</span><h3>' + esc(x.title) + '</h3></a>'; }).join('') + '</div>' +
        '<div class="rest">' + a.slice(3, 15).map(function (x) { return '<a href="' + href(x) + '">' + esc(x.title) + '</a>'; }).join('') + '</div></div>';
    },
    // 5. DIGEST — numbered compact list
    digest: function (a) {
      css('.dg{max-width:720px;margin:0 auto;counter-reset:d}.dg .it{counter-increment:d;display:flex;gap:16px;padding:16px 0;border-bottom:1px solid var(--ln)}.dg .it:before{content:counter(d);font-family:var(--se);font-size:28px;color:var(--a);min-width:40px}.dg h3{font-size:19px}.dg .c{font-size:11px;font-weight:800;text-transform:uppercase;color:var(--a);letter-spacing:.05em}');
      return '<div class="dg"><p class="byl" style="text-align:center;margin-bottom:10px">The RENTV Daily — today\'s Western-U.S. CRE, in order.</p>' +
        a.slice(0, 15).map(function (x) { return '<a class="it" href="' + href(x) + '"><div><span class="c">' + esc(x.cat) + '</span><h3>' + esc(x.title) + '</h3></div></a>'; }).join('') + '</div>';
    },
    // 6. GRID — uniform photo cards
    grid: function (a) {
      css('.gr{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:18px}.gr a{background:var(--wa);border:1px solid var(--ln);border-radius:8px;overflow:hidden}.gr .imgph{aspect-ratio:16/10;border-radius:0}.gr .b{padding:14px 16px}.gr .c{font-size:11px;font-weight:800;text-transform:uppercase;color:var(--a)}.gr h4{font-size:16px;margin-top:6px}');
      return '<div class="gr">' + a.slice(0, 18).map(function (x) { return '<a href="' + href(x) + '">' + img(x) + '<div class="b"><span class="c">' + esc(x.cat) + '</span><h4>' + esc(x.title) + '</h4></div></a>'; }).join('') + '</div>';
    },
    // 7. SPLIT — headlines left, rail right, no hero
    split: function (a) {
      css('.sp{display:grid;grid-template-columns:1fr 320px;gap:40px}.sp .it{padding:16px 0;border-bottom:1px solid var(--ln)}.sp h3{font-size:23px}.sp .c{font-size:11px;font-weight:800;text-transform:uppercase;color:var(--a)}.sp aside h3{border-top:3px solid var(--ink);padding-top:8px}.sp .mr{list-style:none}.sp .mr li{padding:9px 0;border-bottom:1px solid var(--ln)}.sp .mr a{font-weight:600;font-size:14px}@media(max-width:820px){.sp{grid-template-columns:1fr}}');
      return '<div class="sp"><div>' + a.slice(0, 12).map(function (x) { return '<a class="it" href="' + href(x) + '"><span class="c">' + esc(x.cat) + '</span><h3>' + esc(x.title) + '</h3><div class="byl">RENTV Staff · live</div></a>'; }).join('') +
        '</div><aside><h3>Most Read</h3><ul class="mr">' + a.slice(0, 8).map(function (x) { return '<li><a href="' + href(x) + '">' + esc(x.title) + '</a></li>'; }).join('') + '</ul></aside></div>';
    },
    // 8. DASHBOARD — metrics band + list + market cards
    dashboard: function (a) {
      css('.mb{display:flex;flex-wrap:wrap;gap:30px;padding:18px 20px;background:var(--wa);border:1px solid var(--ln);border-radius:10px;margin-bottom:24px}.mb .v{font-family:var(--se);font-size:30px;color:var(--a)}.mb .l{font-size:11px;text-transform:uppercase;color:var(--sub)}.db{display:grid;grid-template-columns:1fr 300px;gap:30px}.db .it{padding:12px 0;border-bottom:1px solid var(--ln)}.db h4{font-size:17px}.db .c{font-size:11px;font-weight:800;text-transform:uppercase;color:var(--a)}.mc{background:var(--wa);border:1px solid var(--ln);border-radius:8px;padding:14px 16px;margin-bottom:12px}.mc .n{font-family:var(--se);font-size:22px}@media(max-width:820px){.db{grid-template-columns:1fr}}');
      var band = [['3','Web properties'],['1998','Est.'],['600+','Conf. audience'],['Daily','Newsletter']];
      return '<div class="mb">' + band.map(function (b) { return '<div><div class="v">' + b[0] + '</div><div class="l">' + b[1] + '</div></div>'; }).join('') + '</div>' +
        '<div class="db"><div>' + a.slice(0, 14).map(function (x) { return '<a class="it" href="' + href(x) + '"><span class="c">' + esc(x.cat) + '</span><h4>' + esc(x.title) + '</h4></a>'; }).join('') +
        '</div><aside><div class="mc"><div class="l" style="font-size:11px;text-transform:uppercase;color:var(--sub)">LA Office Vacancy</div><div class="n">23.1%</div></div><div class="mc"><div class="l" style="font-size:11px;text-transform:uppercase;color:var(--sub)">IE Industrial Cap</div><div class="n">5.2%</div></div><div class="mc"><div class="l" style="font-size:11px;text-transform:uppercase;color:var(--sub)">10-YR Treasury</div><div class="n">4.63%</div></div></aside></div>';
    },
    // 9. MINIMAL — single centered column, whitespace
    minimal: function (a) {
      css('.mn{max-width:640px;margin:0 auto}.mn .it{padding:26px 0;border-bottom:1px solid var(--ln)}.mn h3{font-size:24px;font-weight:400}.mn .c{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--a)}.mn .byl{margin-top:8px}');
      return '<div class="mn">' + a.slice(0, 12).map(function (x) { return '<a class="it" href="' + href(x) + '" style="display:block"><span class="c">' + esc(x.cat) + '</span><h3>' + esc(x.title) + '</h3><div class="byl">RENTV Staff · Western U.S.</div></a>'; }).join('') + '</div>';
    },
    // 10. BIG FEED — one column, large images
    feed: function (a) {
      css('.fd{max-width:680px;margin:0 auto}.fd a{display:block;margin-bottom:30px}.fd .imgph{aspect-ratio:16/9;margin-bottom:12px}.fd .c{font-size:11px;font-weight:800;text-transform:uppercase;color:var(--a)}.fd h2{font-size:26px;margin:6px 0}');
      return '<div class="fd">' + a.slice(0, 12).map(function (x) { return '<a href="' + href(x) + '">' + img(x) + '<span class="c">' + esc(x.cat) + '</span><h2>' + esc(x.title) + '</h2><div class="byl">RENTV Staff · live from rentv.com</div></a>'; }).join('') + '</div>';
    },
  };

  fetch('/api/news').then(function (r) { return r.json(); }).then(function (d) {
    var items = (d.items || []);
    var app = document.getElementById('app');
    if (!items.length) { app.innerHTML = '<div class="load">Live feed is refreshing — check back shortly.</div>'; return; }
    app.innerHTML = (L[ver] || L.wire)(items);
  }).catch(function () { document.getElementById('app').innerHTML = '<div class="load">Live feed temporarily unavailable — retrying…</div>'; });
})();