Mudanças entre as edições de "Widget:C.Background"

De Wiki Gla
Ir para navegação Ir para pesquisar
m
m
 
(Uma revisão intermediária pelo mesmo usuário não está sendo mostrada)
Linha 95: Linha 95:


               request.onsuccess = function () {
               request.onsuccess = function () {
                 var blob = request.result;
                 var entry = request.result;
                 if (blob && blob instanceof Blob) {
                 if (entry && entry.blob instanceof Blob) {
                   var blobUrl = URL.createObjectURL(blob);
                   resolve(entry);
                   resolve(blobUrl);
                } else if (entry && entry instanceof Blob) {
                   resolve({ blob: entry, meta: {}, cachedAt: 0 });
                 } else {
                 } else {
                   resolve(null);
                   resolve(null);
Linha 119: Linha 120:
       * @param {Blob} blob - Blob da imagem
       * @param {Blob} blob - Blob da imagem
       */
       */
       function set(url, blob) {
       function set(url, blob, meta) {
         initDB()
         initDB()
           .then(function (database) {
           .then(function (database) {
             var transaction = database.transaction([STORE_NAME], "readwrite");
             var transaction = database.transaction([STORE_NAME], "readwrite");
             var store = transaction.objectStore(STORE_NAME);
             var store = transaction.objectStore(STORE_NAME);
             store.put(blob, url);
             store.put(
              {
                blob: blob,
                meta: meta || {},
                cachedAt: Date.now(),
              },
              url
            );
           })
           })
           .catch(function () {
           .catch(function () {
             // Falha silenciosa - cache opcional
             // 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 };
          });
        });
       }
       }


Linha 138: Linha 191:
       function loadImage(url) {
       function loadImage(url) {
         return get(url)
         return get(url)
           .then(function (cachedUrl) {
           .then(function (cachedEntry) {
             if (cachedUrl) {
             if (cachedEntry && cachedEntry.blob instanceof Blob) {
               return Promise.resolve(cachedUrl);
               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
             // Carrega a imagem e salva no cache
             return fetch(url, { mode: "cors" })
             return fetchBlob(url).then(function (fresh) {
              .then(function (response) {
               set(url, fresh.blob, fresh.meta);
                if (!response.ok) {
              return URL.createObjectURL(fresh.blob);
                  throw new Error(
            });
                    "Falha ao carregar imagem: " + response.status
                  );
                }
                return response.blob();
               })
              .then(function (blob) {
                set(url, blob);
                return URL.createObjectURL(blob);
              });
           })
           })
           .catch(function () {
           .catch(function () {
Linha 185: Linha 244:


         if (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
           // Tenta carregar do cache primeiro
           BgCache.loadImage(url)
           BgCache.loadImage(url)
             .then(function (blobUrl) {
             .then(function (blobUrl) {
               el.style.backgroundImage = 'url("' + blobUrl + '")';
               if (blobUrl && blobUrl !== url) {
                setBgImage(blobUrl, url);
              } else {
                setBgImage(url);
              }
               // Remove o atributo temporário se existir
               // Remove o atributo temporário se existir
               el.removeAttribute("data-bg-loading");
               el.removeAttribute("data-bg-loading");
Linha 194: Linha 267:
             .catch(function () {
             .catch(function () {
               // Fallback para URL original em caso de erro
               // Fallback para URL original em caso de erro
               el.style.backgroundImage = 'url("' + url + '")';
              setBgImage(url);
               el.removeAttribute("data-bg-loading");
             });
             });


Linha 200: Linha 274:
           if (!el.hasAttribute("data-bg-loading")) {
           if (!el.hasAttribute("data-bg-loading")) {
             el.setAttribute("data-bg-loading", "1");
             el.setAttribute("data-bg-loading", "1");
             el.style.backgroundImage = 'url("' + url + '")';
             if (!currentBg || currentBg === "none") {
              setBgImage(url);
            }
           }
           }
         }
         }

Edição atual tal como às 05h20min de 24 de janeiro de 2026

<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>