<commit_before>'use strict';

const Joi = require('joi');
const Postgres = require('pg').native;
const Schemas = require('./schemas');
const Config = require('./config');

const internals = {
    query: function (query, next) {

        Postgres.connect(Config.postgresURL, (connErr, client, done) => {

            if (connErr) {
                done();

                return next(connErr);
            }

            client.query(query, (queryErr, result) => {

                done();

                return next(queryErr, result);
            });
        });
    },
    read: function (id, query, next) {

        const err = Schemas.id.validate(id).error;

        if (err) {

            return next(err);
        }

        internals.query({
            text: query,
            values: [id]
        }, (err, result) => {

            if (err) {

                return next(err);
            }

            return next(null, result.rows);
        });
    },
    edit: function (id, schema, data, query, next) {

        const err = schema.validate(data).error ||
            Schemas.id.validate(id).error;

        if (err) {

            return next(err);
        }

        internals.query(query, (err, result) => {

            if (err) {

                return next(err);
            }

            return next(null, result.rowCount);
        });
    },
    add: function (schema, data, query, next) {

        const err = schema.validate(data).error;

        if (err) {

            return next(err);
        }

        internals.query(query, (err, result) => {

            if (err) {

                return next(err);
            }

            return next(null, result.rows);
        });
    },
    delete: function (id, table, next) {

        const err = Schemas.id.validate(id).error;

        if (err) {

            return next(err);
        }

        internals.query({
            text: `DELETE FROM ${table} WHERE id = $1`,
            values: [id]
        }, (err, result) => {

            if (err) {

                return next(err);
            }

            return next(null, result.rowCount);
        });
    }
};

internals.query(`
        CREATE TABLE IF NOT EXISTS authors (
            id               serial PRIMARY KEY,
            penName          varchar (100) NOT NULL,
            lastName         varchar (50),
            firstName        varchar (50)
        );
        CREATE TABLE IF NOT EXISTS books (
            id               serial PRIMARY KEY,
            title            varchar (300) NOT NULL,
            synopsis         varchar (1000),
            isbn10           bigint,
            isbn13           bigint,
            author           int NOT NULL
        );
    `, (err) => {

    if (err) {

        throw err;
    }
});

exports.authorModel = [
    {
        name: 'author.browse',
        method: function (query, sort, order, page, perPage, next) {

            const err = Schemas.page.validate(page).error ||
                Schemas.perPage.validate(perPage).error ||
                Schemas.order.validate(order).error ||
                Schemas.authorSort.validate(sort).error;

            if (err) {

                return next(err);
            }

            const values = [sort, perPage, (page - 1) * perPage];
            let filter = '';
            if (query) {
                filter = `WHERE penName like '%${query}%' OR
                                lastName like '%${query}%' OR
                                firstName like '%${query}%' `;
            }

            internals.query({
                text: `SELECT COUNT(id) FROM authors ${filter}`,
                values: []
            }, (err, total) => {

                if (err) {

                    return next(err);
                }

                const pages = Math.ceil(total.rows[0].count / perPage) || 1;

                internals.query({
                    text: `SELECT * FROM authors ${filter}ORDER BY $1 ${order} LIMIT $2 OFFSET $3`,
                    values: values
                }, (err, result) => {

                    if (err) {

                        return next(err);
                    }

                    return next(null, {
                        page: (pages < page) ? pages : page,
                        pages: pages,
                        items: result.rows
                    });
                });
            });
        }
    },
    {
        name: 'author.read',
        method: function (id, next) {

            return internals.read(id, 'SELECT * FROM authors WHERE id = $1', next);
        }
    },
    {
        name: 'author.edit',
        method: function (id, author, next) {

            const queryValues = [];
            let queryText = 'UPDATE authors SET';

            if (author.hasOwnProperty('penName')) {
                queryValues.push(author.penName);
                queryText = `${queryText} penName = $${queryValues.length}`;
            }

            if (author.hasOwnProperty('lastName')) {
                queryValues.push(author.lastName);
                queryText = `${queryText} lastName = $${queryValues.length}`;
            }

            if (author.hasOwnProperty('firstName')) {
                queryValues.push(author.firstName);
                queryText = `${queryText} firstName = $${queryValues.length}`;
            }

            queryValues.push(id);

            return internals.edit(id, Schemas.author, author, {
                text: `${queryText} WHERE id = $${queryValues.length}`,
                values: queryValues
            }, next);
        }
    },
    {
        name: 'author.add',
        method: function (author, next) {

            return internals.add(Schemas.author.requiredKeys('penName'), author, {
                text: 'INSERT INTO authors (penName, lastName, firstName) VALUES ($1, $2, $3) RETURNING id',
                values: [author.penName, author.lastName, author.firstName]
            }, next);
        }
    },
    {
        name: 'author.delete',
        method: function (id, next) {

            return internals.delete(id, 'authors', next);
        }
    }
];

exports.bookModel = [
    {
        name: 'book.browse',
        method: function (query, sort, order, page, perPage, author, next) {

            const err = Schemas.page.validate(page).error ||
                Schemas.perPage.validate(perPage).error ||
                Schemas.order.validate(order).error ||
                Schemas.bookSort.validate(sort).error;

            if (err) {

                return next(err);
            }

            const values = [sort, perPage, (page - 1) * perPage];
            let filter = '';
            let filterCount = '';

            if (author && !Schemas.id.validate(author).error) {
                values.push(author);
                filter = `books.author = $${values.length}`;
            }

            if (query && !Joi.string().validate(query).error) {
                if (filter) {
                    filter = `${filter} AND`;
                }
                filter = `${filter} books.title like '%${query}%' OR
                                    books.synopsis like '%${query}%'`;
            }

            if (filter) {
                filter = `WHERE ${filter}`;
                filterCount = filter.replace(` = $${values.length}`, ` = ${author}`);
            }

            internals.query({
                text: `SELECT COUNT(books.id)
                       FROM books INNER JOIN authors
                       ON books.author = authors.id
                       ${filterCount}`,
                values: []
            }, (err, total) => {

                if (err) {

                    return next(err);
                }

                const pages = Math.ceil(total.rows[0].count / perPage) || 1;

                internals.query({
                    text: `SELECT   books.id,
                                    books.title,
                                    books.synopsis,
                                    books.isbn10,
                                    books.isbn13,
                                    authors.id as author,
                                    authors.penName
                           FROM books INNER JOIN authors
                           ON books.author = authors.id
                       ${filter} ORDER BY $1 ${order} LIMIT $2 OFFSET $3`,
                    values: values
                }, (err, result) => {

                    if (err) {

                        return next(err);
                    }

                    return next(null, {
                        page: (pages < page) ? pages : page,
                        pages: pages,
                        items: result.rows
                    });
                });
            });
        }
    },
    {
        name: 'book.read',
        method: function (id, next) {

            return internals.read(id,
                `SELECT books.title,
                        books.synopsis,
                        books.isbn10,
                        books.isbn13,
                        authors.penName
                FROM books INNER JOIN authors
                ON books.author = authors.id
                WHERE books.id = $1`, next);
        }
    },
    {
        name: 'book.edit',
        method: function (id, book, next) {

            const queryValues = [];
            let queryText = 'UPDATE books SET';

            if (book.hasOwnProperty('title')) {
                queryValues.push(book.title);
                queryText = `${queryText} title = $${queryValues.length}`;
            }

            if (book.hasOwnProperty('synopsis')) {
                queryValues.push(book.synopsis);
                queryText = `${queryText} synopsis = $${queryValues.length}`;
            }

            if (book.hasOwnProperty('isbn10')) {
                queryValues.push(book.isbn10);
                queryText = `${queryText} isbn10 = $${queryValues.length}`;
            }

            if (book.hasOwnProperty('isbn13')) {
                queryValues.push(book.isbn13);
                queryText = `${queryText} isbn13 = $${queryValues.length}`;
            }

            if (book.hasOwnProperty('author')) {
                queryValues.push(book.author);
                queryText = `${queryText} author = $${queryValues.length}`;
            }

            queryValues.push(id);

            return internals.edit(id, Schemas.book, book, {
                text: `${queryText} WHERE id = $${queryValues.length}`,
                values: queryValues
            }, next);
        }
    },
    {
        name: 'book.add',
        method: function (book, next) {

            return internals.add(Schemas.book.requiredKeys('title', 'author'), book, {
                text: 'INSERT INTO books (title, synopsis, isbn10, isbn13, author) VALUES ($1, $2, $3, $4, $5) RETURNING id',
                values: [book.title, book.synopsis, book.isbn10, book.isbn13, book.author]
            }, next);
        }
    },
    {
        name: 'book.delete',
        method: function (id, next) {

            return internals.delete(id, 'books', next);
        }
    }
];
<commit_msg>optimize browse arguments validation<commit_after>'use strict';

const Joi = require('joi');
const Postgres = require('pg').native;
const Schemas = require('./schemas');
const Config = require('./config');

