Widget:C.Background

De Wiki Gla
Ir para navegação Ir para pesquisar

<script>

 /**
  * @typedef {Object} MediaWiki
  * @property {Object} util
  * @property {Function} util.wikiScript
  * @property {Function} util.wikiUrlencode
  * @property {Object} config
  * @property {Function} config.get
  */
 // @ts-ignore - MediaWiki global
 var mw = window.mw || {};
 (function () {
   /**
    * Builds a MediaWiki file URL from a filename
    * @param {string} fileName - The filename (with or without "Arquivo:" prefix)
    * @returns {string} The full URL to the file
    */
   function buildBgURL(fileName) {
     if (!fileName) return "";
     var base =
       window.mw && mw.util && typeof mw.util.wikiScript === "function"
         ? mw.util.wikiScript()
         : window.mw && mw.config
           ? mw.config.get("wgScript") || "/index.php"
           : "/index.php";
     var path =
       "Especial:FilePath/" + fileName.replace(/^Arquivo:|^File:/, "");
     if (window.mw && mw.util && typeof mw.util.wikiUrlencode === "function") {
       return base + "?title=" + mw.util.wikiUrlencode(path);
     } else {
       return base + "?title=" + encodeURIComponent(path).replace(/%2F/g, "/");
     }
   }
   /**
    * Sistema de cache persistente usando IndexedDB
    */
   var BgCache = (function () {
     var DB_NAME = "character-bg-cache";
     var DB_VERSION = 1;
     var STORE_NAME = "backgrounds";
     var db = null;
     /**
      * Inicializa o banco de dados IndexedDB
      */
     function initDB() {
       return new Promise(function (resolve, reject) {
         if (db) {
           resolve(db);
           return;
         }
         if (!window.indexedDB) {
           reject(new Error("IndexedDB não disponível"));
           return;
         }
         var request = indexedDB.open(DB_NAME, DB_VERSION);
         request.onerror = function () {
           reject(request.error);
         };
         request.onsuccess = function () {
           db = request.result;
           resolve(db);
         };
         request.onupgradeneeded = function (event) {
           var database = event.target.result;
           if (!database.objectStoreNames.contains(STORE_NAME)) {
             database.createObjectStore(STORE_NAME);
           }
         };
       });
     }
     /**
      * Obtém imagem do cache
      * @param {string} url - URL da imagem
      * @returns {Promise<string|null>} URL do blob ou null se não estiver em cache
      */
     function get(url) {
       return initDB()
         .then(function (database) {
           return new Promise(function (resolve, reject) {
             var transaction = database.transaction([STORE_NAME], "readonly");
             var store = transaction.objectStore(STORE_NAME);
             var request = store.get(url);
             request.onsuccess = function () {
               var entry = request.result;
               if (entry && entry.blob instanceof Blob) {
                 resolve(entry);
               } else if (entry && entry instanceof Blob) {
                 resolve({ blob: entry, meta: {}, cachedAt: 0 });
               } else {
                 resolve(null);
               }
             };
             request.onerror = function () {
               reject(request.error);
             };
           });
         })
         .catch(function () {
           return Promise.resolve(null);
         });
     }
     /**
      * Salva imagem no cache
      * @param {string} url - URL da imagem
      * @param {Blob} blob - Blob da imagem
      */
     function set(url, blob, meta) {
       initDB()
         .then(function (database) {
           var transaction = database.transaction([STORE_NAME], "readwrite");
           var store = transaction.objectStore(STORE_NAME);
           store.put(
             {
               blob: blob,
               meta: meta || {},
               cachedAt: Date.now(),
             },
             url
           );
         })
         .catch(function () {
           // Falha silenciosa - cache opcional
         });
     }
     function extractMeta(response) {
       return {
         etag:
           response.headers.get("ETag") ||
           response.headers.get("Etag") ||
           "",
         lastModified: response.headers.get("Last-Modified") || "",
       };
     }
     function metaMatches(a, b) {
       if (!a || !b) return false;
       if (a.etag && b.etag) return a.etag === b.etag;
       if (a.lastModified && b.lastModified)
         return a.lastModified === b.lastModified;
       return false;
     }
     function headMeta(url) {
       return fetch(url, { method: "HEAD", cache: "no-store" }).then(function (
         response
       ) {
         if (!response.ok) {
           throw new Error("HEAD falhou: " + response.status);
         }
         return extractMeta(response);
       });
     }
     function fetchBlob(url) {
       return fetch(url, { mode: "cors", cache: "no-store" }).then(function (
         response
       ) {
         if (!response.ok) {
           throw new Error(
             "Falha ao carregar imagem: " + response.status
           );
         }
         var meta = extractMeta(response);
         return response.blob().then(function (blob) {
           return { blob: blob, meta: meta };
         });
       });
     }
     /**
      * Carrega imagem e aplica cache
      * @param {string} url - URL da imagem
      * @returns {Promise<string>} URL do blob (do cache ou recém-carregada)
      */
     function loadImage(url) {
       return get(url)
         .then(function (cachedEntry) {
           if (cachedEntry && cachedEntry.blob instanceof Blob) {
             return headMeta(url)
               .then(function (remoteMeta) {
                 var hasRemoteMeta =
                   remoteMeta &&
                   (remoteMeta.etag || remoteMeta.lastModified);
                 if (hasRemoteMeta && metaMatches(cachedEntry.meta, remoteMeta)) {
                   return URL.createObjectURL(cachedEntry.blob);
                 }
                 return fetchBlob(url).then(function (fresh) {
                   set(url, fresh.blob, fresh.meta);
                   return URL.createObjectURL(fresh.blob);
                 });
               })
               .catch(function () {
                 return URL.createObjectURL(cachedEntry.blob);
               });
           }
           // Carrega a imagem e salva no cache
           return fetchBlob(url).then(function (fresh) {
             set(url, fresh.blob, fresh.meta);
             return URL.createObjectURL(fresh.blob);
           });
         })
         .catch(function () {
           // Em caso de erro, retorna a URL original
           return Promise.resolve(url);
         });
     }
     return {
       loadImage: loadImage,
     };
   })();
   /**
    * Applies background image to an element (com cache persistente)
    * @param {HTMLElement} el - The element to apply background to
    */
   function applyBg(el) {
     try {
       var url = el.getAttribute("data-bg-url");
       if (!url) {
         var f = el.getAttribute("data-bg-file");
         if (f) {
           url = buildBgURL(f);
           el.setAttribute("data-bg-url", url);
         }
       }
       if (url) {
         var setBgImage = function (primary, fallback) {
           if (!primary) return;
           if (fallback) {
             el.style.backgroundImage =
               'url("' + primary + '"), url("' + fallback + '")';
           } else {
             el.style.backgroundImage = 'url("' + primary + '")';
           }
         };
         var currentBg = el.style.backgroundImage || "";
         // Tenta carregar do cache primeiro
         BgCache.loadImage(url)
           .then(function (blobUrl) {
             if (blobUrl && blobUrl !== url) {
               setBgImage(blobUrl, url);
             } else {
               setBgImage(url);
             }
             // Remove o atributo temporário se existir
             el.removeAttribute("data-bg-loading");
           })
           .catch(function () {
             // Fallback para URL original em caso de erro
             setBgImage(url);
             el.removeAttribute("data-bg-loading");
           });
         // Aplica URL original imediatamente enquanto carrega do cache (progressive enhancement)
         if (!el.hasAttribute("data-bg-loading")) {
           el.setAttribute("data-bg-loading", "1");
           if (!currentBg || currentBg === "none") {
             setBgImage(url);
           }
         }
       }
     } catch (e) {
       /* no-op */
     }
   }
   // Aplicar backgrounds imediatamente
   document.querySelectorAll("[data-bg-url], [data-bg-file]").forEach(applyBg);
   // Apply to future elements (AJAX)
   new MutationObserver(function (mutations) {
     mutations.forEach(function (m) {
       if (m.type === "childList") {
         m.addedNodes.forEach(function (n) {
           if (n.nodeType === 1) {
             if (
               n.hasAttribute &&
               (n.hasAttribute("data-bg-file") ||
                 n.hasAttribute("data-bg-url"))
             ) {
               applyBg(n);
             }
             if (n.querySelectorAll) {
               n.querySelectorAll("[data-bg-file], [data-bg-url]").forEach(
                 applyBg
               );
             }
           }
         });
       } else if (m.type === "attributes" && m.target) {
         applyBg(m.target);
       }
     });
   }).observe(document.body, {
     attributes: true,
     attributeFilter: ["data-bg-file", "data-bg-url"],
     childList: true,
     subtree: true,
   });
 })();

</script>