{"lang":"en","introFile":"Title.jpg","favicon":"favicon.ico","facebookImage":"BenoitMalphettesLogo-V2Final2.png","browserTitle":"The Photography of Benoit Malphettes","metaDescription":"The Photography of Benoit Malphettes","metaKeywords":"benoit malphettes, Benoit,New York,  los angeles, photographer, photography, photographe, portraits,portrait photography, studio charis, pasadena, french,fine art photographer, benoit photographer, benoit photographe, photographer in Pasadena, photographer in Los Angeles,photographer in Riverside,fine art photographer, Benoit Malphettes,","cdnSslUri":"/pf-media","redirects":[],"blogSectionId":"15","advancedSeo":true,"globalBrowserTitle":true,"facebookUseMeta":false,"enablePinterest":false,"siteHead":"<!-- Global site tag (gtag.js) - Google Analytics --> <script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-21227015-1\"></script> <script>   window.dataLayer = window.dataLayer || [];   function gtag(){dataLayer.push(arguments);}   gtag('js', new Date());    gtag('config', 'UA-21227015-1'); </script>","siteBody":"<script>\n(() => {\n  const DEFAULT_SECTION_KEY = \"Studio book images\";\n\n  const INITIAL_DELAY_MS = 800;\n  const POLL_EVERY_MS = 250;\n  const MAX_WAIT_MS = 20000;\n\n  const LOG_PREFIX = \"[SBV]\";\n\n  function log(...args) { console.log(LOG_PREFIX, ...args); }\n  function warn(...args) { console.warn(LOG_PREFIX, ...args); }\n\n  function joinUrl(base, path) {\n    const b = String(base || \"\").replace(/\\/+$/, \"\");\n    const p = String(path || \"\").replace(/^\\/+/, \"\");\n    return b && p ? `${b}/${p}` : p || b;\n  }\n\n  function getDxModel() {\n    return window.DX_MODEL || null;\n  }\n\n  function getDxManifest(sectionKey) {\n    const m = getDxModel();\n    if (!m) return null;\n\n    const cdn = String(m?.settings?.cdnSslUri || \"\").replace(/\\/+$/, \"\");\n    const ids = m?.sections?.[sectionKey]?.mediaItems || [];\n    const paths = ids\n      .map((id) => m?.media?.[id]?.content)\n      .filter((v) => typeof v === \"string\" && v.trim().length > 0);\n\n    return { cdnSslUri: cdn, paths, sectionKey };\n  }\n\n  function chunkIntoPages(paths) {\n    const cover = paths[0] ? [paths[0]] : [];\n    const rest = paths.slice(1);\n    const spreads = [];\n    for (let i = 0; i < rest.length; i += 2) spreads.push(rest.slice(i, i + 2));\n    return [cover, ...spreads];\n  }\n\n  function el(tag, attrs = {}, children = []) {\n    const node = document.createElement(tag);\n    for (const [k, v] of Object.entries(attrs)) {\n      if (k === \"class\") node.className = v;\n      else if (k === \"html\") node.innerHTML = String(v);\n      else node.setAttribute(k, String(v));\n    }\n    for (const c of children) node.append(c);\n    return node;\n  }\n\n  function ensureLoadingUI(container) {\n    if (container.__sbvPrepared) return;\n    container.__sbvPrepared = true;\n    container.innerHTML = `\n      <div class=\"sbv-shell\">\n        <div class=\"sbv-stage\" role=\"region\" tabindex=\"0\" aria-label=\"Studio book viewer\">\n          <div style=\"font:14px/1.4 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;opacity:.75;\">\n            Loading book…\n          </div>\n        </div>\n        <button class=\"sbv-nav left\" type=\"button\" aria-label=\"Previous page\" disabled style=\"display:none\"></button>\n        <button class=\"sbv-nav right\" type=\"button\" aria-label=\"Next page\" disabled></button>\n        <div class=\"sbv-dots\" aria-hidden=\"true\"></div>\n      </div>\n    `;\n  }\n\n  function setMessage(container, msg) {\n    const stage = container.querySelector(\".sbv-stage\");\n    if (stage) stage.innerHTML = `<div style=\"font:14px/1.4 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;opacity:.8;text-align:center;padding:18px;\">${msg}</div>`;\n  }\n\n  function renderBookInto(container, { cdnSslUri, paths, sectionKey }) {\n    container.__sbvMounted = true;\n\n    const pages = chunkIntoPages(paths);\n    let pageIndex = 0;\n\n    const shell = el(\"div\", { class: \"sbv-shell\" });\n    const stage = el(\"div\", { class: \"sbv-stage\", role: \"region\", tabindex: \"0\", \"aria-label\": \"Studio book viewer\" });\n\n    const leftBtn = el(\"button\", {\n      class: \"sbv-nav left\",\n      type: \"button\",\n      \"aria-label\": \"Previous page\",\n      html: `\n         <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" aria-hidden=\"true\">\n    <path d=\"M14.5 5.5L8 12l6.5 6.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n  </svg>\n      `\n    });\n\n    const rightBtn = el(\"button\", {\n      class: \"sbv-nav right\",\n      type: \"button\",\n      \"aria-label\": \"Next page\",\n      html: `\n  <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" aria-hidden=\"true\">\n    <path d=\"M9.5 5.5L16 12l-6.5 6.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n  </svg>\n      `\n    });\n\n    const dots = el(\"div\", { class: \"sbv-dots\", \"aria-hidden\": \"true\" });\n\n    function render() {\n      stage.innerHTML = \"\";\n      dots.innerHTML = \"\";\n\n      const spread = pages[pageIndex] || [];\n      const isCover = pageIndex === 0;\n\n      const spreadEl = el(\"div\", { class: `sbv-spread ${spread.length === 2 ? \"two\" : \"one\"}` });\n\n      if (!spread.length) {\n        spreadEl.appendChild(el(\"div\", { html: \"No images.\" }));\n      } else {\n        for (const p of spread) {\n          spreadEl.appendChild(el(\"img\", {\n            class: \"sbv-img\",\n            src: joinUrl(cdnSslUri, p),\n            alt: isCover ? \"Cover\" : \"Page\",\n            loading: \"eager\"\n          }));\n        }\n      }\n\n      stage.appendChild(spreadEl);\n\n      leftBtn.style.display = isCover ? \"none\" : \"\";\n      leftBtn.disabled = pageIndex <= 0;\n      rightBtn.disabled = pageIndex >= pages.length - 1;\n\n      for (let i = 0; i < pages.length; i++) {\n        dots.appendChild(el(\"div\", { class: `sbv-dot ${i === pageIndex ? \"active\" : \"\"}` }));\n      }\n    }\n\n    function next() { if (pageIndex < pages.length - 1) { pageIndex++; render(); } }\n    function prev() { if (pageIndex > 0) { pageIndex--; render(); } }\n\n    rightBtn.addEventListener(\"click\", next);\n    leftBtn.addEventListener(\"click\", prev);\n    stage.addEventListener(\"keydown\", (e) => {\n      if (e.key === \"ArrowRight\") next();\n      if (e.key === \"ArrowLeft\") prev();\n    });\n\n    shell.append(stage, leftBtn, rightBtn, dots);\n    container.innerHTML = \"\";\n    container.appendChild(shell);\n\n    log(`Mounted section=\"${sectionKey}\" images=${paths.length}`);\n    render();\n  }\n\n  function tryMount(container) {\n    const sectionKey = container.getAttribute(\"data-section-key\") || DEFAULT_SECTION_KEY;\n    ensureLoadingUI(container);\n\n    const startTs = Date.now();\n    const poll = () => {\n      const m = getDxModel();\n      if (!m) {\n        if (Date.now() - startTs >= MAX_WAIT_MS) {\n          setMessage(container, \"Timed out waiting for DX_MODEL (app data).\");\n          warn(\"DX_MODEL never appeared.\");\n          return;\n        }\n        setTimeout(poll, POLL_EVERY_MS);\n        return;\n      }\n\n      const manifest = getDxManifest(sectionKey);\n\n      // Helpful error if the section key is wrong\n      const sections = Object.keys(m?.sections || {});\n      const hasSection = !!m?.sections?.[sectionKey];\n\n      if (!hasSection && sections.length) {\n        setMessage(container, `Section key not found: <b>${sectionKey}</b><br><br>Available sections:<br>${sections.map(s => `• ${s}`).join(\"<br>\")}`);\n        warn(\"Section key not found:\", sectionKey, \"available:\", sections);\n        return;\n      }\n\n      const ready = manifest && manifest.cdnSslUri && manifest.paths && manifest.paths.length > 0;\n      if (ready) {\n        renderBookInto(container, manifest);\n        return;\n      }\n\n      if (Date.now() - startTs >= MAX_WAIT_MS) {\n        setMessage(container, `Timed out waiting for media in section: <b>${sectionKey}</b>`);\n        warn(\"Timed out waiting for media:\", { sectionKey, manifest });\n        return;\n      }\n\n      setTimeout(poll, POLL_EVERY_MS);\n    };\n\n    setTimeout(poll, INITIAL_DELAY_MS);\n  }\n\n  function mountAll() {\n    const containers = Array.from(document.querySelectorAll(\"[data-studio-book]\"))\n      .filter((c) => !c.__sbvMounted);\n\n    if (!containers.length) {\n      log(\"No [data-studio-book] containers found on this page.\");\n      return;\n    }\n\n    log(\"Found containers:\", containers.length);\n    for (const c of containers) tryMount(c);\n  }\n\n  // Handles builders that inject page content after navigation\n  function observeForContainers() {\n    const mo = new MutationObserver(() => {\n      const pending = document.querySelector(\"[data-studio-book]:not([data-sbv-seen])\");\n      if (!pending) return;\n\n      document.querySelectorAll(\"[data-studio-book]:not([data-sbv-seen])\").forEach((c) => {\n        c.setAttribute(\"data-sbv-seen\", \"1\");\n      });\n      mountAll();\n    });\n\n    mo.observe(document.documentElement, { childList: true, subtree: true });\n  }\n\n  function safeStart() {\n    try {\n      mountAll();\n      observeForContainers();\n    } catch (e) {\n      console.error(LOG_PREFIX, \"Fatal error:\", e);\n    }\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\"DOMContentLoaded\", safeStart, { once: true });\n  } else {\n    safeStart();\n  }\n  window.addEventListener(\"load\", () => setTimeout(mountAll, 0));\n})();\n</script>","enableCookieBanner":false,"cookieBannerPosition":"bottom","cookieBannerLink":"http://aboutcookies.org","cookieBannerMessage":"We use cookies to enhance your experience. By continuing to visit this site you agree to our use of cookies.","localMasterVersion":"16.1","dev":false,"currentTemplate":72,"globalMasterVersion":"16.0","portfolioEmailMessage":"info@Benoit.LA","googleSiteVerification":"googled53458dfbfd43b36.html","logoText":"Benoit Malphettes","useHTML":true,"customFonts":["Didi_Regular:400:Didi_Regular:custom"],"inquiryTitle":"","generalEmail":"Benoit@BenoitMalphettes.com","betaProgramEnabled":true,"padUseDesktop":true,"inquiryInfo":"","podUseDesktop":true,"copyright":"","defaultEditorMode":"source","globalBetaVersion":"16.1","accountName":"benoitmalphettesphotos","contactInfo":"Benoit Malphettes\n\n626.390.4285","betaProgramVisible":true,"socialLinks":[10866,10865],"backgroundImages":[],"revision":88,"rsSslUri":"https://0e52076e533eb6f5ff25-71d2905eec7b3b5fa3c362e459ba3e97.ssl.cf1.rackcdn.com","filters":["Bio Photo","Library Best Camera","Library Blue Cut Fire","Library FASHION","Library Merchants Riverside","Library Portraits","Library Ruins","Library STILL LIFE","Library Scotland","Library Unreal Estate","Library drive time (Not Active)","Library logos","OLD PRESS PAGES","SOCIAL MEDIA LINKS","Studio Book","contact form","fonts","35mm Slides"],"adminSortDefault":"size descending","adminLockout":false}