const internals = {
    query: function (query, next) {

        Postgres.connect(Config.postgresURL, (connErr, client, done) => {

            if (connErr) {
                done();

                return next(connErr);
            }

            client.query(query, (queryErr, result) => {

                done();

                return next(queryErr, result);
            });
        });
    },
    validateBrowseArgs: function (sortSchema, sort, order, page, perPage) {

        return Schemas.page.validate(page).error ||
            Schemas.perPage.validate(perPage).error ||
            Schemas.order.validate(order).error ||
            sortSchema.validate(sort).error;
    },
    read: function (id, query, next) {

        const err = Schemas.id.validate(id).error;

        if (err) {

            return next(err);
        }

        internals.query({
            text: query,
            values: [id]
        }, (err, result) => {

            if (err) {

                return next(err);
            }

            return next(null, result.rows);
        });
    },
    edit: function (id, schema, data, query, next) {

        const err = schema.validate(data).error ||
            Schemas.id.validate(id).error;

        if (err) {

            return next(err);
        }

        internals.query(query, (err, result) => {

            if (err) {

                return next(err);
            }

            return next(null, result.rowCount);
        });
    },
    add: function (schema, data, query, next) {

        const err = schema.validate(data).error;

        if (err) {

            return next(err);
        }

        internals.query(query, (err, result) => {

            if (err) {

                return next(err);
            }

            return next(null, result.rows);
        });
    },
    delete: function (id, table, next) {

        const err = Schemas.id.validate(id).error;

        if (err) {

            return next(err);
        }

        internals.query({
            text: `DELETE FROM ${table} WHERE id = $1`,
            values: [id]
        }, (err, result) => {

            if (err) {

                return next(err);
            }

            return next(null, result.rowCount);
        });
    }
};

internals.query(`
        CREATE TABLE IF NOT EXISTS authors (
            id               serial PRIMARY KEY,
            penName          varchar (100) NOT NULL,
            lastName         varchar (50),
            firstName        varchar (50)
        );
        CREATE TABLE IF NOT EXISTS books (
            id               serial PRIMARY KEY,
            title            varchar (300) NOT NULL,
            synopsis         varchar (1000),
            isbn10           bigint,
            isbn13           bigint,
            author           int NOT NULL
        );
    `, (err) => {

    if (err) {

        throw err;
    }
});

exports.authorModel = [
    {
        name: 'author.browse',
        method: function (query, sort, order, page, perPage, next) {

            const err = internals.validateBrowseArgs(Schemas.authorSort, sort, order, page, perPage);

            if (err) {

                return next(err);
            }

            const values = [sort, perPage, (page - 1) * perPage];
            let filter = '';
            if (query) {
                filter = `WHERE penName like '%${query}%' OR
                                lastName like '%${query}%' OR
                                firstName like '%${query}%' `;
            }

            internals.query({
                text: `SELECT COUNT(id) FROM authors ${filter}`,
                values: []
            }, (err, total) => {

                if (err) {

                    return next(err);
                }

                const pages = Math.ceil(total.rows[0].count / perPage) || 1;

                internals.query({
                    text: `SELECT * FROM authors ${filter}ORDER BY $1 ${order} LIMIT $2 OFFSET $3`,
                    values: values
                }, (err, result) => {

                    if (err) {

                        return next(err);
                    }

                    return next(null, {
                        page: (pages < page) ? pages : page,
                        pages: pages,
                        items: result.rows
                    });
                });
            });
        }
    },
    {
        name: 'author.read',
        method: function (id, next) {

            return internals.read(id, 'SELECT * FROM authors WHERE id = $1', next);
        }
    },
    {
        name: 'author.edit',
        method: function (id, author, next) {

            const queryValues = [];
            let queryText = 'UPDATE authors SET';

            if (author.hasOwnProperty('penName')) {
                queryValues.push(author.penName);
                queryText = `${queryText} penName = $${queryValues.length}`;
            }

            if (author.hasOwnProperty('lastName')) {
                queryValues.push(author.lastName);
                queryText = `${queryText} lastName = $${queryValues.length}`;
            }

            if (author.hasOwnProperty('firstName')) {
                queryValues.push(author.firstName);
                queryText = `${queryText} firstName = $${queryValues.length}`;
            }

            queryValues.push(id);

            return internals.edit(id, Schemas.author, author, {
                text: `${queryText} WHERE id = $${queryValues.length}`,
                values: queryValues
            }, next);
        }
    },
    {
        name: 'author.add',
        method: function (author, next) {

            return internals.add(Schemas.author.requiredKeys('penName'), author, {
                text: 'INSERT INTO authors (penName, lastName, firstName) VALUES ($1, $2, $3) RETURNING id',
                values: [author.penName, author.lastName, author.firstName]
            }, next);
        }
    },
    {
        name: 'author.delete',
        method: function (id, next) {

            return internals.delete(id, 'authors', next);
        }
    }
];

exports.bookModel = [
    {
        name: 'book.browse',
        method: function (query, sort, order, page, perPage, author, next) {

            const err = internals.validateBrowseArgs(Schemas.bookSort, sort, order, page, perPage);

            if (err) {

                return next(err);
            }

            const values = [sort, perPage, (page - 1) * perPage];
            let filter = '';
            let filterCount = '';

            if (author && !Schemas.id.validate(author).error) {
                values.push(author);
                filter = `books.author = $${values.length}`;
            }

            if (query && !Joi.string().validate(query).error) {
                if (filter) {
                    filter = `${filter} AND`;
                }
                filter = `${filter} books.title like '%${query}%' OR
                                    books.synopsis like '%${query}%'`;
            }

            if (filter) {
                filter = `WHERE ${filter}`;
                filterCount = filter.replace(` = $${values.length}`, ` = ${author}`);
            }

            internals.query({
                text: `SELECT COUNT(books.id)
                       FROM books INNER JOIN authors
                       ON books.author = authors.id
                       ${filterCount}`,
                values: []
            }, (err, total) => {

                if (err) {

                    return next(err);
                }

                const pages = Math.ceil(total.rows[0].count / perPage) || 1;

                internals.query({
                    text: `SELECT   books.id,
                                    books.title,
                                    books.synopsis,
                                    books.isbn10,
                                    books.isbn13,
                                    authors.id as author,
                                    authors.penName
                           FROM books INNER JOIN authors
                           ON books.author = authors.id
                       ${filter} ORDER BY $1 ${order} LIMIT $2 OFFSET $3`,
                    values: values
                }, (err, result) => {

                    if (err) {

                        return next(err);
                    }

                    return next(null, {
                        page: (pages < page) ? pages : page,
                        pages: pages,
                        items: result.rows
                    });
                });
            });
        }
    },
    {
        name: 'book.read',
        method: function (id, next) {

            return internals.read(id,
                `SELECT books.title,
                        books.synopsis,
                        books.isbn10,
                        books.isbn13,
                        authors.penName
                FROM books INNER JOIN authors
                ON books.author = authors.id
                WHERE books.id = $1`, next);
        }
    },
    {
        name: 'book.edit',
        method: function (id, book, next) {

            const queryValues = [];
            let queryText = 'UPDATE books SET';

            if (book.hasOwnProperty('title')) {
                queryValues.push(book.title);
                queryText = `${queryText} title = $${queryValues.length}`;
            }

            if (book.hasOwnProperty('synopsis')) {
                queryValues.push(book.synopsis);
                queryText = `${queryText} synopsis = $${queryValues.length}`;
            }

            if (book.hasOwnProperty('isbn10')) {
                queryValues.push(book.isbn10);
                queryText = `${queryText} isbn10 = $${queryValues.length}`;
            }

            if (book.hasOwnProperty('isbn13')) {
                queryValues.push(book.isbn13);
                queryText = `${queryText} isbn13 = $${queryValues.length}`;
            }

            if (book.hasOwnProperty('author')) {
                queryValues.push(book.author);
                queryText = `${queryText} author = $${queryValues.length}`;
            }

            queryValues.push(id);

            return internals.edit(id, Schemas.book, book, {
                text: `${queryText} WHERE id = $${queryValues.length}`,
                values: queryValues
            }, next);
        }
    },
    {
        name: 'book.add',
        method: function (book, next) {

            return internals.add(Schemas.book.requiredKeys('title', 'author'), book, {
                text: 'INSERT INTO books (title, synopsis, isbn10, isbn13, author) VALUES ($1, $2, $3, $4, $5) RETURNING id',
                values: [book.title, book.synopsis, book.isbn10, book.isbn13, book.author]
            }, next);
        }
    },
    {
        name: 'book.delete',
        method: function (id, next) {

            return internals.delete(id, 'books', next);
        }
    }
];
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015-2016 Fraunhofer FOKUS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

