PageSourceSearch

https://chateando.net/ch-app-banner.js?v=4

js chateando.net collected 2026-09-24 09:00:07 UTC 24,102 bytes, 191 lines download raw bytes

1/**
2 * ch-app-banner.js — invita a instalar la app Android (APK oficial) desde la
3 * web, con la maña de una PWA. UN script para los cuatro sitios (Yvenel,
4 * 2026-08-19): cada web lo carga con data-attributes:
5 *
6 *   <script src="https://chateando.net/ch-app-banner.js" defer
7 *           data-app="Ticket3D" data-paquete="com.ticket3d.app"
8 *           data-apk="https://chateando.net/descargas/ticket3d-aptoide.apk"
9 *           data-color="#43a047" data-derecha="84"></script>
10 *
11 * Reglas de la casa:
12 *  - SOLO Android (ni iPhone ni escritorio) y nunca dentro de una app/PWA.
13 *  - Si la app YA está instalada no se enseña nada — se comprueba con
14 *    navigator.getInstalledRelatedApps(): exige el manifest de la web con
15 *    related_applications y el asset_statements dentro del APK. Al
16 *    desinstalarla, el banner vuelve solo.
17 *  - El banner cerrado reaparece a la HORA (no a los 14 días). El icono del
18 *    header es persistente siempre.
19 *  - 30 idiomas por navigator.languages, 10 mensajes por idioma elegidos al
20 *    azar en cada impresión. Árabe y hebreo van con dir=rtl.
21 */
22(function () {
23  'use strict';
24  var cs = document.currentScript;
25  if (!cs) return;
26  var APP = cs.dataset.app || 'la app';
27  var PAQUETE = cs.dataset.paquete || '';
28  var APK = cs.dataset.apk || '#';
29  // Cache-buster: el alias del APK cambia de contenido con cada versión y a
30  // algún navegador se le quedaba la build vieja pegada. La query rompe esa
31  // caché sin romper el nombre estable (subir el número al publicar builds).
32  if (APK.indexOf('?') < 0) APK += '?v=2';
33  var COLOR = cs.dataset.color || '#e91e63';
34  var DERECHA = parseInt(cs.dataset.derecha || '64', 10);
35  // Ajuste vertical opcional: algunos headers son más altos de lo que se ve
36  // (envuelven pestañas) y el 50% deja el icono colgando. data-arriba lo
37  // clava a N px del borde superior del header.
38  var ARRIBA = cs.dataset.arriba ? parseInt(cs.dataset.arriba, 10) : null;
39
40  if (!/Android/i.test(navigator.userAgent)) return;
41  if (window.matchMedia && matchMedia('(display-mode: standalone)').matches) return;
42
43  var RTL = { ar: 1, he: 1 };
44  var L = {
45    es: { b: 'Instalar', s: 'APK oficial firmado', m: ['Instala la app de {app} para Android', '{app} completa, en tu Android', 'Llévate {app} en el bolsillo', 'La app de {app}: más rápida y cómoda', 'Mejor en la app: instala {app}', 'Un toque y {app} en tu móvil', 'Tu {app} de cada día, ahora como app', 'Instala {app} y entra al instante', 'La experiencia completa de {app}, solo en la app', '¿Sin la app todavía? Instala {app}'] },
46    en: { b: 'Install', s: 'Official signed APK', m: ['Install the {app} app for Android', 'The full {app}, on your Android', 'Take {app} with you — get the app', '{app} is faster in the app', 'Better in the app: install {app}', 'One tap and {app} is on your phone', 'Your everyday {app}, now as an app', 'Install {app} and jump right in', 'The complete {app} experience, app only', 'No app yet? Install {app}'] },
47    fr: { b: 'Installer', s: 'APK officiel signé', m: ['Installez l’appli {app} pour Android', '{app} au complet, sur votre Android', 'Emportez {app} partout — installez l’appli', '{app} est plus rapide dans l’appli', 'C’est mieux dans l’appli : installez {app}', 'Un geste et {app} est sur votre téléphone', 'Votre {app} de tous les jours, en appli', 'Installez {app} et entrez direct', 'L’expérience {app} complète, dans l’appli', 'Pas encore l’appli ? Installez {app}'] },
48    de: { b: 'Installieren', s: 'Offizielles signiertes APK', m: ['Hol dir die {app}-App für Android', '{app} komplett — auf deinem Android', 'Nimm {app} überallhin mit — als App', 'In der App ist {app} schneller', 'Besser in der App: {app} installieren', 'Ein Tipp und {app} ist auf deinem Handy', 'Dein tägliches {app}, jetzt als App', '{app} installieren und direkt loslegen', 'Das volle {app}-Erlebnis gibt es in der App', 'Noch keine App? Installier {app}'] },
49    it: { b: 'Installa', s: 'APK ufficiale firmato', m: ['Installa l’app di {app} per Android', '{app} al completo, sul tuo Android', 'Porta {app} con te — installa l’app', 'Nell’app {app} è più veloce', 'Meglio nell’app: installa {app}', 'Un tocco e {app} è sul tuo telefono', 'Il tuo {app} di ogni giorno, ora come app', 'Installa {app} ed entra subito', 'L’esperienza completa di {app}, solo nell’app', 'Ancora senza app? Installa {app}'] },
50    pt: { b: 'Instalar', s: 'APK oficial assinado', m: ['Instale o app do {app} para Android', '{app} completo, no seu Android', 'Leve o {app} com você — instale o app', 'No app, o {app} é mais rápido', 'Melhor no app: instale o {app}', 'Um toque e o {app} está no seu celular', 'Seu {app} de todo dia, agora como app', 'Instale o {app} e entre na hora', 'A experiência completa do {app}, só no app', 'Ainda sem o app? Instale o {app}'] },
51    ru: { b: 'Установить', s: 'Официальный подписанный APK', m: ['Установите приложение {app} для Android', 'Весь {app} — на вашем Android', 'Возьмите {app} с собой — установите приложение', 'В приложении {app} быстрее', 'В приложении удобнее: установите {app}', 'Одно касание — и {app} на вашем телефоне', 'Ваш {app} каждый день, теперь как приложение', 'Установите {app} и заходите сразу', 'Полный {app} — только в приложении', 'Ещё без приложения? Установите {app}'] },
52    uk: { b: 'Встановити', s: 'Офіційний підписаний APK', m: ['Встановіть застосунок {app} для Android', 'Увесь {app} — на вашому Android', 'Візьміть {app} із собою — встановіть застосунок', 'У застосунку {app} швидший', 'У застосунку зручніше: встановіть {app}', 'Один дотик — і {app} на вашому телефоні', 'Ваш щоденний {app} — тепер як застосунок', 'Встановіть {app} і заходьте одразу', 'Повний {app} — лише в застосунку', 'Ще без застосунку? Встановіть {app}'] },
53    pl: { b: 'Zainstaluj', s: 'Oficjalny podpisany APK', m: ['Zainstaluj aplikację {app} na Androida', 'Cały {app} na Twoim Androidzie', 'Zabierz {app} ze sobą — zainstaluj aplikację', 'W aplikacji {app} działa szybciej', 'Lepiej w aplikacji: zainstaluj {app}', 'Jedno dotknięcie i {app} jest na telefonie', 'Twój codzienny {app}, teraz jako aplikacja', 'Zainstaluj {app} i wejdź od razu', 'Pełny {app} tylko w aplikacji', 'Nie masz jeszcze aplikacji? Zainstaluj {app}'] },
54    nl: { b: 'Installeren', s: 'Officiële ondertekende APK', m: ['Installeer de {app}-app voor Android', 'Heel {app} op je Android', 'Neem {app} overal mee — installeer de app', 'In de app is {app} sneller', 'Beter in de app: installeer {app}', 'Eén tik en {app} staat op je telefoon', 'Je dagelijkse {app}, nu als app', 'Installeer {app} en ga meteen verder', 'De volledige {app}-ervaring, alleen in de app', 'Nog geen app? Installeer {app}'] },
55    ro: { b: 'Instalează', s: 'APK oficial semnat', m: ['Instalează aplicația {app} pentru Android', '{app} complet, pe Androidul tău', 'Ia {app} cu tine — instalează aplicația', 'În aplicație, {app} e mai rapid', 'Mai bine în aplicație: instalează {app}', 'O atingere și {app} e pe telefonul tău', '{app} de zi cu zi, acum ca aplicație', 'Instalează {app} și intră imediat', 'Experiența completă {app}, doar în aplicație', 'Încă fără aplicație? Instalează {app}'] },
56    cs: { b: 'Nainstalovat', s: 'Oficiální podepsaný APK', m: ['Nainstalujte si aplikaci {app} pro Android', 'Celý {app} ve vašem Androidu', 'Vezměte si {app} s sebou — nainstalujte aplikaci', 'V aplikaci je {app} rychlejší', 'V aplikaci je to lepší: nainstalujte {app}', 'Jedno klepnutí a {app} je v telefonu', 'Váš každodenní {app}, teď jako aplikace', 'Nainstalujte {app} a rovnou vstupte', 'Kompletní {app} jen v aplikaci', 'Ještě bez aplikace? Nainstalujte {app}'] },
57    hu: { b: 'Telepítés', s: 'Hivatalos aláírt APK', m: ['Telepítsd a(z) {app} appot Androidra', 'A teljes {app} az Androidodon', 'Vidd magaddal a(z) {app}-ot — telepítsd az appot', 'Az appban a(z) {app} gyorsabb', 'Appban jobb: telepítsd a(z) {app}-ot', 'Egy érintés, és a(z) {app} a telefonodon van', 'A mindennapi {app}-od, most appként', 'Telepítsd a(z) {app}-ot, és lépj be azonnal', 'A teljes {app}-élmény csak az appban', 'Még nincs meg az app? Telepítsd a(z) {app}-ot'] },
58    el: { b: 'Εγκατάσταση', s: 'Επίσημο υπογεγραμμένο APK', m: ['Εγκατάστησε την εφαρμογή {app} για Android', 'Όλο το {app} στο Android σου', 'Πάρε το {app} μαζί σου — βάλε την εφαρμογή', 'Στην εφαρμογή το {app} είναι πιο γρήγορο', 'Καλύτερα στην εφαρμογή: εγκατάστησε το {app}
58', 'Ένα άγγιγμα και το {app} είναι στο κινητό σου', 'Το καθημερινό σου {app}, τώρα ως εφαρμογή', 'Εγκατάστησε το {app} και μπες αμέσως', 'Η πλήρης εμπειρία {app}, μόνο στην εφαρμογή', 'Ακόμα χωρίς την εφαρμογή; Εγκατάστησε το {app}'] },
59    sv: { b: 'Installera', s: 'Officiell signerad APK', m: ['Installera {app}-appen för Android', 'Hela {app} på din Android', 'Ta med {app} överallt — skaffa appen', 'I appen är {app} snabbare', 'Bättre i appen: installera {app}', 'Ett tryck och {app} finns i din mobil', 'Ditt dagliga {app}, nu som app', 'Installera {app} och kom igång direkt', 'Hela {app}-upplevelsen, bara i appen', 'Ingen app än? Installera {app}'] },
60    da: { b: 'Installér', s: 'Officiel signeret APK', m: ['Installér {app}-appen til Android', 'Hele {app} på din Android', 'Tag {app} med dig — hent appen', 'I appen er {app} hurtigere', 'Bedre i appen: installér {app}', 'Ét tryk, og {app} er på din telefon', 'Din daglige {app}, nu som app', 'Installér {app} og kom i gang med det samme', 'Den fulde {app}-oplevelse, kun i appen', 'Ingen app endnu? Installér {app}'] },
61    nb: { b: 'Installer', s: 'Offisiell signert APK', m: ['Installer {app}-appen for Android', 'Hele {app} på din Android', 'Ta med {app} overalt — hent appen', 'I appen er {app} raskere', 'Bedre i appen: installer {app}', 'Ett trykk, og {app} er på telefonen din', 'Din daglige {app}, nå som app', 'Installer {app} og kom i gang med én gang', 'Hele {app}-opplevelsen, bare i appen', 'Ingen app ennå? Installer {app}'] },
62    fi: { b: 'Asenna', s: 'Virallinen allekirjoitettu APK', m: ['Asenna {app}-sovellus Androidille', 'Koko {app} Androidillasi', 'Ota {app} mukaan — asenna sovellus', 'Sovelluksessa {app} on nopeampi', 'Parempi sovelluksessa: asenna {app}', 'Yksi napautus, ja {app} on puhelimessasi', 'Jokapäiväinen {app}, nyt sovelluksena', 'Asenna {app} ja aloita heti', 'Täysi {app}-kokemus vain sovelluksessa', 'Ei vielä sovellusta? Asenna {app}'] },
63    tr: { b: 'Yükle', s: 'Resmî imzalı APK', m: ['Android için {app} uygulamasını yükle', '{app}’in tamamı Android’inde', '{app}’i yanında taşı — uygulamayı yükle', 'Uygulamada {app} daha hızlı', 'Uygulamada daha iyi: {app}’i yükle', 'Tek dokunuş, {app} telefonunda', 'Her günkü {app}’in, artık uygulama olarak', '{app}’i yükle, hemen gir', 'Tam {app} deneyimi yalnızca uygulamada', 'Uygulama hâlâ yok mu? {app}’i yükle'] },
64    ar: { b: 'تثبيت', s: 'APK رسمي موقّع', m: ['ثبّت تطبيق {app} لأندرويد', '{app} كاملاً على هاتفك أندرويد', 'خذ {app} معك أينما كنت — ثبّت التطبيق', '{app} أسرع داخل التطبيق', 'التجربة أفضل في التطبيق: ثبّت {app}', 'لمسة واحدة ويصبح {app} على هاتفك', '{app} اليومي، الآن كتطبيق', 'ثبّت {app} وادخل فوراً', 'تجربة {app} الكاملة في التطبيق فقط', 'لم تثبّت التطبيق بعد؟ ثبّت {app}'] },
65    he: { b: 'התקנה', s: 'APK רשמי חתום', m: ['התקינו את אפליקציית {app} לאנדרואיד', 'כל {app} באנדרואיד שלכם', 'קחו את {app} לכל מקום — התקינו את האפליקציה', 'באפליקציה {app} מהיר יותר', 'עדיף באפליקציה: התקינו את {app}', 'נגיעה אחת ו-{app} אצלכם בטלפון', 'ה-{app} היומי שלכם, עכשיו כאפליקציה', 'התקינו את {app} והיכנסו מיד', 'חוויית {app} המלאה, רק באפליקציה', 'עדיין בלי האפליקציה? התקינו את {app}'] },
66    hi: { b: 'इंस्टॉल करें', s: 'आधिकारिक हस्ताक्षरित APK', m: ['Android के लिए {app} ऐप इंस्टॉल करें', 'पूरा {app} आपके Android पर', '{app} साथ ले चलें — ऐप इंस्टॉल करें', 'ऐप में {app} और तेज़ है', 'ऐप में बेहतर: {app}
66 इंस्टॉल करें', 'एक टैप और {app} आपके फ़ोन में', 'आपका रोज़ का {app}, अब ऐप के रूप में', '{app} इंस्टॉल करें और तुरंत शुरू करें', '{app} का पूरा अनुभव सिर्फ़ ऐप में', 'अभी तक ऐप नहीं? {app} इंस्टॉल करें'] },
67    bn: { b: 'ইনস্টল করুন', s: 'অফিসিয়াল স্বাক্ষরিত APK', m: ['Android-এর জন্য {app} অ্যাপ ইনস্টল করুন', 'পুরো {app} আপনার Android-এ', '{app} সাথে রাখুন — অ্যাপটি ইনস্টল করুন', 'অ্যাপে {app} আরও দ্রুত', 'অ্যাপেই ভালো: {app} ইনস্টল করুন', 'এক টোকায় {app} আপনার ফোনে', 'আপনার প্রতিদিনের {app}, এখন অ্যাপ হিসেবে', '{app} ইনস্টল করুন, সাথে সাথে ঢুকুন', '{app}-এর পূর্ণ অভিজ্ঞতা শুধু অ্যাপে', 'এখনও অ্যাপ নেই? {app} ইনস্টল করুন'] },
68    id: { b: 'Instal', s: 'APK resmi bertanda tangan', m: ['Instal aplikasi {app} untuk Android', '{app} lengkap di Android kamu', 'Bawa {app} ke mana saja — instal aplikasinya', 'Di aplikasi, {app} lebih cepat', 'Lebih enak di aplikasi: instal {app}', 'Sekali ketuk, {app} ada di ponselmu', '{app} harianmu, kini sebagai aplikasi', 'Instal {app} dan langsung masuk', 'Pengalaman {app} seutuhnya hanya di aplikasi', 'Belum punya aplikasinya? Instal {app}'] },
69    ms: { b: 'Pasang', s: 'APK rasmi ditandatangani', m: ['Pasang aplikasi {app} untuk Android', '{app} sepenuhnya di Android anda', 'Bawa {app} ke mana-mana — pasang aplikasinya', 'Dalam aplikasi, {app} lebih pantas', 'Lebih baik dalam aplikasi: pasang {app}', 'Satu ketukan dan {app} ada di telefon anda', '{app} harian anda, kini sebagai aplikasi', 'Pasang {app} dan terus masuk', 'Pengalaman penuh {app} hanya dalam aplikasi', 'Belum ada aplikasi? Pasang {app}'] },
70    vi: { b: 'Cài đặt', s: 'APK chính thức có chữ ký', m: ['Cài ứng dụng {app} cho Android', 'Trọn vẹn {app} trên Android của bạn', 'Mang {app} theo bên mình — cài ứng dụng', 'Trong ứng dụng, {app} nhanh hơn', 'Dùng ứng dụng tốt hơn: cài {app}', 'Một chạm, {app} đã có trên điện thoại', '{app} mỗi ngày của bạn, nay là ứng dụng', 'Cài {app} và vào ngay', 'Trải nghiệm {app} đầy đủ chỉ có trong ứng dụng', 'Chưa có ứng dụng? Cài {app} ngay'] },
71    th: { b: 'ติดตั้ง', s: 'APK ทางการพร้อมลายเซ็น', m: ['ติดตั้งแอป {app} สำหรับ Android', '{app} ครบทุกอย่างบน Android ของคุณ', 'พก {app} ไปทุกที่ — ติดตั้งแอปเลย', 'ในแอป {app} เร็วกว่า', 'ดีกว่าในแอป: ติดตั้ง {app}', 'แตะครั้งเดียว {app} ก็อยู่ในมือถือคุณ', '{app} คู่ใจทุกวัน ตอนนี้เป็นแอปแล้ว', 'ติดตั้ง {app} แล้วเข้าใช้ได้ทันที', 'ประสบการณ์ {app} เต็มรูปแบบ เฉพาะในแอป', 'ยังไม่มีแอป? ติดตั้ง {app} เลย'] },
72    ja: { b: 'インストール', s: '公式署名済みAPK', m: ['Android版{app}アプリをインストール', '{app}のすべてをあなたのAndroidに', '{app}をいつでも持ち歩こう — アプリをどうぞ', 'アプリなら{app}がもっと速い', 'アプリの方が快適:{app}をインストール', 'ワンタップで{app}がスマホに', '毎日の{app}を、アプリで', '{app}を入れてすぐスタート', '{app}のフル体験はアプリだけ', 'まだアプリ未導入?{app}をインストール'] },
73    ko: { b: '설치', s: '공식 서명 APK', m: ['Android용 {app} 앱을 설치하세요', '{app}의 모든 것을 Android에서', '{app}
73을 어디서나 — 앱을 설치하세요', '앱에서는 {app}이 더 빨라요', '앱이 더 좋아요: {app} 설치', '한 번의 터치로 {app}이 폰에', '매일 쓰는 {app}, 이제 앱으로', '{app} 설치하고 바로 시작하세요', '완전한 {app} 경험은 앱에서만', '아직 앱이 없나요? {app}을 설치하세요'] },
74    zh: { b: '安装', s: '官方签名 APK', m: ['安装 {app} 安卓应用', '完整的 {app},尽在你的安卓手机', '随身带着 {app} — 立即安装应用', '在应用里,{app} 更快', '应用体验更佳:安装 {app}', '一键安装,{app} 立刻到手机', '你每天用的 {app},现在有应用了', '安装 {app},马上开始', '完整的 {app} 体验,只在应用里', '还没有应用?安装 {app}'] }
75  };
76
77  function idioma() {
78    var pref = navigator.languages || [navigator.language || 'en'];
79    for (var i = 0; i < pref.length; i++) {
80      var c = String(pref[i] || '').toLowerCase().split('-')[0];
81      if (c === 'no') c = 'nb';
82      if (L[c]) return c;
83    }
84    return 'en';
85  }
86
87  var LANG = idioma();
88  var T = L[LANG];
89  var DIR = RTL[LANG] ? 'rtl' : 'ltr';
90  var MSG = T.m[Math.floor(Math.random() * T.m.length)].split('{app}').join(APP);
91
92  // Cabeza del androide (SVG oficial simplificado) para el icono del header.
93  var ANDROIDE = '<svg viewBox="0 0 24 24" width="16" height="16" fill="#fff" aria-hidden="true"><path d="M17.6 9.48l1.84-3.18c.16-.31.04-.69-.26-.85-.29-.15-.65-.06-.83.22l-1.88 3.24a11.46 11.46 0 0 0-8.94 0L5.65 5.67c-.19-.29-.58-.38-.87-.2-.28.18-.37.54-.22.83l1.84 3.18C3.4 11.15 1.5 14.06 1.5 17.4h21c0-3.34-1.9-6.25-4.9-7.92zM7.75 14.9a1.05 1.05 0 1 1 0-2.1 1.05 1.05 0 0 1 0 2.1zm8.5 0a1.05 1.05 0 1 1 0-2.1 1.05 1.05 0 0 1 0 2.1z"/></svg>';
94
95  function iconoHeader() {
96    if (document.getElementById('chAppIcono')) return;
97    var a = document.createElement('a');
98    a.id = 'chAppIcono';
99    a.href = APK;
100    a.rel = 'nofollow';
101    a.title = MSG;
102    a.setAttribute('aria-label', T.b + ' · ' + APP);
103    a.innerHTML = ANDROIDE;
104    a.style.cssText = 'display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;background:' + COLOR + ';box-shadow:0 2px 8px rgba(0,0,0,.3);z-index:99998;';
105    // data-fijo="1": ni buscar header — icono fijo (para barras superiores
106    // que son fixed pero no <header>, como la de Chateando).
107    if (cs.dataset.fijo === '1') {
108      a.style.position = 'fixed';
109      a.style.top = (ARRIBA === null ? 10 : ARRIBA) + 'px';
110      a.style[DIR === 'rtl' ? 'left' : 'right'] = DERECHA + 'px';
111      document.body.appendChild(a);
112      return;
113    }
114    var head = document.querySelector('header, .site-header, #masthead, .main-header, .l-header, .td-header-wrap');
115    if (head) {
116      var pos = getComputedStyle(head).position;
117      if (pos === 'static') head.style.position = 'relative';
118      a.style.position = 'absolute';
119      if (ARRIBA === null) {
120        a.style.top = '50%';
121        a.style.transform = 'translateY(-50%)';
122      } else {
123        a.style.top = ARRIBA + 'px';
124      }
125      a.style[DIR === 'rtl' ? 'left' : 'right'] = DERECHA + 'px';
126      head.appendChild(a);
127    } else {
128      a.style.position = 'fixed';
129      a.style.top = '10px';
130      a.style[DIR === 'rtl' ? 'left' : 'right'] = '10px';
131      document.body.appendChild(a);
132    }
133  }
134
135  function banner() {
136    if (document.getElementById('chBannerApp')) return;
137    if (Number(localStorage.getItem('chBannerAppOff') || 0) > Date.now()) return;
138    var b = document.createElement('div');
139    b.id = 'chBannerApp';
140    b.dir = DIR;
141    b.style.cssText = 'position:fixed;left:0;right:0;bottom:0;z-index:99999;background:#111;color:#fff;font-family:system-ui,sans-serif;box-shadow:0 -4px 18px rgba(0,0,0,.35);padding:12px 14px calc(12px + env(safe-area-inset-bottom));display:flex;align-items:center;gap:12px;';
142    b.innerHTML =
143      '<div style="flex:1;min-width:0">' +
144      '<div style="font-weight:700;font-size:.95rem">' + MSG + '</div>' +
145      '<div style="opacity:.75;font-size:.8rem">' + T.s + '</div>' +
146      '</div>' +
147      '<a href="' + APK + '" rel="nofollow" style="background:' + COLOR + ';color:#fff;text-decoration:none;font-weight:700;padding:10px 16px;border-radius:9px;white-space:nowrap;display:flex;align-items:center;gap:7px">' + ANDROIDE + T.b + '</a>' +
148      '<button id="chBannerAppX" aria-label="×" style="background:none;border:0;color:#fff;opacity:.6;font-size:1.3rem;line-height:1;padding:6px">&times;</button>';
149    document.body.appendChild(b);
150    function calla() {
151      b.remove();
152      // A la HORA vuelve: es un recordatorio persistente, no un adiós.
153      localStorage.setItem('chBannerAppOff', String(Date.now() + 36e5));
154    }
155    document.getElementById('chBannerAppX').addEventListener('click', calla);
156    b.querySelector('a').addEventListener('click', function () {
157      localStorage.setItem('chBannerAppOff', String(Date.now() + 36e5));
158    });
159  }
160
161  // data-solo-icono="1": se dibuja SOLO el icono del header, sin el banner
162  // inferior. Lo usan los sitios que ya enseñan la hoja «Continuar en…»
163  // (ch-app-continuar.js): dos avisos a la vez abajo se pisan, y el que se
164  // quedaría tapado es justo el que ofrece elegir. El icono del header no
165  // estorba a ninguno de los dos y es la puerta permanente.
166  var SOLO_ICONO = cs.dataset.soloIcono === '1';
167
168  function pinta() {
169    function dibuja() {
170      iconoHeader();
171      if (!SOLO_ICONO) banner();
172    }
173    if (document.readyState === 'loading') {
174      document.addEventListener('DOMContentLoaded', dibuja);
175    } else {
176      dibuja();
177    }
178  }
179
180  // Con la app instalada no se molesta; al desinstalarla, esto vuelve a dar
181  // false y todo reaparece solo. Si la API no existe (navegador viejo), se
182  // enseña igual: mejor un aviso de más que ninguno.
183  if (navigator.getInstalledRelatedApps) {
184    navigator.getInstalledRelatedApps().then(function (apps) {
185      var instalada = (apps || []).some(function (x) { return x && x.id === PAQUETE; });
186      if (!instalada) pinta();
187    }).catch(pinta);
188  } else {
189    pinta();
190  }
191})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.