BetterMangaBuff

Синхронный ZIP-движок. Умные закладки (точный фикс нумерации страниц). Скачивание прямо из списка глав. Смайлы в чате. Фикс шапки.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         BetterMangaBuff
// @namespace    https://mangabaf-dl
// @version      3
// @description  Синхронный ZIP-движок. Умные закладки (точный фикс нумерации страниц). Скачивание прямо из списка глав. Смайлы в чате. Фикс шапки.
// @author       countlynxz
// @match        *://mangabuff.ru/*
// @match        *://*.mangabuff.ru/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @connect      *
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const isChapterPage = () => location.pathname.includes('/manga/') && /\/\d+/.test(location.pathname);

    // =========================================================================
    // 1. СТИЛИ ИНТЕРФЕЙСА
    // =========================================================================
    GM_addStyle(`
        body.mb-reader-mode header.reader__header,
        body.mb-reader-mode .reader__header {
            background: transparent !important; background-color: transparent !important;
            backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
            border-bottom: none !important; box-shadow: none !important;
            display: flex !important; align-items: center !important; justify-content: space-between !important;
        }
        body.mb-reader-mode .reader__header > div {
            background: rgba(20, 20, 20, 0.75) !important;
            backdrop-filter: blur(10px) !important; -webkit-backdrop-filter: blur(10px) !important;
            border: 1px solid rgba(255, 255, 255, 0.05) !important; border-radius: 12px !important;
            padding: 6px 12px !important; transition: all 0.2s ease !important;
        }
        body.mb-reader-mode .reader__header [class*="dropdown-menu"],
        body.mb-reader-mode .reader__header .dropdown-menu {
            background: #141414 !important; background-color: #141414 !important;
            border: 1px solid #262626 !important; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6) !important;
            border-radius: 8px !important; padding: 8px 0 !important;
        }
        body.mb-reader-mode .reader__header [class*="dropdown-menu"] a,
        body.mb-reader-mode .reader__header .dropdown-menu a {
            color: #b3b3b3 !important; padding: 10px 16px !important; display: flex !important;
            align-items: center !important; gap: 10px !important; transition: background 0.15s ease, color 0.15s ease !important;
        }
        body.mb-reader-mode .reader__header [class*="dropdown-menu"] a:hover {
            background: #222 !important; color: #fff !important;
        }

        /* Чат */
        input[placeholder*="Отправить сообщение"], textarea[placeholder*="Отправить сообщение"],
        div:has(> input[placeholder*="Отправить сообщение"]) {
            border: none !important; outline: none !important; box-shadow: none !important;
        }
        .custom-emoji-btn { background: none; border: none; font-size: 20px; cursor: pointer; padding: 0 8px; transition: transform 0.1s; user-select: none; }
        .custom-emoji-btn:hover { transform: scale(1.15); }
        .custom-emoji-picker { position: absolute; bottom: 50px; left: 10px; background: #1e1e1e; border: 1px solid #333; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.5); padding: 10px; display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; z-index: 99999; max-height: 200px; overflow-y: auto; width: 220px; }
        .custom-emoji-item { font-size: 20px; cursor: pointer; text-align: center; padding: 4px; border-radius: 4px; transition: background 0.1s; user-select: none; }
        .custom-emoji-item:hover { background: #333; }

        /* Кнопки скачивания */
        #mb-download-btn { position: fixed; bottom: 20px; right: 20px; z-index: 99998; background: #ff5c5c; color: #fff; border: none; padding: 12px 20px; border-radius: 8px; font-weight: bold; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.2); transition: all 0.2s ease; }
        #mb-download-btn:hover { background: #e04e4e; transform: scale(1.03); }
        #mb-download-btn.loading { background: #666; cursor: wait; }

        .mb-list-dl-btn { background: rgba(255, 92, 92, 0.1); color: #ff5c5c; border: 1px solid rgba(255, 92, 92, 0.3); border-radius: 6px; padding: 4px 10px; cursor: pointer; font-size: 14px; margin-left: 15px; transition: all 0.2s ease; z-index: 10; display: inline-flex; align-items: center; justify-content: center; font-weight: bold; }
        .mb-list-dl-btn:hover { background: rgba(255, 92, 92, 0.8); color: #fff; transform: translateY(-1px); }
        .mb-list-dl-btn.loading { background: #666; color: #fff; border-color: #666; cursor: wait; transform: none; }

        /* Умные закладки */
        #mb-bookmark-toast { position: fixed; top: 80px; left: 50%; transform: translateX(-50%); z-index: 99999; background: rgba(20, 20, 20, 0.95); backdrop-filter: blur(8px); border: 1px solid #333; color: #fff; padding: 10px 20px; border-radius: 30px; font-size: 14px; box-shadow: 0 10px 25px rgba(0,0,0,0.6); display: flex; align-items: center; gap: 15px; animation: mbSlideDown 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
        #mb-bookmark-toast span.mb-bm-link { color: #5eff5e; font-weight: bold; cursor: pointer; text-decoration: underline; transition: color 0.2s; }
        #mb-bookmark-toast span.mb-bm-link:hover { color: #8cff8c; }
        #mb-bookmark-toast span.mb-bm-close { cursor: pointer; color: #888; font-size: 16px; margin-left: 5px; }
        #mb-bookmark-toast span.mb-bm-close:hover { color: #fff; }
        @keyframes mbSlideDown { from { top: -50px; opacity: 0; } to { top: 80px; opacity: 1; } }

        /* Уведомления */
        .mb-toast { position: fixed; bottom: 80px; right: 20px; z-index: 99999; background: #222; color: #fff; padding: 10px 15px; border-radius: 6px; font-size: 14px; box-shadow: 0 4px 10px rgba(0,0,0,0.3); animation: mbFadeIn 0.3s ease; }
        @keyframes mbFadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
        .mbd-ok { color: #5eff5e; font-weight: bold; }
        .mbd-err { color: #ff5e5e; font-weight: bold; }
    `);

    // =========================================================================
    // 2. БАЗОВЫЕ ФУНКЦИИ (Архиватор и Уведомления)
    // =========================================================================
    function showToast(html, text, duration = 3000) {
        const t = document.createElement('div');
        t.className = 'mb-toast'; t.innerHTML = html || text;
        document.body.appendChild(t);
        setTimeout(() => t.remove(), duration);
    }

    function makeZip(files) {
        const crcTable = [];
        for (let n = 0; n < 256; n++) { let c = n;
            for (let k = 0; k < 8; k++) c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
            crcTable[n] = c; }
        function getCRC32(data) { let crc = 0 ^ (-1);
            for (let i = 0; i < data.length; i++) crc = (crc >>> 8) ^ crcTable[(crc ^ data[i]) & 0xFF];
            return (crc ^ (-1)) >>> 0; }

        const parts = []; const centralDirectory = []; let currentOffset = 0;
        const now = new Date();
        const time = ((now.getHours() << 11) | (now.getMinutes() << 5) | (now.getSeconds() >> 1)) & 0xFFFF;
        const date = (((now.getFullYear() - 1980) << 9) | ((now.getMonth() + 1) << 5) | now.getDate()) & 0xFFFF;
        
        for (const file of files) {
            const nameBytes = new TextEncoder().encode(file.name);
            const dataBytes = file.data; const crc = getCRC32(dataBytes); const size = dataBytes.length;
            const lfh = new Uint8Array(30 + nameBytes.length); const view = new DataView(lfh.buffer);
            view.setUint32(0, 0x04034b50, true); view.setUint16(4, 10, true); view.setUint16(10, time, true); view.setUint16(12, date, true);
            view.setUint32(14, crc, true); view.setUint32(18, size, true); view.setUint32(22, size, true); view.setUint16(26, nameBytes.length, true);
            lfh.set(nameBytes, 30); parts.push(lfh); parts.push(dataBytes);

            const cdh = new Uint8Array(46 + nameBytes.length); const cdView = new DataView(cdh.buffer);
            cdView.setUint32(0, 0x02014b50, true); cdView.setUint16(4, 20, true); cdView.setUint16(6, 10, true); cdView.setUint16(12, time, true);
            cdView.setUint16(14, date, true); cdView.setUint32(16, crc, true); cdView.setUint32(20, size, true); cdView.setUint32(24, size, true);
            cdView.setUint16(28, nameBytes.length, true); cdView.setUint32(42, currentOffset, true); cdh.set(nameBytes, 46);
            centralDirectory.push(cdh); currentOffset += lfh.length + dataBytes.length;
        }

        const cdBlob = new Blob(centralDirectory); const eocd = new Uint8Array(22); const eocdView = new DataView(eocd.buffer);
        eocdView.setUint32(0, 0x06054b50, true); eocdView.setUint16(8, files.length, true); eocdView.setUint16(10, files.length, true);
        eocdView.setUint32(12, cdBlob.size, true); eocdView.setUint32(16, currentOffset, true);
        parts.push(cdBlob); parts.push(eocd); return new Blob(parts, { type: 'application/zip' });
    }

    // =========================================================================
    // 3. УМНЫЕ ЗАКЛАДКИ (Исправленный точный подсчет)
    // =========================================================================
    let scrollTimeout;
    window.addEventListener('scroll', () => {
        if (!isChapterPage()) return;
        
        clearTimeout(scrollTimeout);
        scrollTimeout = setTimeout(() => {
            const images = document.querySelectorAll('.reader-images img, .page-image img, [class*="chapter"] img, .reader img');
            if (!images.length) return;

            let currentIndex = 0;
            // Ищем картинку, которая сейчас пересекает центр экрана
            for (let i = 0; i < images.length; i++) {
                const rect = images[i].getBoundingClientRect();
                if (rect.top <= window.innerHeight / 2 && rect.bottom >= window.innerHeight / 2) {
                    currentIndex = i;
                    break;
                }
            }
            
            // Сохраняем индекс картинки, если ушли дальше первой
            if (currentIndex > 0) {
                localStorage.setItem('mb_bm_' + location.pathname, currentIndex);
            }
        }, 500);
    });

    function checkSmartBookmark() {
        if (!isChapterPage()) return;

        const savedIndexStr = localStorage.getItem('mb_bm_' + location.pathname);
        if (!savedIndexStr) return;
        
        const savedIndex = parseInt(savedIndexStr, 10);
        const oldToast = document.getElementById('mb-bookmark-toast');
        if (oldToast) oldToast.remove();

        if (savedIndex && savedIndex > 0) {
            const toast = document.createElement('div');
            toast.id = 'mb-bookmark-toast';
            // Отображаем ровно сохраненное число без накруток, теперь оно совпадает со страницей
            toast.innerHTML = `
                📍 Вы остановились на ${savedIndex} стр.
                <span class="mb-bm-link">Вернуться</span>
                <span class="mb-bm-close">✖</span>
            `;

            toast.querySelector('.mb-bm-link').addEventListener('click', () => {
                const images = document.querySelectorAll('.reader-images img, .page-image img, [class*="chapter"] img, .reader img');
                if (images[savedIndex]) {
                    images[savedIndex].scrollIntoView({ behavior: 'smooth', block: 'start' });
                    toast.remove();
                } else {
                    showToast(null, '❌ Страница еще не подгрузилась, листайте чуть медленнее...', 3000);
                }
            });

            toast.querySelector('.mb-bm-close').addEventListener('click', () => toast.remove());
            document.body.appendChild(toast);
            
            setTimeout(() => { if (toast.parentNode) toast.remove(); }, 10000);
        }
    }

    // =========================================================================
    // 4. СКАЧИВАНИЕ (В читалке и из списка)
    // =========================================================================
    async function downloadChapterBackground(url, chapterNameFallback, btnElement) {
        showToast(null, `⏳ Запрос главы...`, 2000);
        GM_xmlhttpRequest({
            method: 'GET', url: url,
            onload: async function(res) {
                if (res.status === 200) {
                    const doc = new DOMParser().parseFromString(res.responseText, "text/html");
                    const urls = [];
                    doc.querySelectorAll('.reader-images img, .page-image img, [class*="chapter"] img, .reader img').forEach(img => {
                        const src = img.getAttribute('data-src') || img.src;
                        if (src && !urls.includes(src) && !src.includes('avatar') && !src.includes('logo')) urls.push(src);
                    });

                    if (!urls.length) {
                        showToast(null, '❌ Страницы в этой главе не найдены!', 3000);
                        btnElement.classList.remove('loading'); btnElement.innerHTML = '📥'; return;
                    }

                    let downloadedFiles = [], done = 0, errors = 0; btnElement.innerHTML = `0/${urls.length}`;
                    await Promise.all(urls.map((imgUrl, index) => new Promise(resolve => {
                        GM_xmlhttpRequest({
                            method: 'GET', url: imgUrl, responseType: 'arraybuffer', headers: { Referer: location.origin },
                            onload: function (imgRes) {
                                if (imgRes.status === 200 && imgRes.response) {
                                    try {
                                        const ext = imgUrl.split('.').pop().split('?')[0] || 'jpg';
                                        downloadedFiles.push({ name: String(index + 1).padStart(3, '0') + '.' + ext, data: new Uint8Array(imgRes.response), index: index });
                                        done++;
                                    } catch(e) { errors++; }
                                } else errors++;
                                btnElement.innerHTML = `${done + errors}/${urls.length}`; resolve();
                            },
                            onerror: () => { errors++; btnElement.innerHTML = `${done + errors}/${urls.length}`; resolve(); },
                            ontimeout: () => { errors++; btnElement.innerHTML = `${done + errors}/${urls.length}`; resolve(); }
                        });
                    })));

                    if (done === 0) { 
                        showToast(null, '❌ Ошибка загрузки страниц.', 3000);
                        btnElement.classList.remove('loading'); btnElement.innerHTML = '📥'; return; 
                    }
                    
                    btnElement.innerHTML = '📦';
                    let safeName = chapterNameFallback.replace(/[^\w\sа-яА-ЯёЁ]/g, '').trim().slice(0, 40) || 'chapter';
                    let mangaTitle = document.title.split('-')[0].trim().replace(/[^\w\sа-яА-ЯёЁ]/g, '');
                    setTimeout(() => {
                        try {
                            downloadedFiles.sort((a, b) => a.index - b.index);
                            const a = document.createElement('a');
                            a.href = URL.createObjectURL(makeZip(downloadedFiles));
                            a.download = `${mangaTitle}_${safeName}`.replace(/\s+/g, '_') + '.zip';
                            a.click(); URL.revokeObjectURL(a.href);
                            showToast(`<span class="mbd-ok">✅ ${safeName} скачана!</span>`, 4000);
                        } catch (err) { showToast(null, '❌ Ошибка архива.', 4000); } 
                        finally { btnElement.classList.remove('loading'); btnElement.innerHTML = '📥'; }
                    }, 50);
                } else {
                    showToast(null, '❌ Ошибка доступа.', 3000);
                    btnElement.classList.remove('loading'); btnElement.innerHTML = '📥';
                }
            }
        });
    }

    function injectListDownloadButtons() {
        if (!location.pathname.includes('/manga/') || isChapterPage()) return;
        document.querySelectorAll('a[href*="/manga/"]:not(.mb-dl-injected)').forEach(link => {
            const href = link.getAttribute('href'); if (!href) return;
            const text = link.innerText.toLowerCase();
            
            if (/\/\d+(?:\/\d+)?\/?$/.test(href) && (text.includes('том') || text.includes('глава') || text.includes('прочитано'))) {
                const rightPart = Array.from(link.children).find(child => /\d{2}\.\d{2}\.\d{4}/.test(child.innerText) || child.innerText.includes('K') || child.innerText.toLowerCase().includes('прочитано'));
                if (!rightPart) return;
                
                link.classList.add('mb-dl-injected');
                const dlBtn = document.createElement('button');
                dlBtn.innerHTML = '📥'; dlBtn.className = 'mb-list-dl-btn'; dlBtn.title = 'Скачать главу';
                
                dlBtn.addEventListener('click', (e) => {
                    e.preventDefault(); e.stopPropagation();
                    if (dlBtn.classList.contains('loading')) return;
                    dlBtn.classList.add('loading'); dlBtn.innerHTML = '⏳';
                    
                    const chapNameMatch = link.innerText.match(/Том\s*\d+\s*Глава\s*\d+(?:\.\d+)?/i) || link.innerText.match(/Глава\s*\d+(?:\.\d+)?/i);
                    let chapName = 'Chapter';
                    if (chapNameMatch) { chapName = chapNameMatch[0]; } 
                    else {
                        const parts = href.split('/').filter(Boolean);
                        chapName = !isNaN(parts[parts.length - 2]) ? `Том ${parts[parts.length - 2]} Глава ${parts[parts.length - 1]}` : `Глава ${parts[parts.length - 1]}`;
                    }
                    downloadChapterBackground(link.href, chapName, dlBtn);
                });
                rightPart.appendChild(dlBtn);
            }
        });
    }

    // =========================================================================
    // 5. ОСТАЛЬНОЙ ИНТЕРФЕЙС
    // =========================================================================
    function injectPromoLink() {
        const subMenu = document.querySelector('.header__link-sub');
        if (subMenu && !subMenu.querySelector('a[href*="promo-code"]')) {
            const a = document.createElement('a'); a.href = 'https://mangabuff.ru/promo-code'; a.className = 'mb-promo-link'; a.innerText = 'Промокоды';
            const sLink = subMenu.querySelector('a'); if (sLink) a.className += ' ' + sLink.className;
            subMenu.appendChild(a);
        }
    }

    const emojis = ['😀','😂','🤣','😊','😍','😘','😜','🤫','🤔','😎','🙄','🤡','💩','🔥','✨','💯','👍','👎','❤️','💔','😭','😡','😱','💀','👽','🤖','👑','👀','💬','🐾','🐈','🦊','🍕','🎮','🎲'];
    function initEmojiPicker() {
        const input = document.querySelector('input[placeholder*="Отправить сообщение"], textarea[placeholder*="Отправить сообщение"]');
        if (!input || input.dataset.emojiInit) return; input.dataset.emojiInit = "true";
        const dBtn = input.parentElement.querySelector('button, div[class*="theme"]');
        const eBtn = document.createElement('button'); eBtn.type = 'button'; eBtn.className = 'custom-emoji-btn'; eBtn.innerText = '😀';
        const picker = document.createElement('div'); picker.className = 'custom-emoji-picker'; picker.style.display = 'none';
        input.parentElement.style.position = 'relative';

        emojis.forEach(e => {
            const s = document.createElement('span'); s.className = 'custom-emoji-item'; s.innerText = e;
            s.addEventListener('click', ev => {
                ev.stopPropagation(); const start = input.selectionStart, end = input.selectionEnd, val = input.value;
                input.value = val.substring(0, start) + e + val.substring(end); input.focus();
                input.selectionStart = input.selectionEnd = start + e.length; input.dispatchEvent(new Event('input', { bubbles: true }));
            });
            picker.appendChild(s);
        });
        if (dBtn) dBtn.after(eBtn); else input.before(eBtn);
        input.parentElement.appendChild(picker);
        eBtn.addEventListener('click', ev => { ev.stopPropagation(); picker.style.display = picker.style.display === 'none' ? 'grid' : 'none'; });
        document.addEventListener('click', () => { picker.style.display = 'none'; });
    }

    function manageReaderUI() {
        if (isChapterPage()) {
            document.body.classList.add('mb-reader-mode');
            
            setTimeout(checkSmartBookmark, 1000); 

            if (!document.getElementById('mb-download-btn')) {
                const btn = document.createElement('button'); btn.id = 'mb-download-btn'; btn.innerHTML = '⬇ Скачать главу'; document.body.appendChild(btn);
                btn.addEventListener('click', async () => {
                    if (btn.classList.contains('loading')) return;
                    const urls = []; 
                    document.querySelectorAll('.reader-images img, .page-image img, [class*="chapter"] img, .reader img').forEach(img => { 
                        const src = img.getAttribute('data-src') || img.src; if (src && !urls.includes(src) && !src.includes('avatar')) urls.push(src); 
                    });
                    if (!urls.length) { showToast(null, '❌ Картинки не найдены!', 4000); return; }

                    btn.classList.add('loading'); let files = [], done = 0, errs = 0; btn.innerHTML = `⏳ 0/${urls.length}`;
                    await Promise.all(urls.map((url, i) => new Promise(res => {
                        GM_xmlhttpRequest({
                            method: 'GET', url: url, responseType: 'arraybuffer',
                            onload: r => { if (r.status===200 && r.response) { try { files.push({ name: String(i+1).padStart(3, '0')+'.'+(url.split('.').pop().split('?')[0]||'jpg'), data: new Uint8Array(r.response), index: i }); done++; } catch(e){errs++;} } else errs++; btn.innerHTML=`⏳ ${done+errs}/${urls.length}`; res(); },
                            onerror: () => { errs++; btn.innerHTML=`⏳ ${done+errs}/${urls.length}`; res(); }
                        });
                    })));

                    if (done === 0) { btn.classList.remove('loading'); btn.innerHTML = '⬇ Скачать главу'; return; }
                    btn.innerHTML = '📦 Упаковываю...';
                    const m = location.pathname.match(/chapter[-_]?(\d+(?:[._]\d+)?)/i) || location.pathname.match(/\/(\d+(?:[._]\d+)?)\/?$/);
                    const chap = m ? 'ch' + m[1] : 'chapter'; const title = document.title.replace(/[^\w\sа-яА-ЯёЁ]/g, '').slice(0, 40);
                    setTimeout(() => {
                        try { files.sort((a, b) => a.index - b.index); const a = document.createElement('a'); a.href = URL.createObjectURL(makeZip(files)); a.download = title+'_'+chap+'.zip'; a.click(); showToast(`<span class="mbd-ok">✅ Скачано: ${done} стр.</span>`, 5000); } 
                        catch (e) { showToast(null, '❌ Ошибка архива.'); } finally { btn.classList.remove('loading'); btn.innerHTML = '⬇ Скачать главу'; }
                    }, 50);
                });
            }
        } else {
            document.body.classList.remove('mb-reader-mode');
            const btn = document.getElementById('mb-download-btn'); if (btn) btn.remove();
        }
    }

    // =========================================================================
    // НАБЛЮДАТЕЛИ (SPA Навигация и Хоткеи)
    // =========================================================================
    document.addEventListener('keydown', e => {
        if (!isChapterPage() || ['INPUT', 'TEXTAREA', 'SELECT'].includes(e.target.tagName)) return;
        let dir = null; if (['ArrowLeft', 'a', 'A', 'ф', 'Ф'].includes(e.key)) dir = 'prev'; if (['ArrowRight', 'd', 'D', 'в', 'В'].includes(e.key)) dir = 'next';
        if (!dir) return;
        for (let el of document.querySelectorAll('a, button')) {
            const text = el.innerText?.toLowerCase() || ''; const href = el.getAttribute('href') || '';
            if (dir === 'next' && (text.includes('след') || href.includes('next') || el.classList.contains('next'))) { el.click(); break; }
            if (dir === 'prev' && (text.includes('пред') || text.includes('прош') || href.includes('prev') || el.classList.contains('prev'))) { el.click(); break; }
        }
    });

    let lastUrl = location.href;
    const globalObserver = new MutationObserver(() => {
        injectPromoLink(); initEmojiPicker(); injectListDownloadButtons(); 
        if (location.href !== lastUrl) { lastUrl = location.href; manageReaderUI(); }
    });
    
    globalObserver.observe(document.body, { childList: true, subtree: true });
    manageReaderUI(); setTimeout(initEmojiPicker, 1000);

})();