angular.module('app')
    .factory('http', function ($http, $q, $cookieStore) {

        var marketplaceIP = 'http://localhost:8082/api/v1/app/';
        var http = {};
        var URL = 'http://emm-dev.nubomedia.eu:8090';
        var customHeaders = {};

        if ($cookieStore.get('tokenNb') === '' || angular.isUndefined($cookieStore.get('tokenNb')))
            customHeaders = {
                'Accept': 'application/json',
                'Content-type': 'application/json'
            };
        else {

            customHeaders = {
                'Accept': 'application/json',
                'Content-type': 'application/json',
                'Authorization': 'Bearer ' + $cookieStore.get('tokenNb')
            };
        }

        http.get = function (url) {
            customHeaders['project-id'] = $cookieStore.get('projectNb').id;
            console.log(customHeaders);
            return $http({
                url: url,
                method: 'GET',
                headers: customHeaders
            })
        };

        http.getMarket = function (url) {
            delete customHeaders['project-id'];
            console.log(customHeaders);
            return $http({
                url: url,
                method: 'GET',
                headers: customHeaders
            })
        };

        http.getMarketplaceIp = function () {
            if (angular.isUndefined($cookieStore.get('URLNb')))
                console.log($cookieStore.get('URLNb'));
            else
                http.syncGet($cookieStore.get('URLNb') + '/api/v1/nubomedia/config')
                    .then(function (result) {
                        console.log(result);
                        $cookieStore.put('marketplaceIP', result);
                        return result;
                    })
        };

        http.post = function (url, data, type) {
            //if (!angular.isUndefined(type)) {
            //
            //    customHeaders['Content-type'] = 'text/plain';
            //    customHeaders['Accept'] = 'text/plain';
            //} else {
            //    customHeaders['Accept'] = 'application/json';
            //    customHeaders['Content-type'] = 'application/json';
            //}
            customHeaders['project-id'] = $cookieStore.get('projectNb').id;
            $('#modalSend').modal('show');
            console.log(customHeaders);
            console.log(data);
            return $http({
                url: url,
                method: 'POST',
                data: data,
                headers: customHeaders
            });
        };
        http.postXML = function (url, data) {
            $('#modalSend').modal('show');
            return $http({
                url: url,
                dataType: 'xml',
                method: 'POST',
                data: data,
                headers: {
                    "Content-Type": "application/xml"
                }

            });
        };
        http.put = function (url, data) {
            customHeaders['project-id'] = $cookieStore.get('projectNb').id;
            $('#modalSend').modal('show');
            return $http({
                url: url,
                method: 'PUT',
                data: data,
                headers: customHeaders
            });
        };

        http.delete = function (url) {
            customHeaders['project-id'] = $cookieStore.get('projectNb').id;
            $('#modalSend').modal('show');
            return $http({
                url: url,
                method: 'DELETE',
                headers: customHeaders
            });
        };

        http.syncGet = function (url) {
            var deferred = $q.defer();
            http.get(url).success(function (data, status) {
                deferred.resolve(data);
            });
            return deferred.promise;
        };

        return http;
    });
<commit_msg>Improved the httpResquets<commit_after>/*
 * Copyright (c) 2015-2016 Fraunhofer FOKUS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

angular.module('app')
    .factory('http', function ($http, $q, $cookieStore) {

        var marketplaceIP = 'http://localhost:8082/api/v1/app/';
        var http = {};
        var URL = 'http://emm-dev.nubomedia.eu:8090';
        var customHeaders = {};

        if ($cookieStore.get('tokenNb') === '' || angular.isUndefined($cookieStore.get('tokenNb')))
            customHeaders = {
                'Accept': 'application/json',
                'Content-type': 'application/json'
            };
        else {

            customHeaders = {
                'Accept': 'application/json',
                'Content-type': 'application/json',
                'Authorization': 'Bearer ' + $cookieStore.get('tokenNb')
            };
        }

        http.get = function (url) {
            checkHeaders(url, customHeaders);
            return $http({
                url: url,
                method: 'GET',
                headers: customHeaders
            })
        };

        http.getMarket = function (url) {
            checkHeaders(url, customHeaders);
            return $http({
                url: url,
                method: 'GET',
                headers: customHeaders
            })
        };

        http.getMarketplaceIp = function () {
            if (angular.isUndefined($cookieStore.get('URLNb')))
                console.log($cookieStore.get('URLNb'));
            else
                http.syncGet($cookieStore.get('URLNb') + '/api/v1/nubomedia/config')
                    .then(function (result) {
                        console.log(result);
                        $cookieStore.put('marketplaceIP', result);
                        return result;
                    })
        };

        http.post = function (url, data, type) {
            //if (!angular.isUndefined(type)) {
            //
            //    customHeaders['Content-type'] = 'text/plain';
            //    customHeaders['Accept'] = 'text/plain';
            //} else {
            //    customHeaders['Accept'] = 'application/json';
            //    customHeaders['Content-type'] = 'application/json';
            //}
            checkHeaders(url, customHeaders);
            $('#modalSend').modal('show');
            console.log(customHeaders);
            console.log(data);
            return $http({
                url: url,
                method: 'POST',
                data: data,
                headers: customHeaders
            });
        };
        http.postXML = function (url, data) {
            $('#modalSend').modal('show');
            return $http({
                url: url,
                dataType: 'xml',
                method: 'POST',
                data: data,
                headers: {
                    "Content-Type": "application/xml"
                }

            });
        };
        http.put = function (url, data) {
            checkHeaders(url, customHeaders);
            $('#modalSend').modal('show');
            return $http({
                url: url,
                method: 'PUT',
                data: data,
                headers: customHeaders
            });
        };

        http.delete = function (url) {
            checkHeaders(url, customHeaders);
            $('#modalSend').modal('show');
            return $http({
                url: url,
                method: 'DELETE',
                headers: customHeaders
            });
        };

        http.syncGet = function (url) {
            var deferred = $q.defer();
            http.get(url).success(function (data, status) {
                deferred.resolve(data);
            });
            return deferred.promise;
        };

        function checkHeaders(url, customHeaders) {
            if (url.indexOf('8082') > -1 || url.indexOf('9000') > -1) {
                delete customHeaders['project-id'];

            } else {
                customHeaders['project-id'] = $cookieStore.get('projectNb').id;
            }
            console.log(customHeaders);
        }

        return http;
    });
<|endoftext|>


<commit_before>import fs from 'fs'
import React, { Component } from 'react'
import debounce from 'lodash/debounce'
import { connect } from 'react-redux'

import IconCheck from 'react-icons/md/check-circle'
import IconChecking from 'react-icons/md/autorenew'
import IconError from 'react-icons/md/error'
import IconWarning from 'react-icons/md/warning'

import {
  exec,
  fileDialog,
  fsAccess,
} from 'helpers/fs'

import { updateSettings } from 'actions/settings'

import Button from 'components/Button'
import RadioGroup from 'components/RadioGroup'
import Radio from 'components/RadioGroup/Radio'

export async function getMJMLVersion (location) {
  try {
    await fsAccess(location, fs.constants.R_OK | fs.constants.R_OK | fs.constants.X_OK)
    const { err, stdout } = await exec(`${location} --version`)
    if (err) { return null }
    const version = stdout.trim()
    return version
  } catch (e) {
    return null
  }
}

@connect(state => {
  const { settings } = state
  return {
    mjmlEngine: settings.getIn(['mjml', 'engine'], 'auto'),
    mjmlPath: settings.getIn(['mjml', 'path'], ''),
  }
}, {
  updateSettings,
})
class MJMLEngine extends Component {

  state = {
    mjmlEngine: 'auto',
    mjmlPath: '',
    // unset / checking / valid / invalid
    pathStatus: 'unset',
    // mjml version of local mjml
    mjmlVersion: null,
  }

  componentWillMount () {
    const { mjmlEngine, mjmlPath } = this.props
    this.setState({
      mjmlEngine,
      mjmlPath,
      pathStatus: mjmlPath ? 'checking' : 'unset',
    })
  }

  componentDidMount () {
    if (this.state.mjmlPath) {
      window.requestIdleCallback(() => {
        this.checkEngine()
      })
    }
  }

  componentDidUpdate (prevProps, prevState) {
    if (prevState.mjmlEngine === 'auto' && this.state.mjmlEngine === 'manual') {
      if (this.state.pathStatus === 'checking') {
        window.requestIdleCallback(this.checkEngine)
      }
    }
  }

  handleChangeEngine = mjmlEngine => {
    this.setState({ mjmlEngine })
    this.debounceSaveSettings()
  }

  handleChangeMJMLPath = p => {
    if (!p) { return this.setState({ pathStatus: 'unset', mjmlPath: '' }) }
    this.setState({ mjmlPath: p })
    this.checkEngine()
    this.debounceSaveSettings()
  }

  handleBrowse = () => {
    const p = fileDialog({
      properties: [
        'openFile',
      ],
    })
    if (!p) { return }
    this.handleChangeMJMLPath(p)
  }

  assignProps = ({ mjmlPath, mjmlEngine }) => this.setState({
    mjmlPath,
    mjmlEngine,
    pathStatus: mjmlPath ? 'checking' : 'unset',
  })

  checkEngine = () => {
    if (this.state.pathStatus !== 'checking') {
      this.setState({ pathStatus: 'checking' })
    }
    this.debounceCheckEngine()
  }

  debounceCheckEngine = debounce(async () => {
    const { mjmlPath } = this.state
    if (!mjmlPath) { return this.setState({ pathStatus: 'unset' }) }
    const version = await getMJMLVersion(mjmlPath)
    this.setState({
      pathStatus: version ? 'valid' : 'invalid',
      mjmlVersion: version,
    })
  }, 250)

  debounceSaveSettings = debounce(() => {
    const { mjmlPath, mjmlEngine } = this.state
    const { updateSettings } = this.props
    updateSettings(settings => {
      return settings
        .setIn(['mjml', 'path'], mjmlPath)
        .setIn(['mjml', 'engine'], mjmlEngine)
    })
  }, 500)

  render () {

    const {
      mjmlEngine,
      mjmlPath,
      mjmlVersion,
      pathStatus,
    } = this.state

    return (
      <RadioGroup
        value={mjmlEngine}
        onChange={this.handleChangeEngine}
      >
        <Radio value='auto'>
          {`Use the embedded MJML engine (v${__MJML_VERSION__})`}
        </Radio>
        <Radio value='manual'>

              {'Use a custom MJML engine (slower)'}

            {mjmlEngine === 'manual' && (

                  <input
                    autoFocus
                    className='fg-1'
                    value={mjmlPath}
                    onChange={e => this.handleChangeMJMLPath(e.target.value)}
                    placeholder='MJML path'
                    type='text'
                  />
                  <Button ghost onClick={this.handleBrowse} type='button'>
                    {'Browse'}
                  </Button>

                {pathStatus === 'unset' ? (

                    <IconWarning className='mr-5' />
                    {'No engine set'}

                ) : pathStatus === 'checking' ? (

                    <IconChecking className='mr-5 rotating' />
                    {'Checking...'}

                ) : pathStatus === 'valid' ? (

                    <IconCheck className='mr-5' />
                    {`MJML ${mjmlVersion} located successfully`}

                ) : pathStatus === 'invalid' ? (

                    <IconError className='mr-5' />
                    {'MJML not found at this location'}

                ) : null}

            )}

        </Radio>
      </RadioGroup>
    )
  }

}

export default MJMLEngine
<commit_msg>prevent unmounted setState<commit_after>import fs from 'fs'
import React, { Component } from 'react'
import debounce from 'lodash/debounce'
import { connect } from 'react-redux'

import IconCheck from 'react-icons/md/check-circle'
import IconChecking from 'react-icons/md/autorenew'
import IconError from 'react-icons/md/error'
import IconWarning from 'react-icons/md/warning'

import {
  exec,
  fileDialog,
  fsAccess,
} from 'helpers/fs'

import { updateSettings } from 'actions/settings'

import Button from 'components/Button'
import RadioGroup from 'components/RadioGroup'
import Radio from 'components/RadioGroup/Radio'

export async function getMJMLVersion (location) {
  try {
    await fsAccess(location, fs.constants.R_OK | fs.constants.R_OK | fs.constants.X_OK)
    const { err, stdout } = await exec(`${location} --version`)
    if (err) { return null }
    const version = stdout.trim()
    return version
  } catch (e) {
    return null
  }
}

@connect(state => {
  const { settings } = state
  return {
    mjmlEngine: settings.getIn(['mjml', 'engine'], 'auto'),
    mjmlPath: settings.getIn(['mjml', 'path'], ''),
  }
}, {
  updateSettings,
})
class MJMLEngine extends Component {

  state = {
    mjmlEngine: 'auto',
    mjmlPath: '',
    // unset / checking / valid / invalid
    pathStatus: 'unset',
    // mjml version of local mjml
    mjmlVersion: null,
  }

  componentWillMount () {
    const { mjmlEngine, mjmlPath } = this.props
    this.setState({
      mjmlEngine,
      mjmlPath,
      pathStatus: mjmlPath ? 'checking' : 'unset',
    })
  }

  componentDidMount () {
    if (this.state.mjmlPath) {
      window.requestIdleCallback(() => {
        this.checkEngine()
      })
    }
  }

  componentDidUpdate (prevProps, prevState) {
    if (prevState.mjmlEngine === 'auto' && this.state.mjmlEngine === 'manual') {
      if (this.state.pathStatus === 'checking') {
        window.requestIdleCallback(this.checkEngine)
      }
    }
  }

  componentWillUnmount () {
    this._unmounted = true
  }

  handleChangeEngine = mjmlEngine => {
    this.setState({ mjmlEngine })
    this.debounceSaveSettings()
  }

  handleChangeMJMLPath = p => {
    if (!p) { return this.setState({ pathStatus: 'unset', mjmlPath: '' }) }
    this.setState({ mjmlPath: p })
    this.checkEngine()
    this.debounceSaveSettings()
  }

  handleBrowse = () => {
    const p = fileDialog({
      properties: [
        'openFile',
      ],
    })
    if (!p) { return }
    this.handleChangeMJMLPath(p)
  }

  assignProps = ({ mjmlPath, mjmlEngine }) => this.setState({
    mjmlPath,
    mjmlEngine,
    pathStatus: mjmlPath ? 'checking' : 'unset',
  })

  checkEngine = () => {
    if (this.state.pathStatus !== 'checking') {
      this.setState({ pathStatus: 'checking' })
    }
    this.debounceCheckEngine()
  }

  debounceCheckEngine = debounce(async () => {
    const { mjmlPath } = this.state
    if (!mjmlPath) { return this.setState({ pathStatus: 'unset' }) }
    const version = await getMJMLVersion(mjmlPath)
    if (this._unmounted) { return }
    this.setState({
      pathStatus: version ? 'valid' : 'invalid',
      mjmlVersion: version,
    })
  }, 250)

  debounceSaveSettings = debounce(() => {
    const { mjmlPath, mjmlEngine } = this.state
    const { updateSettings } = this.props
    updateSettings(settings => {
      return settings
        .setIn(['mjml', 'path'], mjmlPath)
        .setIn(['mjml', 'engine'], mjmlEngine)
    })
  }, 500)

  render () {

    const {
      mjmlEngine,
      mjmlPath,
      mjmlVersion,
      pathStatus,
    } = this.state

    return (
      <RadioGroup
        value={mjmlEngine}
        onChange={this.handleChangeEngine}
      >
        <Radio value='auto'>
          {`Use the embedded MJML engine (v${__MJML_VERSION__})`}
        </Radio>
        <Radio value='manual'>

              {'Use a custom MJML engine (slower)'}

            {mjmlEngine === 'manual' && (

                  <input
                    autoFocus
                    className='fg-1'
                    value={mjmlPath}
                    onChange={e => this.handleChangeMJMLPath(e.target.value)}
                    placeholder='MJML path'
                    type='text'
                  />
                  <Button ghost onClick={this.handleBrowse} type='button'>
                    {'Browse'}
                  </Button>

                {pathStatus === 'unset' ? (

                    <IconWarning className='mr-5' />
                    {'No engine set'}

                ) : pathStatus === 'checking' ? (

                    <IconChecking className='mr-5 rotating' />
                    {'Checking...'}

                ) : pathStatus === 'valid' ? (

                    <IconCheck className='mr-5' />
                    {`MJML ${mjmlVersion} located successfully`}

                ) : pathStatus === 'invalid' ? (

                    <IconError className='mr-5' />
                    {'MJML not found at this location'}

                ) : null}

            )}

        </Radio>
      </RadioGroup>
    )
  }

}

export default MJMLEngine
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var util = require('./util')
  , transports = require('./transports')
  , debug = require('debug')('engine-client:socket')
  , EventEmitter = require('./event-emitter')

/**
 * Module exports.
 */

