import cache from '@fly/cache';
import { request, GraphQLClient } from 'graphql-request';
import polarity from 'polarity-webpack';

fly.http.respondWith( async function( req ) {

    let data, status, contentType;
    const url = new URL(req.url);

    if( req.method === 'POST' ) {
        ({ data, status, contentType } = await getGitHubData( req, url ));
    } else {
        ({ data, status, contentType } = await serveGUI( url ));
    }

    return new Response( data, { status: status, headers: { 'content-type': contentType } } );
});

/****
 * send html, css, and js
 * for gui
 */

async function serveGUI( url ) {

    let res, fileType;
    const headerContentTypes = {
        'html': 'text/html',
        'js': 'application/javascript',
        'css': 'text/css'
    }

    const pathname = ( url.pathname === '/' ) ? '/index.html' : url.pathname;
    res = await fetch( 'file://build' + pathname );

    fileType = pathname.split('.').pop();
    if( res.status === 200 && Object.keys( headerContentTypes ).includes( fileType ) ) {
        let fileText = await res.text();
        return { data: fileText, status: 200, contentType: headerContentTypes[ fileType ] };
    }

    return { data: "
Not found.
", status: 404, contentType: "text/html" };
}

/****
 * serve api data; we only validate fields if query
 * is not coming from the gui
 */

async function getGitHubData( req, url ) {

    let cacheKey = "", queryString = "";
    let reqString = await req.text();

    const client = new GraphQLClient( 'https://api.github.com/graphql', {
        headers: {
            'User-Agent': req.headers.get('User-Agent') || app.config.userAgent,
            'Authorization': req.headers.get('Authorization') || 'Bearer ' + app.config.userToken
        }
    });

    //only requests from gui are json; non-gui requests use graphql syntax
    if( url.pathname === '/standard' ) {
        let reqStringJSON = await JSON.parse( reqString );
        cacheKey = reqStringJSON.org + "-" + reqStringJSON.repo;
        queryString = reqStringJSON.query;
    } else {
        queryString = addRequiredQueryFields( reqString );
    }

    const { dataString, status } = await getIssues( queryString, client, cacheKey );

    return { data: dataString, status: status, contentType: "application/json" };
}

/****
 * add missing query fields (if any) for custom queries;
 * we need id, number, title, and bodyText to deliver sentiment
 */

function addRequiredQueryFields( query ) {

    let stringCondensed = query.replace(/\s+/g, " ");
    let starti = stringCondensed.indexOf('node {') + 6;
    let endi = stringCondensed.indexOf('}', starti);
    let currentParams = ( stringCondensed.substring(starti, endi) ).split(" ");
    const requiredParams = [ "id", "number", "title", "bodyText" ];
    requiredParams.forEach( function( p ) {
        if( !currentParams.includes( p ) ) { currentParams.push(p) }
    });
    currentParams = currentParams.join(" ");

    return stringCondensed.substr(0, starti) + currentParams + stringCondensed.substr(endi);
}

/****
 * send user's request to github OR
 * retrieve from cache if possible
 */

async function getIssues( query, client, cacheKey ) {

    let data, dataString, dataJson, status, cachedResponse = null;

    if( cacheKey ) { cachedResponse = await cache.getString( cacheKey ); }
    if( cachedResponse !== null ) {
        status = 200;
        dataString = cachedResponse;
        return { dataString, status };
    } else {
        //get issue details
        try {

            data = await client.request( query );   //query github

            dataString = await JSON.stringify( data );
            dataJson = await JSON.parse( dataString );

            dataJson = getSentiment( dataJson );

            status = 200;

        } catch( err ) {

            //remove github status before returning because it seems to return 200 no matter what
            dataString = await JSON.stringify( err );
            dataJson = await JSON.parse( dataString );
            delete dataJson.response.status;

            status = 500;
        }

        dataString = await JSON.stringify( dataJson );

        if( cacheKey && ( status !== 500 ) ) {
            cache.set( cacheKey, dataString, 172800 );   //cache for 2 days (only non-custom requests)
        }

        return { dataString, status };
    }
}

/****
 * analyze issue(s) and 'stitch' sentiment into github response;
 * return updated response
 */

function getSentiment( response ) {

    const issuesToEvaluate = response.repository.issues.edges;

    for( let i = issuesToEvaluate.length - 1; i >= 0; i-- ) {
        let sent = polarity( ( issuesToEvaluate[i].node.title + " " + issuesToEvaluate[i].node.bodyText ).split(" ") );
        issuesToEvaluate[i].node.sentiment = sent;
    };

    return response;
}


"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
 * A fetch function load balancer. Distributes requests to a set of backends; attempts to
 * send requests to most recently healthy backends using a 2 random (pick two healthiest,
 * randomize which gets requests).
 *
 * If all backends are healthy, tries to evenly distribute requests as much as possible.
 *
 * When backends return server errors (500-599) it retries idempotent requests
 *  until it gets a good response, or all backends have been tried.
 *
 * @param backends fetch functions for each backend to balance accross
 * @returns a function that behaves just like fetch, with a `.backends` property for
 * retrieving backend stats.
 */
function balancer(backends) {
    const tracked = backends.map((h) => {
        if (typeof h !== "function") {
            throw Error("Backend must be a fetch like function");
        }
        return {
            proxy: h,
            requestCount: 0,
            scoredRequestCount: 0,
            statuses: Array(10),
            lastError: 0,
            healthScore: 1,
            errorCount: 0
        };
    });
    const fn = async function fetchBalancer(req, init) {
        if (typeof req === "string") {
            req = new Request(req);
        }
        const attempted = new Set();
        while (attempted.size < tracked.length) {
            let backend = null;
            const [backendA, backendB] = chooseBackends(tracked, attempted);
            if (!backendA) {
                return new Response("No backend available", { status: 502 });
            }
            if (!backendB) {
                backend = backendA;
            }
            else {
                // randomize between 2 good candidates
                backend = (Math.floor(Math.random() * 2) == 0) ? backendA : backendB;
            }
            const promise = backend.proxy(req, init);
            if (backend.scoredRequestCount != backend.requestCount) {
                // fixup score
                // this should be relatively concurrent with the fetch promise
                score(backend);
            }
            backend.requestCount += 1;
            attempted.add(backend);
            let resp;
            try {
                resp = await promise;
            }
            catch (e) {
                resp = proxyError;
            }
            if (backend.statuses.length < 10) {
                backend.statuses.push(resp.status);
            }
            else {
                backend.statuses[(backend.requestCount - 1) % backend.statuses.length] = resp.status;
            }
            if (resp.status >= 500 && resp.status < 600) {
                backend.lastError = Date.now();
                // always recompute score on errors
                score(backend);
                // clear out response to trigger retry
                if (canRetry(req, resp)) {
                    continue;
                }
            }
            return resp;
        }
        return proxyError;
    };
    return Object.assign(fn, { backends: tracked });
}
exports.default = balancer;
const proxyError = new Response("couldn't connect to origin", { status: 502 });
// compute a backend health score with time + status codes
function score(backend, errorBasis) {
    if (typeof errorBasis !== "number" && !errorBasis)
        errorBasis = Date.now();
    const timeSinceError = (errorBasis - backend.lastError);
    const statuses = backend.statuses;
    const timeWeight = (backend.lastError === 0 && 0) ||
        ((timeSinceError < 1000) && 1) ||
        ((timeSinceError < 3000) && 0.8) ||
        ((timeSinceError < 5000) && 0.3) ||
        ((timeSinceError < 10000) && 0.1) ||
        0;
    if (statuses.length == 0)
        return 0;
    let requests = 0;
    let errors = 0;
    for (let i = 0; i < statuses.length; i++) {
        const status = statuses[i];
        if (status && !isNaN(status)) {
            requests += 1;
            if (status >= 500 && status < 600) {
                errors += 1;
            }
        }
    }
    const score = (1 - (timeWeight * (errors / requests)));
    backend.healthScore = score;
    backend.scoredRequestCount = backend.requestCount;
    return score;
}
function canRetry(req, resp) {
    if (resp && resp.status < 500)
        return false; // don't retry normal boring errors or success
    if (req.method == "GET" || req.method == "HEAD")
        return true;
    return false;
}
function chooseBackends(backends, attempted) {
    let b1;
    let b2;
    for (let i = 0; i < backends.length; i++) {
        const b = backends[i];
        if (attempted && attempted.has(b))
            continue;
        if (!b1) {
            b1 = b;
            continue;
        }
        if (!b2) {
            b2 = b;
            continue;
        }
        const old1 = b1;
        b1 = bestBackend(b, b1);
        if (old1 != b1) {
            // b1 got replaced, make sure it's not better
            b2 = bestBackend(old1, b2);
        }
        else {
            b2 = bestBackend(b, b2);
        }
    }
    return [b1, b2];
}
function bestBackend(b1, b2) {
    if (b1.healthScore > b2.healthScore ||
        (b1.healthScore == b2.healthScore && b1.requestCount < b2.requestCount)) {
        return b1;
    }
    return b2;
}
exports._internal = {
    chooseBackends,
    score
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFsYW5jZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvYmFsYW5jZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQTs7Ozs7Ozs7Ozs7OztHQWFHO0FBQ0gsa0JBQWlDLFFBQW1CO0lBQ2xELE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtRQUNqQyxJQUFJLE9BQU8sQ0FBQyxLQUFLLFVBQVUsRUFBRTtZQUMzQixNQUFNLEtBQUssQ0FBQyx1Q0FBdUMsQ0FBQyxDQUFBO1NBQ3JEO1FBQ0QsT0FBZ0I7WUFDZCxLQUFLLEVBQUUsQ0FBQztZQUNSLFlBQVksRUFBRSxDQUFDO1lBQ2Ysa0JBQWtCLEVBQUUsQ0FBQztZQUNyQixRQUFRLEVBQUUsS0FBSyxDQUFTLEVBQUUsQ0FBQztZQUMzQixTQUFTLEVBQUUsQ0FBQztZQUNaLFdBQVcsRUFBRSxDQUFDO1lBQ2QsVUFBVSxFQUFFLENBQUM7U0FDZCxDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7SUFFRixNQUFNLEVBQUUsR0FBRyxLQUFLLHdCQUF3QixHQUFnQixFQUFFLElBQThCO1FBQ3RGLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFFO1lBQzNCLEdBQUcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQTtTQUN2QjtRQUNELE1BQU0sU0FBUyxHQUFHLElBQUksR0FBRyxFQUFXLENBQUE7UUFDcEMsT0FBTyxTQUFTLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxNQUFNLEVBQUU7WUFDdEMsSUFBSSxPQUFPLEdBQW1CLElBQUksQ0FBQTtZQUNsQyxNQUFNLENBQUMsUUFBUSxFQUFFLFFBQVEsQ0FBQyxHQUFHLGNBQWMsQ0FBQyxPQUFPLEVBQUUsU0FBUyxDQUFDLENBQUE7WUFFL0QsSUFBSSxDQUFDLFFBQVEsRUFBRTtnQkFDYixPQUFPLElBQUksUUFBUSxDQUFDLHNCQUFzQixFQUFFLEVBQUUsTUFBTSxF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QUFBO0tBQ1Y7SUFDRCxPQUFPLEVBQUUsQ0FBQTtBQUNYLENBQUM7QUFFWSxRQUFBLFNBQVMsR0FBRztJQUN2QixjQUFjO0lBQ2QsS0FBSztDQUNOLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEEgZmV0Y2ggZnVuY3Rpb24gbG9hZCBiYWxhbmNlci4gRGlzdHJpYnV0ZXMgcmVxdWVzdHMgdG8gYSBzZXQgb2YgYmFja2VuZHM7IGF0dGVtcHRzIHRvIFxuICogc2VuZCByZXF1ZXN0cyB0byBtb3N0IHJlY2VudGx5IGhlYWx0aHkgYmFja2VuZHMgdXNpbmcgYSAyIHJhbmRvbSAocGljayB0d28gaGVhbHRoaWVzdCwgXG4gKiByYW5kb21pemUgd2hpY2ggZ2V0cyByZXF1ZXN0cykuXG4gKiBcbiAqIElmIGFsbCBiYWNrZW5kcyBhcmUgaGVhbHRoeSwgdHJpZXMgdG8gZXZlbmx5IGRpc3RyaWJ1dGUgcmVxdWVzdHMgYXMgbXVjaCBhcyBwb3NzaWJsZS5cbiAqIFxuICogV2hlbiBiYWNrZW5kcyByZXR1cm4gc2VydmVyIGVycm9ycyAoNTAwLTU5OSkgaXQgcmV0cmllcyBpZGVtcG90ZW50IHJlcXVlc3RzXG4gKiAgdW50aWwgaXQgZ2V0cyBhIGdvb2QgcmVzcG9uc2UsIG9yIGFsbCBiYWNrZW5kcyBoYXZlIGJlZW4gdHJpZWQuXG4gKiBcbiAqIEBwYXJhbSBiYWNrZW5kcyBmZXRjaCBmdW5jdGlvbnMgZm9yIGVhY2ggYmFja2VuZCB0byBiYWxhbmNlIGFjY3Jvc3NcbiAqIEByZXR1cm5zIGEgZnVuY3Rpb24gdGhhdCBiZWhhdmVzIGp1c3QgbGlrZSBmZXRjaCwgd2l0aCBhIGAuYmFja2VuZHNgIHByb3BlcnR5IGZvciBcbiAqIHJldHJpZXZpbmcgYmFja2VuZCBzdGF0cy5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gYmFsYW5jZXIoYmFja2VuZHM6IEZldGNoRm5bXSkge1xuICBjb25zdCB0cmFja2VkID0gYmFja2VuZHMubWFwKChoKSA9PiB7XG4gICAgaWYgKHR5cGVvZiBoICE9PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgIHRocm93IEVycm9yKFwiQmFja2VuZCBtdXN0IGJlIGEgZmV0Y2ggbGlrZSBmdW5jdGlvblwiKVxuICAgIH1cbiAgICByZXR1cm4gPEJhY2tlbmQ+e1xuICAgICAgcHJveHk6IGgsXG4gICAgICByZXF1ZXN0Q291bnQ6IDAsXG4gICAgICBzY29yZWRSZXF1ZXN0Q291bnQ6IDAsXG4gICAgICBzdGF0dXNlczogQXJyYXk8bnVtYmVyPigxMCksXG4gICAgICBsYXN0RXJyb3I6IDAsXG4gICAgICBoZWFsdGhTY29yZTogMSxcbiAgICAgIGVycm9yQ291bnQ6IDBcbiAgICB9XG4gIH0pXG5cbiAgY29uc3QgZm4gPSBhc3luYyBmdW5jdGlvbiBmZXRjaEJhbGFuY2VyKHJlcTogUmVxdWVzdEluZm8sIGluaXQ/OiBSZXF1ZXN0SW5pdCB8IHVuZGVmaW5lZCk6IFByb21pc2U8UmVzcG9uc2U+IHtcbiAgICBpZiAodHlwZW9mIHJlcSA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgcmVxID0gbmV3IFJlcXVlc3QocmVxKVxuICAgIH1cbiAgICBjb25zdCBhdHRlbXB0ZWQgPSBuZXcgU2V0PEJhY2tlbmQ+KClcbiAgICB3aGlsZSAoYXR0ZW1wdGVkLnNpemUgPCB0cmFja2VkLmxlbmd0aCkge1xuICAgICAgbGV0IGJhY2tlbmQ6IEJhY2tlbmQgfCBudWxsID0gbnVsbFxuICAgICAgY29uc3QgW2JhY2tlbmRBLCBiYWNrZW5kQl0gPSBjaG9vc2VCYWNrZW5kcyh0cmFja2VkLCBhdHRlbXB0ZWQpXG5cbiAgICAgIGlmICghYmFja2VuZEEpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBSZXNwb25zZShcIk5vIGJhY2tlbmQgYXZhaWxhYmxlXCIsIHsgc3RhdHVzOiA1MDIgfSlcbiAgICAgIH1cbiAgICAgIGlmICghYmFja2VuZEIpIHtcbiAgICAgICAgYmFja2VuZCA9IGJhY2tlbmRBXG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyByYW5kb21pemUgYmV0d2VlbiAyIGdvb2QgY2FuZGlkYXRlc1xuICAgICAgICBiYWNrZW5kID0gKE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIDIpID09IDApID8gYmFja2VuZEEgOiBiYWNrZW5kQlxuICAgICAgfVxuXG4gICAgICBjb25zdCBwcm9taXNlID0gYmFja2VuZC5wcm94eShyZXEsIGluaXQpXG4gICAgICBpZiAoYmFja2VuZC5zY29yZWRSZXF1ZXN0Q291bnQgIT0gYmFja2VuZC5yZXF1ZXN0Q291bnQpIHtcbiAgICAgICAgLy8gZml4dXAgc2NvcmVcbiAgICAgICAgLy8gdGhpcyBzaG91bGQgYmUgcmVsYXRpdmVseSBjb25jdXJyZW50IHdpdGggdGhlIGZldGNoIHByb21pc2VcbiAgICAgICAgc2NvcmUoYmFja2VuZClcbiAgICAgIH1cbiAgICAgIGJhY2tlbmQucmVxdWVzdENvdW50ICs9IDFcbiAgICAgIGF0dGVtcHRlZC5hZGQoYmFja2VuZClcblxuICAgICAgbGV0IHJlc3A6IFJlc3BvbnNlXG4gICAgICB0cnkge1xuICAgICAgICByZXNwID0gYXdhaXQgcHJvbWlzZVxuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICByZXNwID0gcHJveHlFcnJvclxuICAgICAgfVxuICAgICAgaWYgKGJhY2tlbmQuc3RhdHVzZXMubGVuZ3RoIDwgMTApIHtcbiAgICAgICAgYmFja2VuZC5zdGF0dXNlcy5wdXNoKHJlc3Auc3RhdHVzKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYmFja2VuZC5zdGF0dXNlc1soYmFja2VuZC5yZXF1ZXN0Q291bnQgLSAxKSAlIGJhY2tlbmQuc3RhdHVzZXMubGVuZ3RoXSA9IHJlc3Auc3RhdHVzXG4gICAgICB9XG5cbiAgICAgIGlmIChyZXNwLnN0YXR1cyA+PSA1MDAgJiYgcmVzcC5zdGF0dXMgPCA2MDApIHtcbiAgICAgICAgYmFja2VuZC5sYXN0RXJyb3IgPSBEYXRlLm5vdygpXG4gICAgICAgIC8vIGFsd2F5cyByZWNvbXB1dGUgc2NvcmUgb24gZXJyb3JzXG4gICAgICAgIHNjb3JlKGJhY2tlbmQpXG5cbiAgICAgICAgLy8gY2xlYXIgb3V0IHJlc3BvbnNlIHRvIHRyaWdnZXIgcmV0cnlcbiAgICAgICAgaWYgKGNhblJldHJ5KHJlcSwgcmVzcCkpIHtcbiAgICAgICAgICBjb250aW51ZVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiByZXNwXG4gICAgfVxuXG4gICAgcmV0dXJuIHByb3h5RXJyb3JcbiAgfVxuXG4gIHJldHVybiBPYmplY3QuYXNzaWduKGZuLCB7IGJhY2tlbmRzOiB0cmFja2VkIH0pXG59XG5jb25zdCBwcm94eUVycm9yID0gbmV3IFJlc3BvbnNlKFwiY291bGRuJ3QgY29ubmVjdCB0byBvcmlnaW5cIiwgeyBzdGF0dXM6IDUwMiB9KVxuZXhwb3J0IGludGVyZmFjZSBGZXRjaEZuIHtcbiAgKHJlcTogUmVxdWVzdEluZm8sIGluaXQ/OiBSZXF1ZXN0SW5pdCB8IHVuZGVmaW5lZCk6IFByb21pc2U8UmVzcG9uc2U+XG59XG5cbi8qKlxuICogUmVwcmVzZW50cyBhIGJhY2tlbmQgd2l0aCBoZWFsdGggYW5kIHN0YXRpc3RpY3MuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQmFja2VuZCB7XG4gIHByb3h5OiAocmVxOiBSZXF1ZXN0SW5mbywgaW5pdD86IFJlcXVlc3RJbml0IHwgdW5kZWZpbmVkKSA9PiBQcm9taXNlPFJlc3BvbnNlPixcbiAgcmVxdWVzdENvdW50OiAwLFxuICBzY29yZWRSZXF1ZXN0Q291bnQ6IDAsXG4gIHN0YXR1c2VzOiBudW1iZXJbXSxcbiAgbGFzdEVycm9yOiBudW1iZXIsXG4gIGhlYWx0aFNjb3JlOiBudW1iZXIsXG4gIGVycm9yQ291bnQ6IDBcbn1cbi8vIGNvbXB1dGUgYSBiYWNrZW5kIGhlYWx0aCBzY29yZSB3aXRoIHRpbWUgKyBzdGF0dXMgY29kZXNcbmZ1bmN0aW9uIHNjb3JlKGJhY2tlbmQ6IEJhY2tlbmQsIGVycm9yQmFzaXM/OiBudW1iZXIpIHtcbiAgaWYgKHR5cGVvZiBlcnJvckJhc2lzICE9PSBcIm51bWJlclwiICYmICFlcnJvckJhc2lzKSBlcnJvckJhc2lzID0gRGF0ZS5ub3coKVxuXG4gIGNvbnN0IHRpbWVTaW5jZUVycm9yID0gKGVycm9yQmFzaXMgLSBiYWNrZW5kLmxhc3RFcnJvcilcbiAgY29uc3Qgc3RhdHVzZXMgPSBiYWNrZW5kLnN0YXR1c2VzXG4gIGNvbnN0IHRpbWVXZWlnaHQgPSAoYmFja2VuZC5sYXN0RXJyb3IgPT09IDAgJiYgMCkgfHxcbiAgICAoKHRpbWVTaW5jZUVycm9yIDwgMTAwMCkgJiYgMSkgfHxcbiAgICAoKHRpbWVTaW5jZUVycm9yIDwgMzAwMCkgJiYgMC44KSB8fFxuICAgICgodGltZVNpbmNlRXJyb3IgPCA1MDAwKSAmJiAwLjMpIHx8XG4gICAgKCh0aW1lU2luY2VFcnJvciA8IDEwMDAwKSAmJiAwLjEpIHx8XG4gICAgMDtcbiAgaWYgKHN0YXR1c2VzLmxlbmd0aCA9PSAwKSByZXR1cm4gMFxuICBsZXQgcmVxdWVzdHMgPSAwXG4gIGxldCBlcnJvcnMgPSAwXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgc3RhdHVzZXMubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBzdGF0dXMgPSBzdGF0dXNlc1tpXVxuICAgIGlmIChzdGF0dXMgJiYgIWlzTmFOKHN0YXR1cykpIHtcbiAgICAgIHJlcXVlc3RzICs9IDFcbiAgICAgIGlmIChzdGF0dXMgPj0gNTAwICYmIHN0YXR1cyA8IDYwMCkge1xuICAgICAgICBlcnJvcnMgKz0gMVxuICAgICAgfVxuICAgIH1cbiAgfVxuICBjb25zdCBzY29yZSA9ICgxIC0gKHRpbWVXZWlnaHQgKiAoZXJyb3JzIC8gcmVxdWVzdHMpKSlcbiAgYmFja2VuZC5oZWFsdGhTY29yZSA9IHNjb3JlXG4gIGJhY2tlbmQuc2NvcmVkUmVxdWVzdENvdW50ID0gYmFja2VuZC5yZXF1ZXN0Q291bnRcbiAgcmV0dXJuIHNjb3JlXG59XG5mdW5jdGlvbiBjYW5SZXRyeShyZXE6IFJlcXVlc3QsIHJlc3A6IFJlc3BvbnNlKSB7XG4gIGlmIChyZXNwICYmIHJlc3Auc3RhdHVzIDwgNTAwKSByZXR1cm4gZmFsc2UgLy8gZG9uJ3QgcmV0cnkgbm9ybWFsIGJvcmluZyBlcnJvcnMgb3Igc3VjY2Vzc1xuICBpZiAocmVxLm1ldGhvZCA9PSBcIkdFVFwiIHx8IHJlcS5tZXRob2QgPT0gXCJIRUFEXCIpIHJldHVybiB0cnVlXG4gIHJldHVybiBmYWxzZVxufVxuXG5mdW5jdGlvbiBjaG9vc2VCYWNrZW5kcyhiYWNrZW5kczogQmFja2VuZFtdLCBhdHRlbXB0ZWQ/OiBTZXQ8QmFja2VuZD4pIHtcbiAgbGV0IGIxOiBCYWNrZW5kIHwgdW5kZWZpbmVkXG4gIGxldCBiMjogQmFja2VuZCB8IHVuZGVmaW5lZFxuICBmb3IgKGxldCBpID0gMDsgaSA8IGJhY2tlbmRzLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgYiA9IGJhY2tlbmRzW2ldXG4gICAgaWYgKGF0dGVtcHRlZCAmJiBhdHRlbXB0ZWQuaGFzKGIpKSBjb250aW51ZTtcblxuICAgIGlmICghYjEpIHtcbiAgICAgIGIxID0gYlxuICAgICAgY29udGludWVcbiAgICB9XG4gICAgaWYgKCFiMikge1xuICAgICAgYjIgPSBiXG4gICAgICBjb250aW51ZVxuICAgIH1cblxuICAgIGNvbnN0IG9sZDEgPSBiMVxuICAgIGIxID0gYmVzdEJhY2tlbmQoYiwgYjEpXG5cbiAgICBpZiAob2xkMSAhPSBiMSkge1xuICAgICAgLy8gYjEgZ290IHJlcGxhY2VkLCBtYWtlIHN1cmUgaXQncyBub3QgYmV0dGVyXG4gICAgICBiMiA9IGJlc3RCYWNrZW5kKG9sZDEsIGIyKVxuICAgIH0gZWxzZSB7XG4gICAgICBiMiA9IGJlc3RCYWNrZW5kKGIsIGIyKVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBbYjEsIGIyXVxufVxuXG5mdW5jdGlvbiBiZXN0QmFja2VuZChiMTogQmFja2VuZCwgYjI6IEJhY2tlbmQpIHtcbiAgaWYgKFxuICAgIGIxLmhlYWx0aFNjb3JlID4gYjIuaGVhbHRoU2NvcmUgfHxcbiAgICAoYjEuaGVhbHRoU2NvcmUgPT0gYjIuaGVhbHRoU2NvcmUgJiYgYjEucmVxdWVzdENvdW50IDwgYjIucmVxdWVzdENvdW50KVxuICApIHtcbiAgICByZXR1cm4gYjFcbiAgfVxuICByZXR1cm4gYjJcbn1cblxuZXhwb3J0IGNvbnN0IF9pbnRlcm5hbCA9IHtcbiAgY2hvb3NlQmFja2VuZHMsXG4gIHNjb3JlXG59Il19


// this imports a utility class for caching HTTP responses
import responseCache from './lib/response-cache'

// Handle HTTP requests
fly.http.respondWith(async function (req) {
  const cacheKey = req.url.replace(/\?status=\d+$/, '')

  // Fetch from
  let resp = await origin(req)

  if (resp.status < 400 || resp.status > 599) {
    // response is good, cache it forever
    await responseCache.set(cacheKey, resp)
    // and return it
    return resp
  }

  let cachedResponse = await responseCache.get(cacheKey)

  if (cachedResponse) {
    // set origin status in headers and return cached response
    cachedResponse.headers.set("Origin-Status", resp.status.toString())
    return cachedResponse
  }

  // if we got here, it's an error and we have nothing cached :()
  return resp
})

/**
 * This is just a basic origin http server that
 * lets us control status codes.
 *
 *   `/` => successful response
 *   `/?status=404` => serves a 404
 *   `/?status=500` => serves a 500
 */
async function origin(req, init) {
  const url = new URL(req.url)
  const status = parseInt(url.searchParams.get('status') || '200')

  if (status === 200) {
    return new Response(`hello from ${req.url} on ${new Date()}`)
  } else {
    return new Response(`an error! Number ${status}`, { status: status })
  }
}


// Handle HTTP requests
fly.http.respondWith(async function (req) {
  // Match the requests against the built
  // in HTTP cache
  let resp = await cache.match(req)

  if (resp) {
    // got a HIT!
    resp.headers.set("Cache", "HIT")
    return resp
  }

  // Cache miss, fetch from origin
  resp = await origin(req)

  // Put the response in the cache
  await cache.put(req, resp)

  // set a header so we can tell
  resp.headers.set("Cache", "MISS")

  // return fresh response
  return resp
})

/**
 * This is just a basic origin http server that
 * response to two paths:
 *
 *   `/` => "hello", cached for 10 min
 *   `/never-cache` => "hello", never cached
 */
async function origin(req, init) {
  const url = new URL(req.url)

  switch (url.pathname) {
    case "/":
      return new Response(`hello at ${new Date()}`, {
        headers: {
          "Cache-Control": "max-age=600"
        }
      })
    case "/never-cache":
      return new Response(`hello at ${new Date()}`)
  }
  return new Response("not found", { status: 404 })
}


let config = require('./config');
let gulp = require('gulp');
let temp = require('temp');

/* ========================================
=            Requiring stuffs            =
========================================*/

let concat = require('gulp-concat');
let csso = require('gulp-csso');
let del = require('del');
let less = require('gulp-less');
let mobilizer = require('gulp-mobilizer');
let path = require('path');
let rename = require('gulp-rename');
let seq = require('gulp-sequence');
let sourcemaps = require('gulp-sourcemaps');
let uglify = require('gulp-uglify');
let connect = require('gulp-connect');
let waitOn = require('wait-on');

/* ================================================
=            Report Errors to Console            =
================================================*/

gulp.on('error', function(e) {
  throw (e);
});

/* =========================================
=            Clean dest folder            =
=========================================*/

gulp.task('clean', function() {
  return del(['dist/**']);
});

/* ==================================
=            Copy fonts            =
==================================*/

gulp.task('fonts', function() {
  return gulp.src(config.globs.fonts)
    .pipe(gulp.dest(path.join('dist', 'fonts')));
});

/* ======================================================================
=            Compile, minify, mobilize less                            =
======================================================================*/

let CSS_TEMP_DIR = temp.path({prefix: 'maui-css'});

gulp.task('css:less', function() {
  gulp.src([
    'src/less/mobile-angular-ui-base.less',
    'src/less/mobile-angular-ui-desktop.less',
    'src/less/sm-grid.less'
  ])
    .pipe(less({paths: config.globs.vendorLess}))
    .pipe(mobilizer('mobile-angular-ui-base.css', {
      'mobile-angular-ui-base.css': {hover: 'exclude', screens: ['0px']},
      'mobile-angular-ui-hover.css': {hover: 'only', screens: ['0px']}
    }))
    .pipe(gulp.dest(CSS_TEMP_DIR));
});

gulp.task('css:less:wait', waitFor([
  path.join(CSS_TEMP_DIR, 'sm-grid.css'),
  path.join(CSS_TEMP_DIR, 'mobile-angular-ui-base.css'),
  path.join(CSS_TEMP_DIR, 'mobile-angular-ui-hover.css'),
  path.join(CSS_TEMP_DIR, 'mobile-angular-ui-desktop.css')
]));

gulp.task('css:concat', function() {
  return gulp.src([
    path.join(CSS_TEMP_DIR, 'sm-grid.css'),
    path.join(CSS_TEMP_DIR, 'mobile-angular-ui-base.css')
  ])
    .pipe(concat('mobile-angular-ui-base.css'))
    .pipe(gulp.dest(path.join('dist', 'css')));
});

gulp.task('css:copy', function() {
  return gulp.src([
    path.join(CSS_TEMP_DIR, 'mobile-angular-ui-hover.css'),
    path.join(CSS_TEMP_DIR, 'mobile-angular-ui-desktop.css')
  ])
    .pipe(gulp.dest(path.join('dist', 'css')));
});

gulp.task('css:minify', function() {
  return gulp.src(path.join('dist', 'css', '*.css'))
    .pipe(csso())
    .pipe(rename({suffix: '.min'}))
    .pipe(gulp.dest(path.join('dist', 'css')));
});

gulp.task('css:rmtemp', function() {
  return del([CSS_TEMP_DIR], {force: true});
});

gulp.task('css', function(done) {
  seq(
    'css:rmtemp',
    'css:less',
    'css:less:wait',
    'css:concat',
    'css:copy',
    'css:minify',
    'css:rmtemp',
    done
  );
});

/* ====================================================================
=            Compile and minify js generating source maps            =
====================================================================*/

let compileJs = function(dest, src) {
  return gulp.src(src)
    .pipe(sourcemaps.init())
    .pipe(concat(dest))
    .pipe(gulp.dest(path.join('dist', 'js')))
    .pipe(uglify())
    .pipe(rename({suffix: '.min'}))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest(path.join('dist', 'js')));
};

gulp.task('js:core', function() {
  return compileJs('mobile-angular-ui.core.js', config.globs.core);
});

gulp.task('js:gestures', function() {
  return compileJs('mobile-angular-ui.gestures.js', config.globs.gestures);
});

gulp.task('js:main', function() {
  return compileJs('mobile-angular-ui.js', config.globs.main);
});

gulp.task('js', ['js:main', 'js:gestures', 'js:core']);

/* ======================================
=            Build Sequence            =
======================================*/

gulp.task('build:wait', waitFor([
  'dist/css/mobile-angular-ui-base.css',
  'dist/css/mobile-angular-ui-base.min.css',
  'dist/css/mobile-angular-ui-desktop.css',
  'dist/css/mobile-angular-ui-desktop.min.css',
  'dist/css/mobile-angular-ui-hover.css',
  'dist/css/mobile-angular-ui-hover.min.css',
  'dist/fonts/fontawesome-webfont.eot',
  'dist/fonts/fontawesome-webfont.svg',
  'dist/fonts/fontawesome-webfont.ttf',
  'dist/fonts/fontawesome-webfont.woff',
  'dist/fonts/fontawesome-webfont.woff2',
  'dist/js/mobile-angular-ui.core.js',
  'dist/js/mobile-angular-ui.core.min.js',
  'dist/js/mobile-angular-ui.core.min.js.map',
  'dist/js/mobile-angular-ui.gestures.js',
  'dist/js/mobile-angular-ui.gestures.min.js',
  'dist/js/mobile-angular-ui.gestures.min.js.map',
  'dist/js/mobile-angular-ui.js',
  'dist/js/mobile-angular-ui.min.js',
  'dist/js/mobile-angular-ui.min.js.map'
]));

gulp.task('build', function(done) {
  seq('clean', ['fonts', 'css', 'js'], 'build:wait', done);
});

/* ==========================================
=            Dev watch and connect          =
==========================================*/

gulp.task('dev', function(done) {
  let tasks = [];

  tasks.push('connect');
  tasks.push('watch');

  seq('build', tasks, done);
});

/* ==========================================
=            Web servers                   =
==========================================*/

gulp.task('connect', function() {
  connect.server({
    root: process.cwd(),
    host: '0.0.0.0',
    port: 3000,
    livereload: true
  });
});

/* ==============================================================
=            Setup live reloading on source changes            =
==============================================================*/

gulp.task('livereload:demo', function() {
  return gulp.src(config.globs.livereloadDemo)
    .pipe(connect.reload());
});

/* ===================================================================
=            Watch for source changes and rebuild/reload            =
===================================================================*/

gulp.task('watch', function() {
  gulp.watch(config.globs.livereloadDemo.concat('dist/**/*'), ['livereload:demo']);
  gulp.watch(['./src/less/**/*'], ['css']);

  ['core', 'gestures', 'main'].forEach(function(target) {
    gulp.watch(config.globs[target], ['js:' + target]);
  });

  gulp.watch(config.globs.fonts, ['fonts']);
});

function waitFor(resources) {
  return function() {
    return new Promise(function(resolve, reject) {
      waitOn({
        resources: resources,
        timeout: 30000
      }, function(err) {
        if (err) {
          process.exit(2);
          return reject(err);
        }

        resolve();
      });
    });
  };
}


/* eslint no-alert: 0 */

'use strict';

//
// Here is how to define your module
// has dependent on mobile-angular-ui
//
var app = angular.module('MobileAngularUiExamples', [
  'ngRoute',
  'mobile-angular-ui',

  // touch/drag feature: this is from 'mobile-angular-ui.gestures.js'.
  // This is intended to provide a flexible, integrated and and
  // easy to use alternative to other 3rd party libs like hammer.js, with the
  // final pourpose to integrate gestures into default ui interactions like
  // opening sidebars, turning switches on/off ..
  'mobile-angular-ui.gestures'
]);

app.run(function($transform) {
  window.$transform = $transform;
});

//
// You can configure ngRoute as always, but to take advantage of SharedState location
// feature (i.e. close sidebar on backbutton) you should setup 'reloadOnSearch: false'
// in order to avoid unwanted routing.
//
app.config(function($routeProvider) {
  $routeProvider.when('/', {templateUrl: 'home.html', reloadOnSearch: false});
  $routeProvider.when('/scroll', {templateUrl: 'scroll.html', reloadOnSearch: false});
  $routeProvider.when('/toggle', {templateUrl: 'toggle.html', reloadOnSearch: false});
  $routeProvider.when('/tabs', {templateUrl: 'tabs.html', reloadOnSearch: false});
  $routeProvider.when('/accordion', {templateUrl: 'accordion.html', reloadOnSearch: false});
  $routeProvider.when('/overlay', {templateUrl: 'overlay.html', reloadOnSearch: false});
  $routeProvider.when('/forms', {templateUrl: 'forms.html', reloadOnSearch: false});
  $routeProvider.when('/dropdown', {templateUrl: 'dropdown.html', reloadOnSearch: false});
  $routeProvider.when('/touch', {templateUrl: 'touch.html', reloadOnSearch: false});
  $routeProvider.when('/swipe', {templateUrl: 'swipe.html', reloadOnSearch: false});
  $routeProvider.when('/drag', {templateUrl: 'drag.html', reloadOnSearch: false});
  $routeProvider.when('/drag2', {templateUrl: 'drag2.html', reloadOnSearch: false});
  $routeProvider.when('/carousel', {templateUrl: 'carousel.html', reloadOnSearch: false});
});

//
// `$touch example`
//

app.directive('toucharea', ['$touch', function($touch) {
  // Runs during compile
  return {
    restrict: 'C',
    link: function($scope, elem) {
      $scope.touch = null;
      $touch.bind(elem, {
        start: function(touch) {
          $scope.containerRect = elem[0].getBoundingClientRect();
          $scope.touch = touch;
          $scope.$apply();
        },

        cancel: function(touch) {
          $scope.touch = touch;
          $scope.$apply();
        },

        move: function(touch) {
          $scope.touch = touch;
          $scope.$apply();
        },

        end: function(touch) {
          $scope.touch = touch;
          $scope.$apply();
        }
      });
    }
  };
}]);

//
// `$drag` example: drag to dismiss
//
app.directive('dragToDismiss', function($drag, $parse, $timeout) {
  return {
    restrict: 'A',
    compile: function(elem, attrs) {
      var dismissFn = $parse(attrs.dragToDismiss);
      return function(scope, elem) {
        var dismiss = false;

        $drag.bind(elem, {
          transform: $drag.TRANSLATE_RIGHT,
          move: function(drag) {
            if (drag.distanceX >= drag.rect.width / 4) {
              dismiss = true;
              elem.addClass('dismiss');
            } else {
              dismiss = false;
              elem.removeClass('dismiss');
            }
          },
          cancel: function() {
            elem.removeClass('dismiss');
          },
          end: function(drag) {
            if (dismiss) {
              elem.addClass('dismitted');
              $timeout(function() {
                scope.$apply(function() {
                  dismissFn(scope);
                });
              }, 300);
            } else {
              drag.reset();
            }
          }
        });
      };
    }
  };
});

//
// Another `$drag` usage example: this is how you could create
// a touch enabled "deck of cards" carousel. See `carousel.html` for markup.
//
app.directive('carousel', function() {
  return {
    restrict: 'C',
    scope: {},
    controller: function() {
      this.itemCount = 0;
      this.activeItem = null;

      this.addItem = function() {
        var newId = this.itemCount++;
        this.activeItem = this.itemCount === 1 ? newId : this.activeItem;
        return newId;
      };

      this.next = function() {
        this.activeItem = this.activeItem || 0;
        this.activeItem = this.activeItem === this.itemCount - 1 ? 0 : this.activeItem + 1;
      };

      this.prev = function() {
        this.activeItem = this.activeItem || 0;
        this.activeItem = this.activeItem === 0 ? this.itemCount - 1 : this.activeItem - 1;
      };
    }
  };
});

app.directive('carouselItem', function($drag) {
  return {
    restrict: 'C',
    require: '^carousel',
    scope: {},
    transclude: true,
    template: '

',
    link: function(scope, elem, attrs, carousel) {
      scope.carousel = carousel;
      var id = carousel.addItem();

      var zIndex = function() {
        var res = 0;
        if (id === carousel.activeItem) {
          res = 2000;
        } else if (carousel.activeItem < id) {
          res = 2000 - (id - carousel.activeItem);
        } else {
          res = 2000 - (carousel.itemCount - 1 - carousel.activeItem + id);
        }
        return res;
      };

      scope.$watch(function() {
        return carousel.activeItem;
      }, function() {
        elem[0].style.zIndex = zIndex();
      });

      $drag.bind(elem, {
        //
        // This is an example of custom transform function
        //
        transform: function(element, transform, touch) {
          //
          // use translate both as basis for the new transform:
          //
          var t = $drag.TRANSLATE_BOTH(element, transform, touch);

          //
          // Add rotation:
          //
          var Dx = touch.distanceX;
          var t0 = touch.startTransform;
          var sign = Dx < 0 ? -1 : 1;
          var angle = sign * Math.min((Math.abs(Dx) / 700) * 30, 30);

          t.rotateZ = angle + (Math.round(t0.rotateZ));

          return t;
        },
        move: function(drag) {
          if (Math.abs(drag.distanceX) >= drag.rect.width / 4) {
            elem.addClass('dismiss');
          } else {
            elem.removeClass('dismiss');
          }
        },
        cancel: function() {
          elem.removeClass('dismiss');
        },
        end: function(drag) {
          elem.removeClass('dismiss');
          if (Math.abs(drag.distanceX) >= drag.rect.width / 4) {
            scope.$apply(function() {
              carousel.next();
            });
          }
          drag.reset();
        }
      });
    }
  };
});

app.directive('dragMe', ['$drag', function($drag) {
  return {
    controller: function($scope, $element) {
      $drag.bind($element,
        {
          //
          // Here you can see how to limit movement
          // to an element
          //
          transform: $drag.TRANSLATE_INSIDE($element.parent()),
          end: function(drag) {
            // go back to initial position
            drag.reset();
          }
        },
        { // release touch when movement is outside bounduaries
          sensitiveArea: $element.parent()
        }
      );
    }
  };
}]);

//
// For this trivial demo we have just a unique MainController
// for everything
//
app.controller('MainController', function($rootScope, $scope) {

  $scope.swiped = function(direction) {
    alert('Swiped ' + direction);
  };

  // User agent displayed in home page
  $scope.userAgent = navigator.userAgent;

  // Needed for the loading screen
  $rootScope.$on('$routeChangeStart', function() {
    $rootScope.loading = true;
  });

  $rootScope.$on('$routeChangeSuccess', function() {
    $rootScope.loading = false;
  });

  // Fake text i used here and there.
  $scope.lorem = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. ' +
    'Vel explicabo, aliquid eaque soluta nihil eligendi adipisci error, illum ' +
    'corrupti nam fuga omnis quod quaerat mollitia expedita impedit dolores ipsam. Obcaecati.';

  //
  // 'Scroll' screen
  //
  var scrollItems = [];

  for (var i = 1; i <= 100; i++) {
    scrollItems.push('Item ' + i);
  }

  $scope.scrollItems = scrollItems;

  $scope.bottomReached = function() {
    alert('Congrats you scrolled to the end of the list!');
  };

  //
  // Right Sidebar
  //
  $scope.chatUsers = [
    {name: 'Carlos  Flowers', online: true},
    {name: 'Byron Taylor', online: true},
    {name: 'Jana  Terry', online: true},
    {name: 'Darryl  Stone', online: true},
    {name: 'Fannie  Carlson', online: true},
    {name: 'Holly Nguyen', online: true},
    {name: 'Bill  Chavez', online: true},
    {name: 'Veronica  Maxwell', online: true},
    {name: 'Jessica Webster', online: true},
    {name: 'Jackie  Barton', online: true},
    {name: 'Crystal Drake', online: false},
    {name: 'Milton  Dean', online: false},
    {name: 'Joann Johnston', online: false},
    {name: 'Cora  Vaughn', online: false},
    {name: 'Nina  Briggs', online: false},
    {name: 'Casey Turner', online: false},
    {name: 'Jimmie  Wilson', online: false},
    {name: 'Nathaniel Steele', online: false},
    {name: 'Aubrey  Cole', online: false},
    {name: 'Donnie  Summers', online: false},
    {name: 'Kate  Myers', online: false},
    {name: 'Priscilla Hawkins', online: false},
    {name: 'Joe Barker', online: false},
    {name: 'Lee Norman', online: false},
    {name: 'Ebony Rice', online: false}
  ];

  //
  // 'Forms' screen
  //
  $scope.rememberMe = true;
  $scope.email = 'me@example.com';

  $scope.login = function() {
    alert('You submitted the login form');
  };

  //
  // 'Drag' screen
  //
  $scope.notices = [];

  for (var j = 0; j < 10; j++) {
    $scope.notices.push({icon: 'envelope', message: 'Notice ' + (j + 1)});
  }

  $scope.deleteNotice = function(notice) {
    var index = $scope.notices.indexOf(notice);
    if (index > -1) {
      $scope.notices.splice(index, 1);
    }
  };
});