<repo_name>dulhaniAshish/GIFted<file_sep>/script.js
const input = document.querySelector('input.search');
const container = document.querySelector('#container');
const loader = document.querySelector('.loading');
const modal = document.getElementById('myModal');
const img = document.getElementById('myImg');
const modalImg = document.getElementById("img01");
const captionText = document.getElementById("caption");
const span = document.getElementsByClassName("close")[0];


const SearchState = new State('searchText');
const Pagination = new State('pagination', 'int');
const FetchState = new State('fetching', 'boolean');
const LimitReachedState = new State('maxLimitForAPIReached', 'boolean');

const DEBOUNCE_TIME = config.UTILS.DEBOUNCE_TIME; // ms

if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
        navigator.serviceWorker.register('./sw.js')
            .then(registration => {
                console.log(`Service Worker registered! Scope: ${registration.scope}`);
            })
            .catch(err => {
                console.log(`Service Worker registration failed: ${err}`);
            });
    });
}

function showLoader(show) {
    FetchState.changeState(show);
    loader.style.display = show ? 'block' : 'none';
}

function getDistFromBottom() {

    const scrollPosition = window.pageYOffset;
    const windowSize = window.innerHeight;
    return Math.max(container.offsetHeight - (scrollPosition + windowSize), 0);

}

document.addEventListener('scroll', function () {
    const distToBottom = getDistFromBottom();
    if (!LimitReachedState.getCurrentState().current && !FetchState.getCurrentState().current && distToBottom >= 0 && distToBottom <= (window.innerHeight * (3 / 4))) {
        searchForGIFs(SearchState.getCurrentState().current, Pagination.getCurrentState().current);
    }
});

window.onload = function () {
    searchForGIFs(SearchState.getCurrentState().current);
};

input.oninput = debounce(handleSearchQueryChange, DEBOUNCE_TIME);

span.onclick = function () {
    modal.style.display = "none";
};

function handleSearchQueryChange(event) {
    let value = event.target.value.trim();
    if (value !== SearchState.getCurrentState().current) {

        if (value.length === 0) {
            value = "";
        }
        container.innerHTML = "";
        SearchState.changeState(value);
        Pagination.changeState(0, 'int');
        LimitReachedState.changeState(false, 'boolean');
        searchForGIFs(value);
    }
}

function addPaginationOffset(count) {
    Pagination.changeState(Pagination.getCurrentState().current + count);
}

function showModal() {
    modal.style.display = "block";
    modalImg.src = this.src;
    captionText.innerHTML = this.title;
}


function searchForGIFs(text, offset = 0) {
    showLoader(true);
    const fn = !!text ? APIRequest.SEARCH : APIRequest.TRENDING;
    fn(text, offset)
        .then((response) => {
            const {data, pagination} = response;

            const _modified = data.map((el) => {
                const {title, id} = el;
                const hasPreview = el['images'].hasOwnProperty('preview_gif');
                const selector = hasPreview ? 'preview_gif' : 'fixed_height_still';
                const stageOne = el['images'][`${isMobile() ? selector : 'original'}`]; // for quickly loading
                const finalStage = el['images'][`${isMobile() ? 'fixed_height' : 'original'}`];
                const bestMatch = findAppropriateSize(el['images']);

                return {
                    title,
                    id,
                    low: stageOne,
                    high: finalStage,
                }
            });

            return Promise.resolve({
                data: _modified,
                pagination,
            });
        })
        .then((response) => {
            const {data, pagination} = response;

            addPaginationOffset(Number.parseInt(pagination['count']));

            const totalCount = pagination['total_count'];

            if (Pagination.getCurrentState().current === Number.parseInt(totalCount)) {
                LimitReachedState.changeState(true);
            }
            const fragment = document.createDocumentFragment();
            console.time('add els');
            data.forEach((el) => {
                fragment.appendChild(getImageDomElement(el));
            });
            container.appendChild(fragment);
            console.timeEnd('add els');
            showLoader(false);
        })
        .catch((err) => {
            console.warn('Error:', err);
            showLoader(false);
            LimitReachedState.changeState(true);
        })
}

function getImageDomElement(el) {
    const divContainer = document.createElement('div');
    const domImage = document.createElement('img');

    domImage.classList.add('gif_item');
    domImage.classList.add('shine');

    const {
        title,
        id,
        low,
        high
    } = el;

    domImage.setAttribute('title', title);
    domImage.setAttribute('src', low['url']);
    domImage.setAttribute('id', id);
    // domImage.style.display = 'flex';

    const addActiveUrl = () => domImage.setAttribute('src', high['url']);

    const addStillUrl = () => domImage.setAttribute('src', low['url']);

    const expectedWidth = `${high['width']}px`;
    domImage.addEventListener('mouseover', addActiveUrl);
    domImage.addEventListener('mouseout', addStillUrl);

    domImage.addEventListener('load', () => {
        const highResDownloaded = (domImage.getAttribute('src') === high['url']);
        !highResDownloaded ?
            domImage.classList.remove('s') :    // remove shimmer
            domImage.removeEventListener('mouseout', addStillUrl);  // best gif downloaded
                        // no need to revert to preview url on mouse out
        if (highResDownloaded) {
            domImage.onclick = showModal;
        }
    });
    domImage.style.height = '200px';
    const idealWidth = 200 * config.UTILS.IDEAL_ASPECT_RATIO;
    const toleranceWidthMin = (config.UTILS.IDEAL_ASPECT_RATIO - config.UTILS.TOLERANCE) * 200;
    const toleranceWidthMax = (config.UTILS.IDEAL_ASPECT_RATIO + config.UTILS.TOLERANCE) * 200;
    const currentWidth = Number.parseInt(high['width']);

    if (currentWidth <= toleranceWidthMax && currentWidth >= toleranceWidthMin) {
        domImage.style.width = expectedWidth;
    } else {
        domImage.style.width = `${idealWidth}px`;
    }
    divContainer.appendChild(domImage);
    return divContainer;
}



<file_sep>/network.js
const APIRequest = (function gen() {
    const API_KEY = config.GIPHY.API_KEY;

    const HOST = config.GIPHY.HOST_URL;
    const BASE_URL = `${HOST}`;

    const GET = (endpoint) => {
        const API_PATH = endpoint;
        return function (string, offset = 0) {
            const finalUrl = `${BASE_URL}${API_PATH}?q=${string}&api_key=${API_KEY}&offset=${offset}`;
            return fetch(finalUrl)
                .then((blob) => {
                    if (blob['status'] === 200)
                        return blob.json();
                    else throw new Error('Some error in fetching');
                })
                .then((res) => {
                    if (res['meta']['status'] === 200) {
                        return res;
                    }
                })
        }
    };

    return {
        SEARCH: GET(config.GIPHY.SEARCH_ENDPOINT),
        TRENDING: GET(config.GIPHY.TRENDING_ENDPOINT),
    }
}());<file_sep>/README.md
# GIFted
Fetch GIFs from giphy
<file_sep>/config.js
const config = {
    GIPHY: {
        API_KEY: '<KEY>',
        HOST_URL: 'http://api.giphy.com',
        SEARCH_ENDPOINT: '/v1/gifs/search',
        TRENDING_ENDPOINT: '/v1/gifs/trending',
    },

    UTILS: {
        MAX_GIF_SIZE: 500 * 1000,
        DEBOUNCE_TIME: 500,
        IDEAL_ASPECT_RATIO: 1.33,
        TOLERANCE: 0.75,
    }
};