module.exports = Socket;

/**
 * Socket constructor.
 *
 * @param {Object} options
 * @api public
 */

function Socket (opts) {
  if ('string' == typeof opts) {
    var uri = util.parseUri(opts);
    opts = arguments[1] || {};
    opts.host = uri.host;
    opts.secure = uri.protocol == 'https' || uri.protocol == 'wss';
    opts.port = uri.port || (opts.secure ? 443 : 80);
  }

  opts = opts || {};
  this.secure = opts.secure || false;
  this.host = opts.host || opts.hostname || 'localhost';
  this.port = opts.port || 80;
  this.query = opts.query || {};
  this.query.uid = rnd();
  this.upgrade = false !== opts.upgrade;
  this.resource = opts.resource || 'default';
  this.path = (opts.path || '/engine.io').replace(/\/$/, '');
  this.path += '/' + this.resource + '/';
  this.forceJSONP = !!opts.forceJSONP;
  this.timestampParam = opts.timestampParam || 't';
  this.timestampRequests = !!opts.timestampRequests;
  this.flashPath = opts.flashPath || '';
  this.transports = opts.transports || ['polling', 'websocket', 'flashsocket'];
  this.readyState = '';
  this.writeBuffer = [];
  this.policyPort = opts.policyPort || 843;
  this.open();
};

/**
 * Inherits from EventEmitter.
 */

util.inherits(Socket, EventEmitter);

/**
 * Creates transport of the given type.
 *
 * @param {String} transport name
 * @return {Transport}
 * @api private
 */

Socket.prototype.createTransport = function (name) {
  debug('creating transport "%s"', name);
  var query = clone(this.query)
  query.transport = name;

  if (this.id) {
    query.sid = this.id;
  }

  var transport = new transports[name]({
      host: this.host
    , port: this.port
    , secure: this.secure
    , path: this.path
    , query: query
    , forceJSONP: this.forceJSONP
    , timestampRequests: this.timestampRequests
    , timestampParam: this.timestampParam
    , flashPath: this.flashPath
    , policyPort: this.policyPort
  });

  return transport;
};

function clone (obj) {
  var o = {};
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
      o[i] = obj[i];
    }
  }
  return o;
}

/**
 * Initializes transport to use and starts probe.
 *
 * @api private
 */

Socket.prototype.open = function () {
  this.readyState = 'opening';
  var transport = this.createTransport(this.transports[0]);
  transport.open();
  this.setTransport(transport);
};

/**
 * Sets the current transport. Disables the existing one (if any).
 *
 * @api private
 */

Socket.prototype.setTransport = function (transport) {
  var self = this;

  if (this.transport) {
    debug('clearing existing transport');
    this.transport.removeAllListeners();
  }

  // set up transport
  this.transport = transport;

  // set up transport listeners
  transport
    .on('drain', function () {
      self.flush();
    })
    .on('packet', function (packet) {
      self.onPacket(packet);
    })
    .on('error', function (e) {
      self.onError(e);
    })
    .on('close', function () {
      self.onClose('transport close');
    })
};

/**
 * Probes a transport.
 *
 * @param {String} transport name
 * @api private
 */

Socket.prototype.probe = function (name) {
  debug('probing transport "%s"', name);
  var transport = this.createTransport(name, { probe: 1 })
    , self = this

  transport.once('open', function () {
    debug('probe transport "%s" opened', name);
    transport.send([{ type: 'ping', data: 'probe' }]);
    transport.once('packet', function (msg) {
      if ('pong' == msg.type && 'probe' == msg.data) {
        debug('probe transport "%s" pong', name);
        self.upgrading = true;
        self.emit('upgrading', transport);

        debug('pausing current transport "%s"', self.transport.name);
        self.transport.pause(function () {
          if ('closed' == self.readyState || 'closing' == self.readyState) return;
          debug('changing transport and sending upgrade packet');
          self.emit('upgrade', transport);
          self.setTransport(transport);
          transport.send([{ type: 'upgrade' }]);
          transport = null;
          self.upgrading = false;
          self.flush();
        });
      } else {
        debug('probe transport "%s" failed', name);
        var err = new Error('probe error');
        err.transport = transport.name;
        self.emit('error', err);
      }
    });
  });

  transport.open();

  this.once('close', function () {
    if (transport) {
      debug('socket closed prematurely - aborting probe');
      transport.close();
      transport = null;
    }
  });

  this.once('upgrading', function (to) {
    if (transport && to.name != transport.name) {
      debug('"%s" works - aborting "%s"', to.name, transport.name);
      transport.close();
      transport = null;
    }
  });
};

/**
 * Called when connection is deemed open.
 *
 * @api public
 */

Socket.prototype.onOpen = function () {
  debug('socket open');
  this.readyState = 'open';
  this.emit('open');
  this.onopen && this.onopen.call(this);
  this.flush();

  if (this.upgrade && this.transport.pause) {
    debug('starting upgrade probes');
    for (var i = 0, l = this.upgrades.length; i < l; i++) {
      this.probe(this.upgrades[i]);
    }
  }
};

/**
 * Handles a packet.
 *
 * @api private
 */

Socket.prototype.onPacket = function (packet) {
  if ('opening' == this.readyState || 'open' == this.readyState) {
    debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
    switch (packet.type) {
      case 'open':
        this.onHandshake(util.parseJSON(packet.data));
        break;

      case 'pong':
        this.ping();
        break;

      case 'error':
        var err = new Error('server error');
        err.code = packet.data;
        this.emit('error', err);
        break;

      case 'message':
        this.emit('message', packet.data);
        var event = { data: packet.data };
        event.toString = function () {
          return packet.data;
        }
        this.onmessage && this.onmessage.call(this, event);
        break;
    }
  } else {
    debug('packet received with socket readyState "%s"', this.readyState);
  }
};

/**
 * Called upon handshake completion.
 *
 * @param {Object} handshake obj
 * @api private
 */

Socket.prototype.onHandshake = function (data) {
  this.emit('handshake', data);
  this.id = data.sid;
  this.transport.query.sid = data.sid;
  this.upgrades = data.upgrades;
  this.pingInterval = data.pingInterval;
  this.pingTimeout = data.pingTimeout;
  this.onOpen();
  this.ping();
};

/**
 * Pings server every `this.pingInterval` and expects response
 * within `this.pingTimeout` or closes connection.
 *
 * @api private
 */

Socket.prototype.ping = function () {
  var self = this;
  clearTimeout(self.pingIntervalTimer);
  clearTimeout(self.pingTimeoutTimer);
  self.pingIntervalTimer = setTimeout(function () {
    debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
    self.emit('heartbeat');
    self.sendPacket('ping');
    clearTimeout(self.pingTimeoutTimer);
    self.pingTimeoutTimer = setTimeout(function () {
      self.onClose('ping timeout');
    }, self.pingTimeout);
  }, self.pingInterval);
};

/**
 * Flush write buffers.
 *
 * @api private
 */

Socket.prototype.flush = function () {
  if ('closed' != this.readyState && this.transport.writable
    && !this.upgrading && this.writeBuffer.length) {
    debug('flushing %d packets in socket', this.writeBuffer.length);
    this.transport.send(this.writeBuffer);
    this.writeBuffer = [];
  }
};

/**
 * Sends a message.
 *
 * @param {String} message.
 * @return {Socket} for chaining.
 * @api public
 */

Socket.prototype.write =
Socket.prototype.send = function (msg) {
  this.sendPacket('message', msg);
  return this;
};

/**
 * Sends a packet.
 *
 * @param {String} packet type.
 * @param {String} data.
 * @api private
 */

Socket.prototype.sendPacket = function (type, data) {
  var packet = { type: type, data: data };
  this.writeBuffer.push(packet);
  this.flush();
};

/**
 * Closes the connection.
 *
 * @api private
 */

Socket.prototype.close = function () {
  if ('opening' == this.readyState || 'open' == this.readyState) {
    this.onClose('forced close');
    debug('socket closing - telling transport to close');
    this.transport.close();
  }

  return this;
};

/**
 * Called upon transport error
 *
 * @api private
 */

Socket.prototype.onError = function (err) {
  this.emit('error', err);
  this.onClose('transport error', err);
};

/**
 * Called upon transport close.
 *
 * @api private
 */

Socket.prototype.onClose = function (reason, desc) {
  if ('closed' != this.readyState) {
    debug('socket close with reason: "%s"', reason);
    this.readyState = 'closed';
    this.emit('close', reason, desc);
    this.onclose && this.onclose.call(this);
  }
};

/**
 * Generates a random uid.
 *
 * @api private
 */

function rnd () {
  return String(Math.random()).substr(5) + String(Math.random()).substr(5);
}
<commit_msg>socket: reset timeout on any incoming data<commit_after>/**
 * Module dependencies.
 */

var util = require('./util')
  , transports = require('./transports')
  , debug = require('debug')('engine-client:socket')
  , EventEmitter = require('./event-emitter')

/**
 * Module exports.
 */

module.exports = Socket;

/**
 * Socket constructor.
 *
 * @param {Object} options
 * @api public
 */

function Socket (opts) {
  if ('string' == typeof opts) {
    var uri = util.parseUri(opts);
    opts = arguments[1] || {};
    opts.host = uri.host;
    opts.secure = uri.protocol == 'https' || uri.protocol == 'wss';
    opts.port = uri.port || (opts.secure ? 443 : 80);
  }

  opts = opts || {};
  this.secure = opts.secure || false;
  this.host = opts.host || opts.hostname || 'localhost';
  this.port = opts.port || 80;
  this.query = opts.query || {};
  this.query.uid = rnd();
  this.upgrade = false !== opts.upgrade;
  this.resource = opts.resource || 'default';
  this.path = (opts.path || '/engine.io').replace(/\/$/, '');
  this.path += '/' + this.resource + '/';
  this.forceJSONP = !!opts.forceJSONP;
  this.timestampParam = opts.timestampParam || 't';
  this.timestampRequests = !!opts.timestampRequests;
  this.flashPath = opts.flashPath || '';
  this.transports = opts.transports || ['polling', 'websocket', 'flashsocket'];
  this.readyState = '';
  this.writeBuffer = [];
  this.policyPort = opts.policyPort || 843;
  this.open();
};

/**
 * Inherits from EventEmitter.
 */

util.inherits(Socket, EventEmitter);

/**
 * Creates transport of the given type.
 *
 * @param {String} transport name
 * @return {Transport}
 * @api private
 */

Socket.prototype.createTransport = function (name) {
  debug('creating transport "%s"', name);
  var query = clone(this.query)
  query.transport = name;

  if (this.id) {
    query.sid = this.id;
  }

  var transport = new transports[name]({
      host: this.host
    , port: this.port
    , secure: this.secure
    , path: this.path
    , query: query
    , forceJSONP: this.forceJSONP
    , timestampRequests: this.timestampRequests
    , timestampParam: this.timestampParam
    , flashPath: this.flashPath
    , policyPort: this.policyPort
  });

  return transport;
};

function clone (obj) {
  var o = {};
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
      o[i] = obj[i];
    }
  }
  return o;
}

/**
 * Initializes transport to use and starts probe.
 *
 * @api private
 */

Socket.prototype.open = function () {
  this.readyState = 'opening';
  var transport = this.createTransport(this.transports[0]);
  transport.open();
  this.setTransport(transport);
};

/**
 * Sets the current transport. Disables the existing one (if any).
 *
 * @api private
 */

Socket.prototype.setTransport = function (transport) {
  var self = this;

  if (this.transport) {
    debug('clearing existing transport');
    this.transport.removeAllListeners();
  }

  // set up transport
  this.transport = transport;

  // set up transport listeners
  transport
    .on('drain', function () {
      self.flush();
    })
    .on('packet', function (packet) {
      self.onPacket(packet);
    })
    .on('error', function (e) {
      self.onError(e);
    })
    .on('close', function () {
      self.onClose('transport close');
    })
};

/**
 * Probes a transport.
 *
 * @param {String} transport name
 * @api private
 */

Socket.prototype.probe = function (name) {
  debug('probing transport "%s"', name);
  var transport = this.createTransport(name, { probe: 1 })
    , self = this

  transport.once('open', function () {
    debug('probe transport "%s" opened', name);
    transport.send([{ type: 'ping', data: 'probe' }]);
    transport.once('packet', function (msg) {
      if ('pong' == msg.type && 'probe' == msg.data) {
        debug('probe transport "%s" pong', name);
        self.upgrading = true;
        self.emit('upgrading', transport);

        debug('pausing current transport "%s"', self.transport.name);
        self.transport.pause(function () {
          if ('closed' == self.readyState || 'closing' == self.readyState) return;
          debug('changing transport and sending upgrade packet');
          self.emit('upgrade', transport);
          self.setTransport(transport);
          transport.send([{ type: 'upgrade' }]);
          transport = null;
          self.upgrading = false;
          self.flush();
        });
      } else {
        debug('probe transport "%s" failed', name);
        var err = new Error('probe error');
        err.transport = transport.name;
        self.emit('error', err);
      }
    });
  });

  transport.open();

  this.once('close', function () {
    if (transport) {
      debug('socket closed prematurely - aborting probe');
      transport.close();
      transport = null;
    }
  });

  this.once('upgrading', function (to) {
    if (transport && to.name != transport.name) {
      debug('"%s" works - aborting "%s"', to.name, transport.name);
      transport.close();
      transport = null;
    }
  });
};

/**
 * Called when connection is deemed open.
 *
 * @api public
 */

Socket.prototype.onOpen = function () {
  debug('socket open');
  this.readyState = 'open';
  this.emit('open');
  this.onopen && this.onopen.call(this);
  this.flush();

  if (this.upgrade && this.transport.pause) {
    debug('starting upgrade probes');
    for (var i = 0, l = this.upgrades.length; i < l; i++) {
      this.probe(this.upgrades[i]);
    }
  }
};

/**
 * Handles a packet.
 *
 * @api private
 */

Socket.prototype.onPacket = function (packet) {
  if ('opening' == this.readyState || 'open' == this.readyState) {
    debug('socket receive: type "%s", data "%s"', packet.type, packet.data);

    // Socket is live - any packet counts
    this.emit('heartbeat');
    switch (packet.type) {
      case 'open':
        this.onHandshake(util.parseJSON(packet.data));
        break;

      case 'pong':
        this.ping();
        break;

      case 'error':
        var err = new Error('server error');
        err.code = packet.data;
        this.emit('error', err);
        break;

      case 'message':
        this.emit('message', packet.data);
        var event = { data: packet.data };
        event.toString = function () {
          return packet.data;
        }
        this.onmessage && this.onmessage.call(this, event);
        break;
    }
  } else {
    debug('packet received with socket readyState "%s"', this.readyState);
  }
};

/**
 * Called upon handshake completion.
 *
 * @param {Object} handshake obj
 * @api private
 */

Socket.prototype.onHandshake = function (data) {
  this.emit('handshake', data);
  this.id = data.sid;
  this.transport.query.sid = data.sid;
  this.upgrades = data.upgrades;
  this.pingInterval = data.pingInterval;
  this.pingTimeout = data.pingTimeout;
  this.onOpen();
  this.ping();

  // Prolong liveness of socket on heartbeat
  this.removeListener('heartbeat', this.onHeartbeat);
  this.on('heartbeat', this.onHeartbeat);
};

/**
 * Resets ping timeout.
 *
 * @api private
 */
Socket.prototype.onHeartbeat = function onHeartbeat() {
  var self = this;

  clearTimeout(self.pingTimeoutTimer);
  self.pingTimeoutTimer = setTimeout(function () {
    if ('closed' == self.readyState) return;
    self.onClose('ping timeout');
  }, self.pingTimeout);
};

/**
 * Pings server every `this.pingInterval` and expects response
 * within `this.pingTimeout` or closes connection.
 *
 * @api private
 */

Socket.prototype.ping = function () {
  var self = this;
  clearTimeout(self.pingIntervalTimer);
  clearTimeout(self.pingTimeoutTimer);
  self.pingIntervalTimer = setTimeout(function () {
    debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
    self.emit('heartbeat');
    self.sendPacket('ping');
  }, self.pingInterval);
};

/**
 * Flush write buffers.
 *
 * @api private
 */

Socket.prototype.flush = function () {
  if ('closed' != this.readyState && this.transport.writable
    && !this.upgrading && this.writeBuffer.length) {
    debug('flushing %d packets in socket', this.writeBuffer.length);
    this.transport.send(this.writeBuffer);
    this.writeBuffer = [];
  }
};

/**
 * Sends a message.
 *
 * @param {String} message.
 * @return {Socket} for chaining.
 * @api public
 */

Socket.prototype.write =
Socket.prototype.send = function (msg) {
  this.sendPacket('message', msg);
  return this;
};

/**
 * Sends a packet.
 *
 * @param {String} packet type.
 * @param {String} data.
 * @api private
 */

Socket.prototype.sendPacket = function (type, data) {
  var packet = { type: type, data: data };
  this.writeBuffer.push(packet);
  this.flush();
};

/**
 * Closes the connection.
 *
 * @api private
 */

Socket.prototype.close = function () {
  if ('opening' == this.readyState || 'open' == this.readyState) {
    this.onClose('forced close');
    debug('socket closing - telling transport to close');
    this.transport.close();
  }

  return this;
};

/**
 * Called upon transport error
 *
 * @api private
 */

Socket.prototype.onError = function (err) {
  this.emit('error', err);
  this.onClose('transport error', err);
};

/**
 * Called upon transport close.
 *
 * @api private
 */

Socket.prototype.onClose = function (reason, desc) {
  if ('closed' != this.readyState) {
    debug('socket close with reason: "%s"', reason);
    this.readyState = 'closed';
    this.emit('close', reason, desc);
    this.onclose && this.onclose.call(this);
  }
};

/**
 * Generates a random uid.
 *
 * @api private
 */

function rnd () {
  return String(Math.random()).substr(5) + String(Math.random()).substr(5);
}
<|endoftext|>


<commit_before>var utility = require('./utility');
var isSignificant = utility.isSignificant;
var reduceMatch = utility.reduceMatch;
var constants = require('./constants.json');
var aggregator = require('./aggregator');
var async = require('async');
var db = require('./db');
var r = require('./redis');
var redis = r.client;
var zlib = require('zlib');
var computeMatchData = require('./compute').computeMatchData;
module.exports = function updatePlayerCaches(match, options, cb) {
    //insert the match into db, then based on the existing document determine whether to do aggregations
    db.matches.findAndModify({
        match_id: match.match_id
    }, {
        $set: match
    }, {
        //don't upsert if inserting skill data
        upsert: options.type !== "skill",
        //explicitly declare we want the pre-modification document
        "new": false
    }, function(err, doc) {
        if (err) {
            return cb(err);
        }
        if (options.type === "skill" && !doc) {
            //we didn't add skill data, return immediately
            return cb(err);
        }
        async.each(match.players || options.players, function(p, cb) {
                redis.get("player:" + p.account_id, function(err, result) {
                    //if player cache doesn't exist, skip
                    var cache = result && !err ? JSON.parse(zlib.inflateSync(new Buffer(result, 'base64'))) : null;
                    if (cache) {
                        var match_copy = JSON.parse(JSON.stringify(match));
                        if (options.type !== "skill") {
                            //m.players[0] should be this player
                            //m.all_players should be all players
                            //duplicate this data into a copy to avoid corrupting original match object
                            match_copy.all_players = match.players.slice(0);
                            match_copy.players = [p];
                            //some data fields require computeMatchData in order to aggregate correctly
                            computeMatchData(match_copy);
                            //check for doc.players containing this match_id
                            //we could need to run aggregation if we are reinserting a match but this player used to be anonymous
                            var playerInMatch = doc && doc.players && doc.players.some(function(player) {
                                return player.account_id === p.account_id;
                            });
                            var reInsert = doc && options.type === "api" && playerInMatch;
                            //determine if we're reparsing this match
                            var reParse = doc && doc.parsed_data && options.type === "parsed";
                            if (!reInsert && !reParse && cache.aggData) {
                                //do aggregations on fields based on type
                                cache.aggData = aggregator([match_copy], options.type, cache.aggData);
                            }
                            //reduce match for display
                            //TODO if we want to cache full data, we don't want to get rid of player.parsedPlayer
                            reduceMatch(match_copy);
                            var orig = cache.data[match_copy.match_id];
                            if (!orig) {
                                cache.data[match_copy.match_id] = match_copy;
                            }
                            else {
                                //check if we can update old values
                                //use new parse status if 2
                                orig.parse_status = match_copy.parse_status === 2 ? match_copy.parse_status : orig.parse_status;
                                //use new players[] if parse_status===2
                                orig.players = match_copy.parse_status === 2 ? match_copy.players : orig.players;
                            }
                        }
                        else {
                            //update only, if type===skill
                            if (cache.data[match_copy.match_id]) {
                                cache.data[match_copy.match_id].skill = match_copy.skill;
                            }
                        }
                        redis.ttl("player:" + p.account_id, function(err, ttl) {
                            if (err) {
                                return cb(err);
                            }
                            redis.setex("player:" + p.account_id, ttl, zlib.deflateSync(JSON.stringify(cache)).toString('base64'));
                        });
                    }
                    /*
                    //temporarily disable inserting new players into db
                    db.players.update({
                        account_id: p.account_id
                    }, {
                        $set: {
                            account_id: p.account_id,
                        }
                    }, {
                        upsert: true
                    }, function(err) {
                        if (err) {
                            return cb(err);
                        }
                        cb(err);
                    });
                    */
                    cb(err);
                });
            },
            //done with all 10 players
            function(err) {
                if (err) {
                    return cb(err);
                }
                //clear the cache for this match
                redis.del("match:" + match.match_id, function(err) {
                    return cb(err, doc);
                });
            });
    });
};<commit_msg>cast ttl to number<commit_after>var utility = require('./utility');
var isSignificant = utility.isSignificant;
var reduceMatch = utility.reduceMatch;
var constants = require('./constants.json');
var aggregator = require('./aggregator');
var async = require('async');
var db = require('./db');
var r = require('./redis');
var redis = r.client;
var zlib = require('zlib');
var computeMatchData = require('./compute').computeMatchData;
module.exports = function updatePlayerCaches(match, options, cb) {
    //insert the match into db, then based on the existing document determine whether to do aggregations
    db.matches.findAndModify({
        match_id: match.match_id
    }, {
        $set: match
    }, {
        //don't upsert if inserting skill data
        upsert: options.type !== "skill",
        //explicitly declare we want the pre-modification document
        "new": false
    }, function(err, doc) {
        if (err) {
            return cb(err);
        }
        if (options.type === "skill" && !doc) {
            //we didn't add skill data, return immediately
            return cb(err);
        }
        async.each(match.players || options.players, function(p, cb) {
                redis.get("player:" + p.account_id, function(err, result) {
                    //if player cache doesn't exist, skip
                    var cache = result && !err ? JSON.parse(zlib.inflateSync(new Buffer(result, 'base64'))) : null;
                    if (cache) {
                        var match_copy = JSON.parse(JSON.stringify(match));
                        if (options.type !== "skill") {
                            //m.players[0] should be this player
                            //m.all_players should be all players
                            //duplicate this data into a copy to avoid corrupting original match object
                            match_copy.all_players = match.players.slice(0);
                            match_copy.players = [p];
                            //some data fields require computeMatchData in order to aggregate correctly
                            computeMatchData(match_copy);
                            //check for doc.players containing this match_id
                            //we could need to run aggregation if we are reinserting a match but this player used to be anonymous
                            var playerInMatch = doc && doc.players && doc.players.some(function(player) {
                                return player.account_id === p.account_id;
                            });
                            var reInsert = doc && options.type === "api" && playerInMatch;
                            //determine if we're reparsing this match
                            var reParse = doc && doc.parsed_data && options.type === "parsed";
                            if (!reInsert && !reParse && cache.aggData) {
                                //do aggregations on fields based on type
                                cache.aggData = aggregator([match_copy], options.type, cache.aggData);
                            }
                            //reduce match for display
                            //TODO if we want to cache full data, we don't want to get rid of player.parsedPlayer
                            reduceMatch(match_copy);
                            var orig = cache.data[match_copy.match_id];
                            if (!orig) {
                                cache.data[match_copy.match_id] = match_copy;
                            }
                            else {
                                //check if we can update old values
                                //use new parse status if 2
                                orig.parse_status = match_copy.parse_status === 2 ? match_copy.parse_status : orig.parse_status;
                                //use new players[] if parse_status===2
                                orig.players = match_copy.parse_status === 2 ? match_copy.players : orig.players;
                            }
                        }
                        else {
                            //update only, if type===skill
                            if (cache.data[match_copy.match_id]) {
                                cache.data[match_copy.match_id].skill = match_copy.skill;
                            }
                        }
                        redis.ttl("player:" + p.account_id, function(err, ttl) {
                            if (err) {
                                return cb(err);
                            }
                            redis.setex("player:" + p.account_id, Number(ttl), zlib.deflateSync(JSON.stringify(cache)).toString('base64'));
                        });
                    }
                    /*
                    //temporarily disable inserting new players into db
                    db.players.update({
                        account_id: p.account_id
                    }, {
                        $set: {
                            account_id: p.account_id,
                        }
                    }, {
                        upsert: true
                    }, function(err) {
                        if (err) {
                            return cb(err);
                        }
                        cb(err);
                    });
                    */
                    cb(err);
                });
            },
            //done with all 10 players
            function(err) {
                if (err) {
                    return cb(err);
                }
                //clear the cache for this match
                redis.del("match:" + match.match_id, function(err) {
                    return cb(err, doc);
                });
            });
    });
};<|endoftext|>


<commit_before>/* @flow */

import * as t from "babel-types";

export function AnyTypeAnnotation() {
  this.push("any");
}

export function ArrayTypeAnnotation(node: Object) {
  this.print(node.elementType, node);
  this.push("[");
  this.push("]");
}

export function BooleanTypeAnnotation() {
  this.push("bool");
}

export function BooleanLiteralTypeAnnotation(node: Object) {
  this.push(node.value ? "true" : "false");
}

export function NullLiteralTypeAnnotation() {
  this.push("null");
}

export function DeclareClass(node: Object) {
  this.push("declare class ");
  this._interfaceish(node);
}

export function DeclareFunction(node: Object) {
  this.push("declare function ");
  this.print(node.id, node);
  this.print(node.id.typeAnnotation.typeAnnotation, node);
  this.semicolon();
}

export function DeclareModule(node: Object) {
  this.push("declare module ");
  this.print(node.id, node);
  this.space();
  this.print(node.body, node);
}

export function DeclareVariable(node: Object) {
  this.push("declare var ");
  this.print(node.id, node);
  this.print(node.id.typeAnnotation, node);
  this.semicolon();
}

export function ExistentialTypeParam() {
  this.push("*");
}

export function FunctionTypeAnnotation(node: Object, parent: Object) {
  this.print(node.typeParameters, node);
  this.push("(");
  this.printList(node.params, node);

  if (node.rest) {
    if (node.params.length) {
      this.push(",");
      this.space();
    }
    this.push("...");
    this.print(node.rest, node);
  }

  this.push(")");

  // this node type is overloaded, not sure why but it makes it EXTREMELY annoying
  if (parent.type === "ObjectTypeProperty" || parent.type === "ObjectTypeCallProperty" || parent.type === "DeclareFunction") {
    this.push(":");
  } else {
    this.space();
    this.push("=>");
  }

  this.space();
  this.print(node.returnType, node);
}

export function FunctionTypeParam(node: Object) {
  this.print(node.name, node);
  if (node.optional) this.push("?");
  this.push(":");
  this.space();
  this.print(node.typeAnnotation, node);
}

export function InterfaceExtends(node: Object) {
  this.print(node.id, node);
  this.print(node.typeParameters, node);
}

export { InterfaceExtends as ClassImplements, InterfaceExtends as GenericTypeAnnotation };

export function _interfaceish(node: Object) {
  this.print(node.id, node);
  this.print(node.typeParameters, node);
  if (node.extends.length) {
    this.push(" extends ");
    this.printJoin(node.extends, node, { separator: ", " });
  }
  if (node.mixins.length) {
    this.push(" mixins ");
    this.printJoin(node.mixins, node, { separator: ", " });
  }
  this.space();
  this.print(node.body, node);
}

export function InterfaceDeclaration(node: Object) {
  this.push("interface ");
  this._interfaceish(node);
}

export function IntersectionTypeAnnotation(node: Object) {
  this.printJoin(node.types, node, { separator: " & " });
}

export function MixedTypeAnnotation() {
  this.push("mixed");
}

export function NullableTypeAnnotation(node: Object) {
  this.push("?");
  this.print(node.typeAnnotation, node);
}

export { NumericLiteral as NumericLiteralTypeAnnotation } from "./types";

export function NumberTypeAnnotation() {
  this.push("number");
}

export function StringLiteralTypeAnnotation(node: Object) {
  this.push(this._stringLiteral(node.value));
}

export function StringTypeAnnotation() {
  this.push("string");
}

export function TupleTypeAnnotation(node: Object) {
  this.push("[");
  this.printJoin(node.types, node, { separator: ", " });
  this.push("]");
}

export function TypeofTypeAnnotation(node: Object) {
  this.push("typeof ");
  this.print(node.argument, node);
}

export function TypeAlias(node: Object) {
  this.push("type ");
  this.print(node.id, node);
  this.print(node.typeParameters, node);
  this.space();
  this.push("=");
  this.space();
  this.print(node.right, node);
  this.semicolon();
}

export function TypeAnnotation(node: Object) {
  this.push(":");
  this.space();
  if (node.optional) this.push("?");
  this.print(node.typeAnnotation, node);
}

export function TypeParameterInstantiation(node: Object) {
  this.push("<");
  this.printJoin(node.params, node, {
    separator: ", ",
    iterator: (node: Object) => {
      this.print(node.typeAnnotation, node);
    }
  });
  this.push(">");
}

export { TypeParameterInstantiation as TypeParameterDeclaration };

export function ObjectTypeAnnotation(node: Object) {
  this.push("{");
  let props = node.properties.concat(node.callProperties, node.indexers);

  if (props.length) {
    this.space();

    this.printJoin(props, node, {
      separator: false,
      indent: true,
      iterator: () => {
        if (props.length !== 1) {
          this.semicolon();
          this.space();
        }
      }
    });

    this.space();
  }

  this.push("}");
}

export function ObjectTypeCallProperty(node: Object) {
  if (node.static) this.push("static ");
  this.print(node.value, node);
}

export function ObjectTypeIndexer(node: Object) {
  if (node.static) this.push("static ");
  this.push("[");
  this.print(node.id, node);
  this.push(":");
  this.space();
  this.print(node.key, node);
  this.push("]");
  this.push(":");
  this.space();
  this.print(node.value, node);
}

export function ObjectTypeProperty(node: Object) {
  if (node.static) this.push("static ");
  this.print(node.key, node);
  if (node.optional) this.push("?");
  if (!t.isFunctionTypeAnnotation(node.value)) {
    this.push(":");
    this.space();
  }
  this.print(node.value, node);
}

export function QualifiedTypeIdentifier(node: Object) {
  this.print(node.qualification, node);
  this.push(".");
  this.print(node.id, node);
}

export function UnionTypeAnnotation(node: Object) {
  this.printJoin(node.types, node, { separator: " | " });
}

export function TypeCastExpression(node: Object) {
  this.push("(");
  this.print(node.expression, node);
  this.print(node.typeAnnotation, node);
  this.push(")");
}

export function VoidTypeAnnotation() {
  this.push("void");
}
<commit_msg>Don't generate class mixins if prop doesn't exist<commit_after>/* @flow */

import * as t from "babel-types";

export function AnyTypeAnnotation() {
  this.push("any");
}

export function ArrayTypeAnnotation(node: Object) {
  this.print(node.elementType, node);
  this.push("[");
  this.push("]");
}

export function BooleanTypeAnnotation() {
  this.push("bool");
}

export function BooleanLiteralTypeAnnotation(node: Object) {
  this.push(node.value ? "true" : "false");
}

export function NullLiteralTypeAnnotation() {
  this.push("null");
}

export function DeclareClass(node: Object) {
  this.push("declare class ");
  this._interfaceish(node);
}

export function DeclareFunction(node: Object) {
  this.push("declare function ");
  this.print(node.id, node);
  this.print(node.id.typeAnnotation.typeAnnotation, node);
  this.semicolon();
}

export function DeclareModule(node: Object) {
  this.push("declare module ");
  this.print(node.id, node);
  this.space();
  this.print(node.body, node);
}

export function DeclareVariable(node: Object) {
  this.push("declare var ");
  this.print(node.id, node);
  this.print(node.id.typeAnnotation, node);
  this.semicolon();
}

export function ExistentialTypeParam() {
  this.push("*");
}

export function FunctionTypeAnnotation(node: Object, parent: Object) {
  this.print(node.typeParameters, node);
  this.push("(");
  this.printList(node.params, node);

  if (node.rest) {
    if (node.params.length) {
      this.push(",");
      this.space();
    }
    this.push("...");
    this.print(node.rest, node);
  }

  this.push(")");

  // this node type is overloaded, not sure why but it makes it EXTREMELY annoying
  if (parent.type === "ObjectTypeProperty" || parent.type === "ObjectTypeCallProperty" || parent.type === "DeclareFunction") {
    this.push(":");
  } else {
    this.space();
    this.push("=>");
  }

  this.space();
  this.print(node.returnType, node);
}

export function FunctionTypeParam(node: Object) {
  this.print(node.name, node);
  if (node.optional) this.push("?");
  this.push(":");
  this.space();
  this.print(node.typeAnnotation, node);
}

export function InterfaceExtends(node: Object) {
  this.print(node.id, node);
  this.print(node.typeParameters, node);
}

export { InterfaceExtends as ClassImplements, InterfaceExtends as GenericTypeAnnotation };

export function _interfaceish(node: Object) {
  this.print(node.id, node);
  this.print(node.typeParameters, node);
  if (node.extends.length) {
    this.push(" extends ");
    this.printJoin(node.extends, node, { separator: ", " });
  }
  if (node.mixins && node.mixins.length) {
    this.push(" mixins ");
    this.printJoin(node.mixins, node, { separator: ", " });
  }
  this.space();
  this.print(node.body, node);
}

export function InterfaceDeclaration(node: Object) {
  this.push("interface ");
  this._interfaceish(node);
}

export function IntersectionTypeAnnotation(node: Object) {
  this.printJoin(node.types, node, { separator: " & " });
}

export function MixedTypeAnnotation() {
  this.push("mixed");
}

export function NullableTypeAnnotation(node: Object) {
  this.push("?");
  this.print(node.typeAnnotation, node);
}

export { NumericLiteral as NumericLiteralTypeAnnotation } from "./types";

export function NumberTypeAnnotation() {
  this.push("number");
}

export function StringLiteralTypeAnnotation(node: Object) {
  this.push(this._stringLiteral(node.value));
}

export function StringTypeAnnotation() {
  this.push("string");
}

export function TupleTypeAnnotation(node: Object) {
  this.push("[");
  this.printJoin(node.types, node, { separator: ", " });
  this.push("]");
}

export function TypeofTypeAnnotation(node: Object) {
  this.push("typeof ");
  this.print(node.argument, node);
}

export function TypeAlias(node: Object) {
  this.push("type ");
  this.print(node.id, node);
  this.print(node.typeParameters, node);
  this.space();
  this.push("=");
  this.space();
  this.print(node.right, node);
  this.semicolon();
}

export function TypeAnnotation(node: Object) {
  this.push(":");
  this.space();
  if (node.optional) this.push("?");
  this.print(node.typeAnnotation, node);
}

export function TypeParameterInstantiation(node: Object) {
  this.push("<");
  this.printJoin(node.params, node, {
    separator: ", ",
    iterator: (node: Object) => {
      this.print(node.typeAnnotation, node);
    }
  });
  this.push(">");
}

export { TypeParameterInstantiation as TypeParameterDeclaration };

export function ObjectTypeAnnotation(node: Object) {
  this.push("{");
  let props = node.properties.concat(node.callProperties, node.indexers);

  if (props.length) {
    this.space();

    this.printJoin(props, node, {
      separator: false,
      indent: true,
      iterator: () => {
        if (props.length !== 1) {
          this.semicolon();
          this.space();
        }
      }
    });

    this.space();
  }

  this.push("}");
}

export function ObjectTypeCallProperty(node: Object) {
  if (node.static) this.push("static ");
  this.print(node.value, node);
}

export function ObjectTypeIndexer(node: Object) {
  if (node.static) this.push("static ");
  this.push("[");
  this.print(node.id, node);
  this.push(":");
  this.space();
  this.print(node.key, node);
  this.push("]");
  this.push(":");
  this.space();
  this.print(node.value, node);
}

export function ObjectTypeProperty(node: Object) {
  if (node.static) this.push("static ");
  this.print(node.key, node);
  if (node.optional) this.push("?");
  if (!t.isFunctionTypeAnnotation(node.value)) {
    this.push(":");
    this.space();
  }
  this.print(node.value, node);
}

export function QualifiedTypeIdentifier(node: Object) {
  this.print(node.qualification, node);
  this.push(".");
  this.print(node.id, node);
}

export function UnionTypeAnnotation(node: Object) {
  this.printJoin(node.types, node, { separator: " | " });
}

export function TypeCastExpression(node: Object) {
  this.push("(");
  this.print(node.expression, node);
  this.print(node.typeAnnotation, node);
  this.push(")");
}

export function VoidTypeAnnotation() {
  this.push("void");
}
<|endoftext|>


<commit_before>$(document).ready(function(){

    var width = $(window).width();
    if (width <= 768) {

        var scroll_tolerance = 20;

        // Navbar Layout
        $('.nav_menu').css('position', 'fixed').css('zIndex', '10');

        // Content Top Spacer
        $('#stream').css('paddingTop', $('.nav_menu').outerHeight() + 'px');

        // StartPos
        var position = $(window).scrollTop();

        $(window).scroll(function () {

            var scroll = $(window).scrollTop();
            var diff = Math.abs(position - $(window).scrollTop());

            if (scroll > position) {
                $('.nav_menu').slideUp();
            } else {

                if (diff > scroll_tolerance) {
                    $('.nav_menu').slideDown();
                }
                else if (scroll < 20) {
                    $('.nav_menu').slideDown();
                }

            }

            position = scroll;

        });

    }

});<commit_msg>Prevent menu slidedown on scrolljumps.<commit_after>$(document).ready(function() {

    var width = $(window).width();
    if (width <= 768) {

        var scroll_tolerance = 20;

        // Navbar Layout
        $('.nav_menu').css('position', 'fixed').css('zIndex', '10');

        // Content Top Spacer
        $('#stream').css('paddingTop', $('.nav_menu').outerHeight() + 'px');

        // StartPos
        var position = $(window).scrollTop();

        // Prevent scrolljumps while switching articles
        var scrollcounter = 0;
        var scrolltimer;

        $(window).scroll(function() {

            var scroll = $(window).scrollTop();
            var diff = Math.abs(position - $(window).scrollTop());

            // Prevent scrolljumps while switching articles
            if (scrolltimer) {
                scrollcounter++;
                clearTimeout(scrolltimer);
            }
            scrolltimer = setTimeout(function() {
                scrollcounter=0;
            }, 1000);

            // Scrolling down
            if (scroll > position) {
                $('.nav_menu').slideUp();
            }
            else {

                // Scrolled up and no jump
                if (diff > scroll_tolerance && scrollcounter > 5) {
                    $('.nav_menu').slideDown();
                }

                // Reached top
                else if (scroll < 20) {
                    $('.nav_menu').slideDown();
                }

            }

            position = scroll;

        });

    }

});<|endoftext|>