<commit_before>module.exports = (function () {

    var _tokenRefreshTimeout = null

    // Default

    function _beforeEach (transition) {
        var auth = _toArray(transition.to.auth)

        if (auth && (auth === true || auth.constructor === Array)) {
            if (!this.check()) {
                this.$router.replace(this.getOption('authRedirect'))
            } else if (auth.constructor === Array && !_compare(auth, _toArray(this.user()[this.getOption('rolesVar')]))) {
                this.$router.replace(this.getOption('forbiddenRedirect'))
            } else {
                return transition.next()
            }
        } else if (auth === false && this.check()) {
            this.$router.replace(this.getOption('notFoundRedirect'))
        } else {
            return transition.next()
        }
    }

    function _userData (res) {
        res = res.json();

        return res.data || res
    }

    function _cookieDomain () {
        return window.location.hostname
    }

    function _invalidToken (res) {
        if (res.status === 401) {
            this.logout(this.getOption('logoutRedirect'))
        }
    }

    // Utils

    function _getUrl () {
        var port = window.location.port

        return window.location.protocol + '//' + window.location.hostname + (port ? ':' + port : '')
    }

    function _compare (one, two) {
        var i, ii

        one = _toArray(one)
        two = _toArray(two)

        if (one.constructor !== Array || two.constructor !== Array) {
            return false
        }

        for (i = 0, ii = one.length; i < ii; i++) {
            if (two.indexOf(one[i]) >= 0) {
                return true
            }
        }

        return false
    }

    function _toArray (val) {
        return (typeof val) === 'string' ? [val] : val
    }

    function _http (options) {

        // Version 0.8.0
        this.$http[options.method || 'get'](options.url, options.data).then(options.success, options.error);
    }

    function _interceptor (Vue, req, res) {

        // Version 0.8.0
        Vue.http.interceptors.push((request, next) => {
            if (req) { req.call(this, request); }

            next((response) => {
                if (res) { res.call(this, response); }
            });
        });

        // // Version before Promise implementation.
        // else {
        //     Vue.http.interceptors.push({
        //         request (request) {
        //             if (req) { req.call(this, request); }
        //         },

        //         response (response) {
        //             if (res) { res.call(this, response); }
        //         }
        //     });
        // }
    }

    // Remember Me

    function _setCookie (name, value, timeOffset) {
        var domain = this.getOption('cookieDomain').call(this),
            expires = (new Date((new Date()).getTime() + timeOffset)).toUTCString()

        document.cookie = (name + '=' + value + '; domain=' + domain + '; path=/;' + (timeOffset ? ' expires=' + expires + ';' : ''))
    }

    function _setRememberMeCookie (rememberMe) {
        _setCookie.call(this,
            'rememberMe',
            rememberMe === true ? 'true' : 'false',
            rememberMe === true ? 12096e5 : undefined
        )
    }

    function _removeRememberMeCookie () {
        _setCookie.call(this, 'rememberMe', 'false', -12096e5)
    }

    // Token

    function _setToken (token) {
        if (token) {
            localStorage.setItem((this.other() ? 'login-as-' : '') + this.getOption('tokenName'), token)
        }
    }

    function _getToken () {
        return localStorage.getItem((this.other() ? 'login-as-' : '') + this.getOption('tokenName'))
    }

    function _removeToken () {
        localStorage.removeItem((this.other() ? 'login-as-' : '') + this.getOption('tokenName'))
    }

    function _refreshToken () {
        var _this = this;

        if (_getToken.call(this) && this.getOption('tokenType') === 'jwt') {
            _http.call(this, {
                url: this.getOption('tokenUrl'),
                method: 'get',
                success: () => {
                    var tokenJSON = _decodeToken(_getToken.call(_this)),
                        expireTime = _getTokenExpirationDate(tokenJSON).valueOf(),
                        nowTime = new Date().valueOf(),
                        offsetTime = this.getOption('tokenTimeoutOffset'),
                        timeout = expireTime - nowTime - offsetTime;

                    clearTimeout(_tokenRefreshTimeout);

                    _tokenRefreshTimeout = setTimeout(function () {
                        _refreshToken.call(_this);
                    }, timeout);
                }
            });
        }
    }

    // Token util

    function _urlBase64Decode (str) {
        let output = str.replace(/-/g, '+').replace(/_/g, '/')

        switch (output.length % 4) {
            case 0: { break }
            case 2: { output += '=='; break }
            case 3: { output += '='; break }
            default: {
                console.log('Illegal base64url string!')
            }
        }

        return decodeURIComponent(escape(window.atob(output))) // polifyll https://github.com/davidchambers/Base64.js
    }

    function _decodeToken (token) {
        if ( ! token) { return; }

        let parts = token.split('.')

        if (parts.length !== 3) {
            throw new Error('JWT must have 3 parts')
        }

        let decoded = _urlBase64Decode(parts[1])

        if ( ! decoded) {
            throw new Error('Cannot decode the token')
        }

        return JSON.parse(decoded)
    }

    function _getTokenExpirationDate (tokenJSON) {
        if ( ! tokenJSON || ! tokenJSON.exp) { return }

        let d = new Date(0) // The 0 here is the key, which sets the date to the epoch

        d.setUTCSeconds(tokenJSON.exp)

        return d
    }

    // Router

    function _setRoute (route, router) {
        this.$route = route
        this.$router = router
    }

    // Auth

    function _login (path, data, rememberMe, redirectUrl, options) {
        options = options || {}

        _http.call(this, {
            url: path,
            method: 'post',
            data: data,
            success: (res) => {
                var _this = this

                _setRememberMeCookie.call(this, rememberMe);

                _setToken.call(this, res.json()[this.getOption('tokenVar')]);

                this.authenticated = null;

                this.fetch(function () {
                    if (options.success) {
                        options.success.call(_this, res);
                    }

                    if (redirectUrl && _this.check()) {
                        _this.$router.go(redirectUrl);
                    }
                });
            },
            error: (res) => {
                if (options.error) {
                    options.error.call(this, res);
                }
            }
        });
    }

    function _oauth2 (type, data, rememberMe, redirectUrl, options) {
        var state,
            params = '';

        data = data || {};

        if (data.code) {
            try {
                state = JSON.parse(decodeURIComponent(this.$route.query.state));
            }
            catch(e) {
                console.error('vue-jwt-auth:error There was an issue retrieving the state data.');
                state = {};
            }

            _login.call(this, this.getOption(type + 'Url'), data, state.rememberMe, state.redirect, options);
        } else {
            data.state = data.state || {};
            data.state.rememberMe = rememberMe === true;
            data.state.redirect = redirectUrl || '';

            data.appId = data.appId || this.getOption(type + 'AppId');;
            data.clientId = data.clientId || this.getOption(type + 'ClientId');
            data.scope = data.scope || this.getOption(type + 'Scope');
            data.redirect = data.redirect || this.getOption(type + 'Redirect');
            data.url = data.url || this.getOption(type + 'OAuth2');

            params = '?client_id=' + data.appId + '&redirect_uri=' + data.redirect + '&scope=' + data.scope + '&response_type=code&state=' + encodeURIComponent(JSON.stringify(data.state));

            window.location = data.url + params;
        }
    }

    function _fetch (cb) {
        cb = cb || function () {}

        _http.call(this, {
            url: this.getOption('fetchUrl'),
            method: 'get',
            success: (res) => {
                this.authenticated = true;
                this.data = this.getOption('userData').call(this, res);
                this.loaded = true;

                return cb();
            },
            error: () => {
                this.loaded = true;

                return cb();
            }
        });
    }

    // Plugin

    var Auth = {
        options: {
            authType: 'bearer',
            tokenType: 'jwt',

            fetchUrl: 'auth/user',
            tokenUrl: 'auth/token',
            loginUrl: 'auth/login',
            loginAsUrl: 'auth/login-as',

            authRedirect: '/login',
            logoutRedirect: '/',
            notFoundRedirect: '/404',
            forbiddenRedirect: '/403',

            rolesVar: 'roles',
            tokenVar: 'token',
            tokenName: 'jwt-auth-token',
            tokenTimeoutOffset: 5 * 1000, // 5 minutes.

            cookieDomain: _cookieDomain,
            userData: _userData,
            beforeEach: _beforeEach,
            invalidToken: _invalidToken,

            facebookUrl: 'auth/facebook',
            facebookAppId: '',
            facebookScope: 'email',
            facebookRedirect: _getUrl() + '/login/facebook',
            facebookOAuth2: 'https://www.facebook.com/v2.5/dialog/oauth',

            googleUrl: 'auth/google',
            googleAppId: '',
            googleScope: 'https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/plus.profile.emails.read',
            googleRedirect: _getUrl() + '/login/google',
            googleOAuth2: 'https://accounts.google.com/o/oauth2/auth',

            bufferUrl: 'auth/buffer',
            bufferAppId: '',
            bufferScope: '',
            bufferRedirect: _getUrl() + '/login/buffer',
            bufferOAuth2: 'https://bufferapp.com/oauth2/authorize'
        },

        data () {
            return {
                data: null,
                loaded: false,
                authenticated: null
            }
        },

        methods: {

            // Options

            getOption (key) {
                return this.$options.options[key]
            },

            setOptions (options) {
                for (var i in options) {
                    this.$options.options[i] = options[i]
                }
            },

            // Login / Logout

            login (data, rememberMe, redirectUrl, options) {
                _login.call(this, this.getOption('loginUrl'), data, rememberMe, redirectUrl, options)
            },

            oauth2(type, data, rememberMe, redirectUrl, options) {
                _oauth2.call(this, type, data, rememberMe, redirectUrl, options)
            },

            // Deprecated
            facebook (data, rememberMe, redirectUrl, options) {
                _oauth2.call(this, 'facebook', data, rememberMe, redirectUrl, options)
            },

            // Deprecated
            google (data, rememberMe, redirectUrl, options) {
                _oauth2.call(this, 'google', data, rememberMe, redirectUrl, options)
            },

            logout (redirectUrl, force) {
                _removeRememberMeCookie.call(this)

                // Need to call twice to remove both tokens.
                _removeToken.call(this)
                _removeToken.call(this)

                this.authenticated = false
                this.data = null

                if (redirectUrl && (this.$route.auth || force)) {
                    this.$router.go(redirectUrl)
                }
            },

            // User

            check (role) {
                if (this.data !== null) {
                    if (role) {
                        return _compare(role, this.data[this.getOption('rolesVar')]);
                    }

                    return true;
                }

                return false;
            },

            fetch (cb) {
                cb = cb || function () {}

                if (!this.loaded) {
                    _refreshToken.call(this)
                }

                if (this.authenticated === null && _getToken.call(this)) {
                    if (!document.cookie.match(/rememberMe/)) {
                        _removeToken.call(this)

                        this.loaded = true

                        return cb()
                    }

                    this.authenticated = false

                    _fetch.call(this, cb)
                } else {
                    this.loaded = true

                    return cb()
                }
            },

            user (data) {
                if (data) {
                    this.data = data;
                }

                return this.data
            },

            ready () {
                return this.loaded
            },

            // Login As

            loginAs (data, redirectUrl, options) {
                options = options || {}

                _http.call(this, {
                    url: this.getOption('loginAsUrl'),
                    method: 'post',
                    data: data,
                    success: (res) => {
                        var _this = this;

                        localStorage.setItem('login-as-' + this.getOption('tokenName'), res.json()[this.getOption('tokenVar')]);

                        _fetch.call(this, function () {
                            if (options.success) {
                                options.success.call(this, res);
                            }

                            if (redirectUrl && _this.check()) {
                                _this.$router.go(redirectUrl);
                            }
                        })
                    },
                    error:  (res) => {
                        if (options.error) {
                            options.error.call(this, res);
                        }
                    }
                });
            },

            logoutAs (redirectUrl) {
                var _this = this

                localStorage.removeItem('login-as-' + this.getOption('tokenName'))

                _fetch.call(this, function () {
                    if (redirectUrl) {
                        _this.$router.go(redirectUrl)
                    }
                })
            },

            other () {
                this.data // TODO: Strange thing, need this to make the check fire consistently ??

                return localStorage.getItem('login-as-' + this.getOption('tokenName'))
            },

            // Token

            getAuthHeader() {
                var token = _getToken.call(this);

                if (token && this.getOption('authType') === 'bearer') {
                    return 'Bearer: ' + token;
                }

                return false;
            },

            version() {
                return '0.10.4';
            }
        }
    }

    return function install (Vue, options, router) {
        var auth = new Vue(Auth)

        auth.setOptions(options || {})

        Object.defineProperties(Vue.prototype, {
            $auth: {
                get () {
                    _setRoute.call(auth, this.$route, this.$router)

                    return auth
                }
            }
        });

        // Setup before each route change check.
        (Vue.router || router).beforeEach(function (transition) {

            // Make sure to use $auth.fetch so context is loaded.
            transition.to.router.app.$auth.fetch(function () {
                auth.getOption('beforeEach').bind(auth)(transition)
            })
        })

        // Set interceptors.
        _interceptor(Vue, (req) => {
            var token = _getToken.call(auth)

            if (token && auth.getOption('authType') === 'bearer') {
                req.headers.Authorization = 'Bearer: ' + token
            }
        },

        // Reset auth token if provided in response.
        (res) => {
            var authorization = res.headers.Authorization,
                invalidTokenMethod = auth.getOption('invalidToken')

            if (authorization) {
                authorization = authorization.split(' ')

                if (authorization[1]) {
                    _setToken.call(auth, authorization[1])
                }
            }

            if (invalidTokenMethod) {
                invalidTokenMethod.bind(auth)(res)
            }
        });
    }
})()
<commit_msg>Update login method as option.<commit_after>module.exports = (function () {

    var _tokenRefreshTimeout = null

    // Default

    function _beforeEach (transition) {
        var auth = _toArray(transition.to.auth)

        if (auth && (auth === true || auth.constructor === Array)) {
            if (!this.check()) {
                this.$router.replace(this.getOption('authRedirect'))
            } else if (auth.constructor === Array && !_compare(auth, _toArray(this.user()[this.getOption('rolesVar')]))) {
                this.$router.replace(this.getOption('forbiddenRedirect'))
            } else {
                return transition.next()
            }
        } else if (auth === false && this.check()) {
            this.$router.replace(this.getOption('notFoundRedirect'))
        } else {
            return transition.next()
        }
    }

    function _userData (res) {
        res = res.json();

        return res.data || res
    }

    function _cookieDomain () {
        return window.location.hostname
    }

    function _invalidToken (res) {
        if (res.status === 401) {
            this.logout(this.getOption('logoutRedirect'))
        }
    }

    // Utils

    function _getUrl () {
        var port = window.location.port

        return window.location.protocol + '//' + window.location.hostname + (port ? ':' + port : '')
    }

    function _compare (one, two) {
        var i, ii

        one = _toArray(one)
        two = _toArray(two)

        if (one.constructor !== Array || two.constructor !== Array) {
            return false
        }

        for (i = 0, ii = one.length; i < ii; i++) {
            if (two.indexOf(one[i]) >= 0) {
                return true
            }
        }

        return false
    }

    function _toArray (val) {
        return (typeof val) === 'string' ? [val] : val
    }

    function _http (options) {

        // Version 0.8.0
        this.$http[options.method || 'get'](options.url, options.data).then(options.success, options.error);
    }

    function _interceptor (Vue, req, res) {

        // Version 0.8.0
        Vue.http.interceptors.push((request, next) => {
            if (req) { req.call(this, request); }

            next((response) => {
                if (res) { res.call(this, response); }
            });
        });

        // // Version before Promise implementation.
        // else {
        //     Vue.http.interceptors.push({
        //         request (request) {
        //             if (req) { req.call(this, request); }
        //         },

        //         response (response) {
        //             if (res) { res.call(this, response); }
        //         }
        //     });
        // }
    }

    // Remember Me

    function _setCookie (name, value, timeOffset) {
        var domain = this.getOption('cookieDomain').call(this),
            expires = (new Date((new Date()).getTime() + timeOffset)).toUTCString()

        document.cookie = (name + '=' + value + '; domain=' + domain + '; path=/;' + (timeOffset ? ' expires=' + expires + ';' : ''))
    }

    function _setRememberMeCookie (rememberMe) {
        _setCookie.call(this,
            'rememberMe',
            rememberMe === true ? 'true' : 'false',
            rememberMe === true ? 12096e5 : undefined
        )
    }

    function _removeRememberMeCookie () {
        _setCookie.call(this, 'rememberMe', 'false', -12096e5)
    }

    // Token

    function _setToken (token) {
        if (token) {
            localStorage.setItem((this.other() ? 'login-as-' : '') + this.getOption('tokenName'), token)
        }
    }

    function _getToken () {
        return localStorage.getItem((this.other() ? 'login-as-' : '') + this.getOption('tokenName'))
    }

    function _removeToken () {
        localStorage.removeItem((this.other() ? 'login-as-' : '') + this.getOption('tokenName'))
    }

    function _refreshToken () {
        var _this = this;

        if (_getToken.call(this) && this.getOption('tokenType') === 'jwt') {
            _http.call(this, {
                url: this.getOption('tokenUrl'),
                method: 'get',
                success: () => {
                    var tokenJSON = _decodeToken(_getToken.call(_this)),
                        expireTime = _getTokenExpirationDate(tokenJSON).valueOf(),
                        nowTime = new Date().valueOf(),
                        offsetTime = this.getOption('tokenTimeoutOffset'),
                        timeout = expireTime - nowTime - offsetTime;

                    clearTimeout(_tokenRefreshTimeout);

                    _tokenRefreshTimeout = setTimeout(function () {
                        _refreshToken.call(_this);
                    }, timeout);
                }
            });
        }
    }

    // Token util

    function _urlBase64Decode (str) {
        let output = str.replace(/-/g, '+').replace(/_/g, '/')

        switch (output.length % 4) {
            case 0: { break }
            case 2: { output += '=='; break }
            case 3: { output += '='; break }
            default: {
                console.log('Illegal base64url string!')
            }
        }

        return decodeURIComponent(escape(window.atob(output))) // polifyll https://github.com/davidchambers/Base64.js
    }

    function _decodeToken (token) {
        if ( ! token) { return; }

        let parts = token.split('.')

        if (parts.length !== 3) {
            throw new Error('JWT must have 3 parts')
        }

        let decoded = _urlBase64Decode(parts[1])

        if ( ! decoded) {
            throw new Error('Cannot decode the token')
        }

        return JSON.parse(decoded)
    }

    function _getTokenExpirationDate (tokenJSON) {
        if ( ! tokenJSON || ! tokenJSON.exp) { return }

        let d = new Date(0) // The 0 here is the key, which sets the date to the epoch

        d.setUTCSeconds(tokenJSON.exp)

        return d
    }

    // Router

    function _setRoute (route, router) {
        this.$route = route
        this.$router = router
    }

    // Auth

    function _login (path, data, rememberMe, redirectUrl, options) {
        options = options || {}

        _http.call(this, {
            url: path,
            method: options.method || this.getOption('loginMethod'),
            data: data,
            success: (res) => {
                var _this = this

                _setRememberMeCookie.call(this, rememberMe);

                _setToken.call(this, res.json()[this.getOption('tokenVar')]);

                this.authenticated = null;

                this.fetch(function () {
                    if (options.success) {
                        options.success.call(_this, res);
                    }

                    if (redirectUrl && _this.check()) {
                        _this.$router.go(redirectUrl);
                    }
                });
            },
            error: (res) => {
                if (options.error) {
                    options.error.call(this, res);
                }
            }
        });
    }

    function _oauth2 (type, data, rememberMe, redirectUrl, options) {
        var state,
            params = '';

        data = data || {};

        if (data.code) {
            try {
                state = JSON.parse(decodeURIComponent(this.$route.query.state));
            }
            catch(e) {
                console.error('vue-jwt-auth:error There was an issue retrieving the state data.');
                state = {};
            }

            _login.call(this, this.getOption(type + 'Url'), data, state.rememberMe, state.redirect, options);
        } else {
            data.state = data.state || {};
            data.state.rememberMe = rememberMe === true;
            data.state.redirect = redirectUrl || '';

            data.appId = data.appId || this.getOption(type + 'AppId');;
            data.clientId = data.clientId || this.getOption(type + 'ClientId');
            data.scope = data.scope || this.getOption(type + 'Scope');
            data.redirect = data.redirect || this.getOption(type + 'Redirect');
            data.url = data.url || this.getOption(type + 'OAuth2');

            params = '?client_id=' + data.appId + '&redirect_uri=' + data.redirect + '&scope=' + data.scope + '&response_type=code&state=' + encodeURIComponent(JSON.stringify(data.state));

            window.location = data.url + params;
        }
    }

    function _fetch (cb) {
        cb = cb || function () {}

        _http.call(this, {
            url: this.getOption('fetchUrl'),
            method: 'get',
            success: (res) => {
                this.authenticated = true;
                this.data = this.getOption('userData').call(this, res);
                this.loaded = true;

                return cb();
            },
            error: () => {
                this.loaded = true;

                return cb();
            }
        });
    }

    // Plugin

    var Auth = {
        options: {
            authType: 'bearer',
            tokenType: 'jwt',

            fetchUrl: 'auth/user',
            tokenUrl: 'auth/token',
            loginUrl: 'auth/login',
            loginMethod: 'post',
            loginAsUrl: 'auth/login-as',
            loginAsMethod: 'post',

            authRedirect: '/login',
            logoutRedirect: '/',
            notFoundRedirect: '/404',
            forbiddenRedirect: '/403',

            rolesVar: 'roles',
            tokenVar: 'token',
            tokenName: 'jwt-auth-token',
            tokenTimeoutOffset: 5 * 1000, // 5 minutes.

            cookieDomain: _cookieDomain,
            userData: _userData,
            beforeEach: _beforeEach,
            invalidToken: _invalidToken,

            facebookUrl: 'auth/facebook',
            facebookAppId: '',
            facebookScope: 'email',
            facebookRedirect: _getUrl() + '/login/facebook',
            facebookOAuth2: 'https://www.facebook.com/v2.5/dialog/oauth',

            googleUrl: 'auth/google',
            googleAppId: '',
            googleScope: 'https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/plus.profile.emails.read',
            googleRedirect: _getUrl() + '/login/google',
            googleOAuth2: 'https://accounts.google.com/o/oauth2/auth',

            bufferUrl: 'auth/buffer',
            bufferAppId: '',
            bufferScope: '',
            bufferRedirect: _getUrl() + '/login/buffer',
            bufferOAuth2: 'https://bufferapp.com/oauth2/authorize'
        },

        data () {
            return {
                data: null,
                loaded: false,
                authenticated: null
            }
        },

        methods: {

            // Options

            getOption (key) {
                return this.$options.options[key]
            },

            setOptions (options) {
                for (var i in options) {
                    this.$options.options[i] = options[i]
                }
            },

            // Login / Logout

            login (data, rememberMe, redirectUrl, options) {
                _login.call(this, this.getOption('loginUrl'), data, rememberMe, redirectUrl, options)
            },

            oauth2(type, data, rememberMe, redirectUrl, options) {
                _oauth2.call(this, type, data, rememberMe, redirectUrl, options)
            },

            // Deprecated
            facebook (data, rememberMe, redirectUrl, options) {
                _oauth2.call(this, 'facebook', data, rememberMe, redirectUrl, options)
            },

            // Deprecated
            google (data, rememberMe, redirectUrl, options) {
                _oauth2.call(this, 'google', data, rememberMe, redirectUrl, options)
            },

            logout (redirectUrl, force) {
                _removeRememberMeCookie.call(this)

                // Need to call twice to remove both tokens.
                _removeToken.call(this)
                _removeToken.call(this)

                this.authenticated = false
                this.data = null

                if (redirectUrl && (this.$route.auth || force)) {
                    this.$router.go(redirectUrl)
                }
            },

            // User

            check (role) {
                if (this.data !== null) {
                    if (role) {
                        return _compare(role, this.data[this.getOption('rolesVar')]);
                    }

                    return true;
                }

                return false;
            },

            fetch (cb) {
                cb = cb || function () {}

                if (!this.loaded) {
                    _refreshToken.call(this)
                }

                if (this.authenticated === null && _getToken.call(this)) {
                    if (!document.cookie.match(/rememberMe/)) {
                        _removeToken.call(this)

                        this.loaded = true

                        return cb()
                    }

                    this.authenticated = false

                    _fetch.call(this, cb)
                } else {
                    this.loaded = true

                    return cb()
                }
            },

            user (data) {
                if (data) {
                    this.data = data;
                }

                return this.data
            },

            ready () {
                return this.loaded
            },

            // Login As

            loginAs (data, redirectUrl, options) {
                options = options || {}

                _http.call(this, {
                    url: options.url || this.getOption('loginAsUrl'),
                    method: options.method || this.getOption('loginAsMethod'),
                    data: data,
                    success: (res) => {
                        var _this = this;

                        localStorage.setItem('login-as-' + this.getOption('tokenName'), res.json()[this.getOption('tokenVar')]);

                        _fetch.call(this, function () {
                            if (options.success) {
                                options.success.call(this, res);
                            }

                            if (redirectUrl && _this.check()) {
                                _this.$router.go(redirectUrl);
                            }
                        })
                    },
                    error:  (res) => {
                        if (options.error) {
                            options.error.call(this, res);
                        }
                    }
                });
            },

            logoutAs (redirectUrl) {
                var _this = this

                localStorage.removeItem('login-as-' + this.getOption('tokenName'))

                _fetch.call(this, function () {
                    if (redirectUrl) {
                        _this.$router.go(redirectUrl)
                    }
                })
            },

            other () {
                this.data // TODO: Strange thing, need this to make the check fire consistently ??

                return localStorage.getItem('login-as-' + this.getOption('tokenName'))
            },

            // Token

            getAuthHeader() {
                var token = _getToken.call(this);

                if (token && this.getOption('authType') === 'bearer') {
                    return 'Bearer: ' + token;
                }

                return false;
            },

            version() {
                return '0.10.4';
            }
        }
    }

    return function install (Vue, options, router) {
        var auth = new Vue(Auth)

        auth.setOptions(options || {})

        Object.defineProperties(Vue.prototype, {
            $auth: {
                get () {
                    _setRoute.call(auth, this.$route, this.$router)

                    return auth
                }
            }
        });

        // Setup before each route change check.
        (Vue.router || router).beforeEach(function (transition) {

            // Make sure to use $auth.fetch so context is loaded.
            transition.to.router.app.$auth.fetch(function () {
                auth.getOption('beforeEach').bind(auth)(transition)
            })
        })

        // Set interceptors.
        _interceptor(Vue, (req) => {
            var token = _getToken.call(auth)

            if (token && auth.getOption('authType') === 'bearer') {
                req.headers.Authorization = 'Bearer: ' + token
            }
        },

        // Reset auth token if provided in response.
        (res) => {
            var authorization = res.headers.Authorization,
                invalidTokenMethod = auth.getOption('invalidToken')

            if (authorization) {
                authorization = authorization.split(' ')

                if (authorization[1]) {
                    _setToken.call(auth, authorization[1])
                }
            }

            if (invalidTokenMethod) {
                invalidTokenMethod.bind(auth)(res)
            }
        });
    }
})()
<|endoftext|>


<commit_before>/*
Simple espruino program for the ear LIDAR sensors that:
   - moves the ears smoothly backwards and forwards
   - sends LIDAR readings via the USB cable to the node-RED on the Pi
   - responds to speed commands from USB serial port

All messages to node-RED are sent as JSON strings terminated in an ! via a
serial connection over a USB cable.

Published under The Unlicense

Richard Hopkins, 9th September 2018
*/
// set up USB connector to send messages to Pi via
// serial over USB
USB.setup(115200,{bytesize:8,stopbits:1});
//LoopbackA.setConsole();
var PWM_l = 0.15; // minimum position for sevo
var PWM_r = 0.8; // maximum position for servo
var PIN_PWM_l = B15; // left ear PWM pin name
var PIN_PWM_r = B14; // right ear PWM pin name
var PIN_LIDARX_l = B4; // pin for LIDAR on/off
var PIN_LIDARX_r = A8; // pin for LIDAR on/off
var PIN_pot_l = B1; // pin for servo potentiometer
var PIN_pot_r = A7; // pin for servo potentiometer
var PIN_sda = B3; // pin for I2C SDA cable
var PIN_scl = B10; // pin for I2C SCL cable
var move_int=1000; // time between servo moves in ms
var scan_int_l=10000; // time between readings in ms on left LIDAR
var scan_int_r=10000; // time between readings in ms on right LIDAR
var num_steps = 50; // number of steps in full sweep
var step = 0; // start at step 0
var direction = 1; // direction of first sweep
var LIDAR_l;  // object for left LIDAR sensor
var LIDAR_r; // object for right LIDAR sensor
var vRef = E.getAnalogVRef(); // voltage reference
var num_readings = 1;  // number of times voltage has been read

// position servo as instructed (from 0 to 1)
// using a pulse between 0.75ms and 2.25ms
function setServo(pin,pos) {
 if (pos<0) pos=0;
 if (pos>1) pos=1;
 //console.log(pos);
 analogWrite(pin, (1+pos) / 50.0, {freq:20});
}

// initialise the LIDAR and I2C interfaces
// turn off the LIDARs and use the I2C 2 bus at 400kHz
function initHW(){
   digitalWrite(PIN_LIDARX_l,0);
   digitalWrite(PIN_LIDARX_r,0);
   I2C2.setup({sda:PIN_sda, scl:PIN_scl, bitrate:100000});
  console.log("HW init done");
}

// create the LIDAR objects on the I2C bus
function initLIDAR(){
   digitalWrite(PIN_LIDARX_l,1);
   LIDAR_l = require("VL53L0X").connect(I2C2,{address:0x54});
   digitalWrite(PIN_LIDARX_r,1);
   LIDAR_r = require("VL53L0X").connect(I2C2,{address:0x56});
}

// maintain an average vRef number
function refine_vRef(){
  num_readings = num_readings + 1;
  reading = E.getAnalogVRef();
  vRef = (vRef * (num_readings-1)/num_readings) + (reading/num_readings);
  //console.log("Vref: " + vRef);
}

// this function is called automatically on Pico initialisation
// it will initise the I2C bus and the LIDAR and then enable three
// fuctions to run at intervals.  This includes:
// - refining the voltage reference for the board
// - moving the ears via their servos
// - taking a LIDAR reading
function onInit() {
   initHW();
   initLIDAR();
   setInterval(refine_vRef,1000);
   setInterval(moveEars,move_int);
   setInterval(takeReading,scan_int_l,'l_ear');
   setInterval(takeReading,scan_int_r,'r_ear');
   USB.on('data', function (data) {
     USB.print(data);
     switch(data) {
       case "stop":
        move_int=1000;
        scan_int_l=1000;
        scan_int_r=1000;
        break;
       case "slow":
        move_int=500;
        scan_int_l=1000;
        scan_int_r=1000;
        break;
      case "medium":
        move_int=50;
        scan_int_l=100;
        scan_int_r=100;
        break;
      case "fast":
        move_int=23;
        scan_int_l=41;
        scan_int_r=43;
        break;
        }
      });
}

// calculate desired servo position based on step
// the trigonometry smooths the movement of the servos
function calculateServoPos(step) {
   // each swing of the ears is separated into a number of steps (num_steps)
   // and the angle is calculated as the fraction of a half circle
   angle = step/num_steps*Math.PI;
   //console.log("Angle: "+String(angle));
   // the position calculated transforms the linear steps into a smoooth
   // natural motion
   position = (angle-(Math.cos(angle)*Math.sin(angle)))/Math.PI;
   //console.log("Pos: "+String(position));
   return position;
}

// translates the relative position to an absolute one
// this allows the movement of the ears to be constrained
// if necessary
function scaleServoPos(position) {
   scaled_pos = PWM_l + ((PWM_r-PWM_l)*position);
   //console.log("Pos: "+String(scaled_pos));
   return scaled_pos;
}

// send a JSON message to the Rapsberry Pi via a USB serial connection
function sendMsg(type,sensor,distance,angle) {
  message = String('{"type":"'+type+'","sensor":"'+sensor+'","distance":"'+distance+'","angle":"'+angle+'"}');
   USB.print(message);
  //console.log(message);
  }

// take a reading from each of the LIDAR sensors
function takeReading(ear){
  var dist;
  var ear_dir;
  if (ear == 'l_ear'){
     read_lidar = LIDAR_l;
     read_pin = PIN_pot_l;
  }
  if (ear == 'r_ear'){
     read_lidar = LIDAR_r;
     read_pin = PIN_pot_r;
  }
  dist = read_lidar.performSingleMeasurement().distance;
  // if distance is less than or equal to 20mm, then report 0mm
  if (dist != 20) {
    ear_dir=analogRead(read_pin)*vRef;
    sendMsg("LIDAR",ear,dist,ear_dir);
  }
}

// calculate the next position for the servos and move them to it
function moveEars(){
   if (move_int > 500) {return;}
   else {
     step = step + direction;
     if (step > num_steps) {
        direction = -1;
        step = num_steps-1;
     }
     if (step < 0) {
        direction = 1;
        step = 1;
     }
     scaled_pos = step_lookup[step];
     setServo(PIN_PWM_l,scaled_pos);
     setServo(PIN_PWM_r,1-scaled_pos);
  }
}

// create an array of all the valid scaled positions
step_lookup = [];
for (step = 0; step <= num_steps; step++) {
  step_lookup[step] = scaleServoPos(calculateServoPos(step));
  //print(step + " : " + step_lookup[step]);
}
step = 0;
<commit_msg>Enabling two-way communication to/from Espruino via USB<commit_after>/*
Simple espruino program for the ear LIDAR sensors that:
   - moves the ears smoothly backwards and forwards
   - sends LIDAR readings via the USB cable to the node-RED on the Pi
   - responds to speed commands from USB serial port

All messages to node-RED are sent as JSON strings terminated in an ! via a
serial connection over a USB cable.

Published under The Unlicense

Richard Hopkins, 9th September 2018
*/
var PWM_l = 0.15; // minimum position for sevo
var PWM_r = 0.8; // maximum position for servo
var PIN_PWM_l = B15; // left ear PWM pin name
var PIN_PWM_r = B14; // right ear PWM pin name
var PIN_LIDARX_l = B4; // pin for LIDAR on/off
var PIN_LIDARX_r = A8; // pin for LIDAR on/off
var PIN_pot_l = B1; // pin for servo potentiometer
var PIN_pot_r = A7; // pin for servo potentiometer
var PIN_sda = B3; // pin for I2C SDA cable
var PIN_scl = B10; // pin for I2C SCL cable
var num_steps = 50; // number of steps in full sweep
var step = 0; // start at step 0
var direction = 1; // direction of first sweep
var LIDAR_l;  // object for left LIDAR sensor
var LIDAR_r; // object for right LIDAR sensor
var vRef = E.getAnalogVRef(); // voltage reference
var num_readings = 1;  // number of times voltage has been read
var mov_int_ref; // the reference id for the ear movement timer
var scan_int_l_ref; // the reference id for the left scan timer
var scan_int_r_ref; // the reference id for the right scan timer
var speed = "fast"; // initial speed is fast

// position servo as instructed (from 0 to 1)
// using a pulse between 0.75ms and 2.25ms
function setServo(pin,pos) {
   if (pos<0) pos=0;
   if (pos>1) pos=1;
   //console.log(pos);
   analogWrite(pin, (1+pos) / 50.0, {freq:20});
}

// initialise the LIDAR and I2C interfaces
// turn off the LIDARs and use the I2C 2 bus at 400kHz
function initHW(){
   digitalWrite(PIN_LIDARX_l,0);
   digitalWrite(PIN_LIDARX_r,0);
   I2C2.setup({sda:PIN_sda, scl:PIN_scl, bitrate:100000});
   //console.log("HW init done");
}

// create the LIDAR objects on the I2C bus
function initLIDAR(){
   digitalWrite(PIN_LIDARX_l,1);
   LIDAR_l = require("VL53L0X").connect(I2C2,{address:0x54});
   digitalWrite(PIN_LIDARX_r,1);
   LIDAR_r = require("VL53L0X").connect(I2C2,{address:0x56});
}

// maintain an average vRef number
function refine_vRef(){
  num_readings = num_readings + 1;
  reading = E.getAnalogVRef();
  vRef = (vRef * (num_readings-1)/num_readings) + (reading/num_readings);
  //console.log("Vref: " + vRef);
}

function resetIntervals(move,left,right){
   clearInterval(mov_int_ref);
   mov_int_ref = setInterval(moveEars,move);
   clearInterval(scan_int_l_ref);
   scan_int_l_ref = setInterval(takeReading,left,'l_ear');
   clearInterval(scan_int_r_ref);
   scan_int_r_ref = setInterval(takeReading,right,'r_ear');
}

// this function is called automatically on Pico initialisation
// it will initise the I2C bus and the LIDAR and then enable three
// fuctions to run at intervals.  This includes:
// - refining the voltage reference for the board
// - moving the ears via their servos
// - taking a LIDAR reading
function onInit() {
   USB.setup(115200,{bytesize:8,stopbits:1});
   initHW();
   initLIDAR();
   setInterval(refine_vRef,1000);
   mov_int_ref = setInterval(moveEars,20);
   scan_int_l_ref = setInterval(takeReading,40,'l_ear');
   scan_int_r_ref = setInterval(takeReading,40,'r_ear');
   setTimeout(function() { Serial1.setConsole(); }, 30000); // give time for Pi to boot and make USB connection
   USB.on('data', function (data) {
      message = {type:"LIDAR",sensor:"none",distance:data,angle:data};
      sendMsg(message);
      speed = data;
      switch(data) {
         case "stop":
            resetIntervals(1000,1000,1000);
            break;
         case "slow":
            resetIntervals(500,1000,1000);
            break;
         case "medium":
            resetIntervals(50,100,100);
            break;
         case "fast":
            resetIntervals(20,40,40);
            break;
         }
      });
}

// calculate desired servo position based on step
// the trigonometry smooths the movement of the servos
function calculateServoPos(step) {
   // each swing of the ears is separated into a number of steps (num_steps)
   // and the angle is calculated as the fraction of a half circle
   angle = step/num_steps*Math.PI;
   //console.log("Angle: "+String(angle));
   // the position calculated transforms the linear steps into a smoooth
   // natural motion
   position = (angle-(Math.cos(angle)*Math.sin(angle)))/Math.PI;
   //console.log("Pos: "+String(position));
   return position;
}

// translates the relative position to an absolute one
// this allows the movement of the ears to be constrained
// if necessary
function scaleServoPos(position) {
   scaled_pos = PWM_l + ((PWM_r-PWM_l)*position);
   //console.log("Pos: "+String(scaled_pos));
   return scaled_pos;
}

// send a JSON message to the Rapsberry Pi via a USB serial connection
function sendMsg(type,sensor,distance,angle) {
   messageStr = JSON.stringify(message);
   USB.write(messageStr);
  }

function volts2angle(volts,ear){
   var MIN_V = 1.33; // lowest likely POT value
   var MAX_V = 2.10; // highest likely POT value
   var MAX_ANG = Math.PI/4;  // ears can travel roughly 45 degrees
   // normalize the voltage to a value between 0 and 1
   angle = Math.min((Math.max(volts - MIN_V,0))/(MAX_V-MIN_V),1.00);
   if (ear == 'l_ear'){
      angle = 1 - angle; // for left ear max volts is minimum angle
   }
   if (ear == 'r_ear'){
      angle = angle * -1; // for right ear, angle is negative
   }
   angle = angle * MAX_ANG;
   return angle;
}

// take a reading from each of the LIDAR sensors
function takeReading(ear){
  var dist;
  var ear_dir;
  if (ear == 'l_ear'){
     read_lidar = LIDAR_l;
     read_pin = PIN_pot_l;
  }
  if (ear == 'r_ear'){
     read_lidar = LIDAR_r;
     read_pin = PIN_pot_r;
  }
  volts = analogRead(read_pin)*vRef;
  ear_dir=volts2angle(volts,ear);
  dist = read_lidar.performSingleMeasurement().distance;
  // if distance does not equal 20mm, then report distance
  if (dist != 20) {
    dist = dist/1000; // convert to metres from mm
    message = {type:"LIDAR",sensor:ear,distance:dist,angle:ear_dir};
    sendMsg(message);
  }
}

// calculate the next position for the servos and move them to it
function moveEars(){
   if (speed == 'stop') {return;}
   else {
     step = step + direction;
     if (step > num_steps) {
        direction = -1;
        step = num_steps-1;
     }
     if (step < 0) {
        direction = 1;
        step = 1;
     }
     scaled_pos = step_lookup[step];
     setServo(PIN_PWM_l,scaled_pos);
     setServo(PIN_PWM_r,1-scaled_pos);
  }
}

// create an array of all the valid scaled positions
step_lookup = [];
for (step = 0; step <= num_steps; step++) {
  step_lookup[step] = scaleServoPos(calculateServoPos(step));
  //print(step + " : " + step_lookup[step]);
}
step = 0;
<|endoftext|>


<commit_before>/*
	angular-photoswipe v0.0.7
	(c) 2015 Massimiliano Sartoretto <massimilianosartoretto@gmail.com>
	License: MIT
*/

'format amd';
/* global define */

(function () {
  'use strict';

  function ngPhotoswipe(angular, Photoswipe) {

    return angular
      .module('ngPhotoswipe', [])
      .directive('ngPhotoswipe', ngPhotoswipeDirective);

    function ngPhotoswipeDirective($compile, $http, $templateCache) {
      return {
        restrict: 'AE',
        replace: true,
        scope: {
          slides: '=',
          options: '=',
          template: '@',
          startOn: '@'
        },
        link: linkFn
      };

      function linkFn(scope, iElement, iAttrs) {
        scope.template = scope.template || 'views/ng-photoswipe.html';

        scope.$on(scope.startOn, function () {
          scope.start();
        });

        $http
          .get(scope.template, { cache: $templateCache })
          .success(function(html) {
            var template = angular.element(html);
            iElement.append($compile(template)(scope));
          });

        scope.start = function() {
          var pswpElement = document.querySelectorAll('.pswp')[0];
          var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default || false, scope.slides, scope.options);
          gallery.init();
        };
      }
    }
  }

  if (typeof define === 'function' && define.amd) {
		define(['angular', 'Photoswipe'], ngPhotoswipe);
	} else if (typeof module !== 'undefined' && module && module.exports) {
		ngPhotoswipe(angular, require('Photoswipe'));
		module.exports = 'ngPhotoswipe';
	} else {
		ngPhotoswipe(angular, (typeof global !== 'undefined' ? global : window).Photoswipe);
	}
})();
<commit_msg>Multiple enhancements<commit_after>/*
	angular-photoswipe v0.0.4
	(c) 2015 Massimiliano Sartoretto <massimilianosartoretto@gmail.com>
	License: MIT
*/

'format amd';
/* global define */

(function () {
  'use strict';

  function ngPhotoswipe(angular, Photoswipe) {

    return angular
      .module('ngPhotoswipe', [])
      .directive('ngPhotoswipe', ngPhotoswipeDirective);

    function ngPhotoswipeDirective($compile, $http, $templateCache) {
      return {
        restrict: 'AE',
        replace: true,
        scope: {
          slides: '=',
          options: '=',
          template: '@',
          open: '=',//lets us put a watch on when the open changes'
          onClose: '&',
          extensions:'=?'
        },
        link: linkFn
      };

      function linkFn(scope, iElement, iAttrs) {
        scope.template = scope.template || 'views/ng-photoswipe.html';
        $http
          .get(scope.template, { cache: $templateCache })
          .success(function(html) {
            var template = angular.element(html);
            iElement.append($compile(template)(scope));
          });

        //var gallery;
        scope.start = function () {
            scope.open = true;
            startGallery();
        };
        var startGallery = function () {
            var pswpElement = document.querySelectorAll('.pswp')[0];
            scope.gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default || false, scope.slides, scope.options);
            scope.gallery.init();
            scope.item = scope.gallery.currItem;
            scope.gallery.listen('destroy', function () {
                if (typeof scope.onClose === 'function') {
                    scope.onClose();
                }

            });
            scope.gallery.listen('afterChange', function () {

                scope.item = scope.gallery.currItem;
                if (!scope.$$phase) {
                    scope.$apply();
                }

            });
        }
        scope.$watch('open', function (nval, oval) {
            if (nval != oval) {
                if (nval) {
                    startGallery();
                }
            } else if (!nval && scope.gallery) {
                //close it
                scope.gallery.close();
                scope.gallery = null;
            }
        });
        scope.$on('destroy', function () {
            scope.gallery = null;
        });

      }
    }
  }

  if (typeof define === 'function' && define.amd) {
		define(['angular', 'Photoswipe'], ngPhotoswipe);
	} else if (typeof module !== 'undefined' && module && module.exports) {
		ngPhotoswipe(angular, require('Photoswipe'));
		module.exports = 'ngPhotoswipe';
	} else {
		ngPhotoswipe(angular, (typeof global !== 'undefined' ? global : window).Photoswipe);
	}
})();
<|endoftext|>


<commit_before>var wallabyFiles = require('test-runner-config').getWallabyFiles(require('./test/testFiles'));

module.exports = function () {
  return {
    //testFramework: 'jasmine@1.3.1',
    files: wallabyFiles.files,
    tests: wallabyFiles.tests,
    env: {
      runner: require('phantomjs2-ext').path,
      params: {
        runner: '--web-security=false'
      }
    }
  };
};
<commit_msg>Clean up Wallaby config<commit_after>var wallabyFiles = require('test-runner-config').getWallabyFiles(require('./test/testFiles'));

module.exports = function () {
  return {
    //testFramework: 'jasmine@1.3.1',
    files: wallabyFiles.files,
    tests: wallabyFiles.tests
  };
};
<|endoftext|>


<commit_before>/* global Movement, Detainee, Event, Prebooking */
'use strict';

var ValidationError = require('../lib/exceptions/ValidationError');
var BedCountService = require('../services/BedCountService');
var LinkingModels = require('sails-linking-models');

const model = {
  schema: true,
  autoCreatedAt: true,
  autoUpdatedAt: true,
  attributes: {
    heartbeat_received: {
      type: 'datetime'
    },
    cid_received_date: {
      type: 'datetime'
    },
    prebooking_received: {
      type: 'datetime'
    },
    name: {
      type: 'string',
      defaultsTo: 0,
      required: true,
      unique: true
    },
    male_capacity: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    female_capacity: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    male_in_use: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    female_in_use: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    male_out_of_commission: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    female_out_of_commission: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    male_cid_name: {
      type: 'array'
    },
    female_cid_name: {
      type: 'array'
    },
    male_prebooking: {
      collection: 'prebooking',
      note: 'this is a workaround until waterline supports conditional joins see balderdashy/waterline#988 and balderdashy/waterline#645',
      via: 'male_prebooking'
    },
    female_prebooking: {
      collection: 'prebooking',
      note: 'this is a workaround until waterline supports conditional joins see balderdashy/waterline#988 and balderdashy/waterline#645',
      via: 'female_prebooking'
    },
    male_contingency: {
      collection: 'prebooking',
      note: 'this is a workaround until waterline supports conditional joins see balderdashy/waterline#988 and balderdashy/waterline#645',
      via: 'male_contingency'
    },
    female_contingency: {
      collection: 'prebooking',
      note: 'this is a workaround until waterline supports conditional joins see balderdashy/waterline#988 and balderdashy/waterline#645',
      via: 'female_contingency'
    },
    events: {
      collection: 'event',
      via: 'centre'
    },
    detainees: {
      collection: 'detainee',
      via: 'centre'
    },
    movements: {
      collection: 'movement',
      via: 'centre'
    },
    toJSON: function () {
      const unreconciledEventCounter = (gender, operations) => this.unreconciledEvents.reduce((count, event) => {
        if (event.detainee.gender === gender && operations.indexOf(event.operation) >= 0) {
          return count + 1;
        }
        return count;
      }, 0);
      const unreconciledMovementCounter = (gender, direction) => this.unreconciledMovements.reduce((count, movement) => {
        if (movement.gender === gender && movement.direction === direction) {
          return count + 1;
        }
        return count;
      }, 0);

      const response = {
        type: 'centre',
        id: this.id.toString(),
        attributes: {
          cidReceivedDate: this.cid_received_date ? this.cid_received_date.toString() : null,
          updated: this.updatedAt,
          heartbeatReceived: this.heartbeat_received ? this.heartbeat_received.toString() : null,
          prebookingReceived: this.prebooking_received ? this.prebooking_received.toString() : null,
          name: this.name
        },
        links: this.modelLinks('centres', reverseRouteService)
      };
      ['male', 'female'].forEach((gender) => {
        response.attributes[gender + 'Capacity'] = this[gender + '_capacity'];
        response.attributes[gender + 'InUse'] = this[gender + '_in_use'];
        response.attributes[gender + 'OutOfCommission'] = this[gender + '_out_of_commission'];
        response.attributes[gender + 'Prebooking'] = this[gender + '_prebooking'].length;
        response.attributes[gender + 'Contingency'] = this[gender + '_contingency'].length;
        response.attributes[gender + 'Availability'] = response.attributes[gender + 'Capacity'];
        response.attributes[gender + 'Availability'] -= response.attributes[gender + 'InUse'];
        response.attributes[gender + 'Availability'] -= response.attributes[gender + 'OutOfCommission'];
        response.attributes[gender + 'Availability'] -= response.attributes[gender + 'Prebooking'];
        response.attributes[gender + 'Availability'] -= response.attributes[gender + 'Contingency'];
        if (this.reconciled) {
          response.attributes[gender + 'UnexpectedIn'] = unreconciledEventCounter(gender, ['check in']);
          response.attributes[gender + 'ExpectedIn'] = unreconciledMovementCounter(gender, 'in');
          response.attributes[gender + 'ExpectedOut'] = unreconciledMovementCounter(gender, 'out');
          response.attributes[gender + 'Availability'] -= response.attributes[gender + 'ExpectedIn'];
        }
      });
      return response;
    }
  },
  getGenderAndCentreByCIDLocation: function (location) {
    return this.find().then(centres =>
      _.compact(_.map(centres, centre => {
        if (_.contains(centre.male_cid_name, location)) {
          return {
            centre: centre.id,
            gender: 'male'
          };
        }
        if (_.contains(centre.female_cid_name, location)) {
          return {
            centre: centre.id,
            gender: 'female'
          };
        }
      }))[0]
    );
  },

  removeNonOccupancy: function () {
    return this.destroy({'mo-type': 'non-occupancy'});
  },

  afterCreate: (centre, done) =>
    Centres.findReconciled({id: centre.id})
      .map((centre) => Centres.publishCreate(centre.toJSON()))
      .finally(() => done()),

  findReconciled: (query) =>
    Centres.find(query || {})
      .populate('male_prebooking')
      .populate('female_prebooking')
      .populate('male_contingency')
      .populate('female_contingency')
      .toPromise()
      .map(BedCountService.performConfiguredReconciliation),

  publishUpdateOne: (centre) =>
    Centres.findReconciled({id: centre.id || centre})
      .map((centre) => Centres.publishUpdate(centre.id, centre.toJSON())),

  publishUpdateAll: () =>
    Centres.findReconciled()
      .map((centre) => Centres.publishUpdate(centre.id, centre.toJSON())),

  afterDestroy: function (records, done) {
    Promise.all(_.map(records, (record) =>
      Movement.destroy({centre: record.id})
        .then(() => Prebooking.destroy({centre: record.id}))
        .then(() => Event.destroy({centre: record.id}))
        .then(() => Detainee.destroy({centre: record.id}))
        .then(() => this.publishDestroy(record.id))
    ))
      .then(() => done());
  },

  getByName: function (name) {
    return this.findByName(name)
      .then((centre) => {
        if (centre === undefined || centre.length !== 1) {
          throw new ValidationError('Invalid centre');
        }
        return centre[0];
      });
  }

};

module.exports = LinkingModels.mixin(model);
<commit_msg>cascade destruction of heartbeats on centre destroy<commit_after>/* global Movement, Detainee, Event, Prebooking */
'use strict';

var ValidationError = require('../lib/exceptions/ValidationError');
var BedCountService = require('../services/BedCountService');
var LinkingModels = require('sails-linking-models');

const model = {
  schema: true,
  autoCreatedAt: true,
  autoUpdatedAt: true,
  attributes: {
    heartbeat_received: {
      type: 'datetime'
    },
    cid_received_date: {
      type: 'datetime'
    },
    prebooking_received: {
      type: 'datetime'
    },
    name: {
      type: 'string',
      defaultsTo: 0,
      required: true,
      unique: true
    },
    male_capacity: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    female_capacity: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    male_in_use: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    female_in_use: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    male_out_of_commission: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    female_out_of_commission: {
      type: 'integer',
      defaultsTo: 0,
      required: true
    },
    male_cid_name: {
      type: 'array'
    },
    female_cid_name: {
      type: 'array'
    },
    male_prebooking: {
      collection: 'prebooking',
      note: 'this is a workaround until waterline supports conditional joins see balderdashy/waterline#988 and balderdashy/waterline#645',
      via: 'male_prebooking'
    },
    female_prebooking: {
      collection: 'prebooking',
      note: 'this is a workaround until waterline supports conditional joins see balderdashy/waterline#988 and balderdashy/waterline#645',
      via: 'female_prebooking'
    },
    male_contingency: {
      collection: 'prebooking',
      note: 'this is a workaround until waterline supports conditional joins see balderdashy/waterline#988 and balderdashy/waterline#645',
      via: 'male_contingency'
    },
    female_contingency: {
      collection: 'prebooking',
      note: 'this is a workaround until waterline supports conditional joins see balderdashy/waterline#988 and balderdashy/waterline#645',
      via: 'female_contingency'
    },
    events: {
      collection: 'event',
      via: 'centre'
    },
    detainees: {
      collection: 'detainee',
      via: 'centre'
    },
    movements: {
      collection: 'movement',
      via: 'centre'
    },
    toJSON: function () {
      const unreconciledEventCounter = (gender, operations) => this.unreconciledEvents.reduce((count, event) => {
        if (event.detainee.gender === gender && operations.indexOf(event.operation) >= 0) {
          return count + 1;
        }
        return count;
      }, 0);
      const unreconciledMovementCounter = (gender, direction) => this.unreconciledMovements.reduce((count, movement) => {
        if (movement.gender === gender && movement.direction === direction) {
          return count + 1;
        }
        return count;
      }, 0);

      const response = {
        type: 'centre',
        id: this.id.toString(),
        attributes: {
          cidReceivedDate: this.cid_received_date ? this.cid_received_date.toString() : null,
          updated: this.updatedAt,
          heartbeatReceived: this.heartbeat_received ? this.heartbeat_received.toString() : null,
          prebookingReceived: this.prebooking_received ? this.prebooking_received.toString() : null,
          name: this.name
        },
        links: this.modelLinks('centres', reverseRouteService)
      };
      ['male', 'female'].forEach((gender) => {
        response.attributes[gender + 'Capacity'] = this[gender + '_capacity'];
        response.attributes[gender + 'InUse'] = this[gender + '_in_use'];
        response.attributes[gender + 'OutOfCommission'] = this[gender + '_out_of_commission'];
        response.attributes[gender + 'Prebooking'] = this[gender + '_prebooking'].length;
        response.attributes[gender + 'Contingency'] = this[gender + '_contingency'].length;
        response.attributes[gender + 'Availability'] = response.attributes[gender + 'Capacity'];
        response.attributes[gender + 'Availability'] -= response.attributes[gender + 'InUse'];
        response.attributes[gender + 'Availability'] -= response.attributes[gender + 'OutOfCommission'];
        response.attributes[gender + 'Availability'] -= response.attributes[gender + 'Prebooking'];
        response.attributes[gender + 'Availability'] -= response.attributes[gender + 'Contingency'];
        if (this.reconciled) {
          response.attributes[gender + 'UnexpectedIn'] = unreconciledEventCounter(gender, ['check in']);
          response.attributes[gender + 'ExpectedIn'] = unreconciledMovementCounter(gender, 'in');
          response.attributes[gender + 'ExpectedOut'] = unreconciledMovementCounter(gender, 'out');
          response.attributes[gender + 'Availability'] -= response.attributes[gender + 'ExpectedIn'];
        }
      });
      return response;
    }
  },
  getGenderAndCentreByCIDLocation: function (location) {
    return this.find().then(centres =>
      _.compact(_.map(centres, centre => {
        if (_.contains(centre.male_cid_name, location)) {
          return {
            centre: centre.id,
            gender: 'male'
          };
        }
        if (_.contains(centre.female_cid_name, location)) {
          return {
            centre: centre.id,
            gender: 'female'
          };
        }
      }))[0]
    );
  },

  removeNonOccupancy: function () {
    return this.destroy({'mo-type': 'non-occupancy'});
  },

  afterCreate: (centre, done) =>
    Centres.findReconciled({id: centre.id})
      .map((centre) => Centres.publishCreate(centre.toJSON()))
      .finally(() => done()),

  findReconciled: (query) =>
    Centres.find(query || {})
      .populate('male_prebooking')
      .populate('female_prebooking')
      .populate('male_contingency')
      .populate('female_contingency')
      .toPromise()
      .map(BedCountService.performConfiguredReconciliation),

  publishUpdateOne: (centre) =>
    Centres.findReconciled({id: centre.id || centre})
      .map((centre) => Centres.publishUpdate(centre.id, centre.toJSON())),

  publishUpdateAll: () =>
    Centres.findReconciled()
      .map((centre) => Centres.publishUpdate(centre.id, centre.toJSON())),

  afterDestroy: function (records, done) {
    Promise.all(_.map(records, (record) =>
      Movement.destroy({centre: record.id})
        .then(() => Prebooking.destroy({centre: record.id}))
        .then(() => Event.destroy({centre: record.id}))
        .then(() => Detainee.destroy({centre: record.id}))
        .then(() => Heartbeat.destroy({centre: record.id}))
        .then(() => this.publishDestroy(record.id))
    ))
      .then(() => done());
  },

  getByName: function (name) {
    return this.findByName(name)
      .then((centre) => {
        if (centre === undefined || centre.length !== 1) {
          throw new ValidationError('Invalid centre');
        }
        return centre[0];
      });
  }

};

module.exports = LinkingModels.mixin(model);
<|endoftext|>


<commit_before>const gulp = require('gulp')
const concat = require('gulp-concat')
const ts = require('gulp-typescript')
const nodemon = require('gulp-nodemon')
const seq = require('run-sequence')
const clean = require('gulp-clean')
const replace = require('gulp-replace')
const $if = require('gulp-if')
const changed = require('gulp-changed')
const watch = require('gulp-watch')

const postcss = require('gulp-postcss')
const modules = require('postcss-modules')

const rollup = require('rollup').rollup
const commonjs = require('rollup-plugin-commonjs')
const nodeResolve = require('rollup-plugin-node-resolve')
const nodeGlobals = require('rollup-plugin-node-globals')
const nodeBuiltins = require('rollup-plugin-node-builtins')
const json = require('rollup-plugin-json')

const bs = require('browser-sync').create()

const _ = require('lodash')

const fs = require('fs-path')
const path = require('path')
const exec = require('child_process').exec;

var opts = {
	compileOnlyChangedTs: true,
	compileOnlyChangedPcss: true,
	dist: 'temp/dist',
	distApp: 'temp/dist/app',
	clientBundle: 'temp/client',
}

//
// Basic Commands
//

gulp.task('test', (done) => {
	seq(
		'compile-changed-pcss',
		'compile-ts',
		'run-unit-test',
		done
	)
})

gulp.task('run', done => {
	seq(
		'compile',
		'move-index-js',
		'start-test-server',
		'init-browser-sync',
		done
	)
})

gulp.task('compile', (done) => {
	opts.compileOnlyChangedTs = false
	opts.compileOnlyChangedPcss = false
	seq(
		'clean',
		'compile-postcss',
		'compile-ts',
		done
	)
})

gulp.task('clean', () => {
	return gulp.src('temp/**/*.*', {read: false})
		.pipe(clean())
})

//
// Compilers
//

// Just copy index.js
gulp.task('move-index-js', () => {
	return gulp.src('index.js')
		.pipe(gulp.dest('temp/dist'))
})

// Compile PostCSS
var cssTsFiles = []

gulp.task('compile-postcss', (done) => {
	seq(
		'pcss-dependency',
		'compile-pcss',
		['create-css-ts', 'create-style-css'],
		'compile-css-ts',
		done
	)
})

gulp.task('pcss-dependency', () => {
	return gulp.src('app/common/{global,base}.pcss')
		.pipe(gulp.dest(opts.distApp + '/common'))
})

gulp.task('compile-pcss', () => {
	cssTsFiles = []
	return gulp.src('app/**/*.pcss')
		.pipe($if(opts.compileOnlyChangedPcss, changed(opts.distApp)))
		.pipe(gulp.dest(opts.distApp))
		.pipe(postcss([
			modules({
				generateScopedName: '[local]_[hash:base64:5]',
				getJSON: (filePath, obj) => {
					const relativeFilePath = filePath.substr(__dirname.length)
					const tsFilePath = (path.join(__dirname, relativeFilePath) + '.ts').replace("pcss", "css")
					let file = {}
					file.path = tsFilePath
					file.content =
						"const styles = " + JSON.stringify(obj) + "\n" +
						"export default styles"
					cssTsFiles.push(file)
				}
			}),
			require('postcss-utilities'),
			require('precss'),
			require('postcss-short'),
			require('postcss-cssnext'),
		]))
		.pipe(gulp.dest(opts.clientBundle))
})

gulp.task('create-css-ts', () => {
	cssTsFiles.forEach(file => {
		fs.writeFileSync(file.path, file.content)
		let path = file.path.replace('dist\\app', 'client')
		fs.writeFileSync(path, file.content)
		path = path.replace(".ts", ".js")
		fs.writeFileSync(path, file.content)
	})
	return "Done"
})

gulp.task('compile-css-ts', () => {
	return gulp.src('temp/dist/app/**/*.css.ts')
		.pipe(ts({
			module: "commonjs",
			target: "es5",
		}))
		.pipe(gulp.dest('temp/dist/app'))
})

gulp.task('create-style-css', () => {
	return gulp.src(opts.clientBundle + '/**/*.pcss')
		.pipe(concat('style.css'))
		.pipe(gulp.dest('temp/dist/static'))
})

// Compile TS
gulp.task('compile-ts', () => {
	return gulp.src('app/**/*.{ts,tsx}')
		.pipe($if(opts.compileOnlyChangedTs, changed(opts.distApp)))
		.pipe(gulp.dest(opts.distApp))
		.pipe(ts({
			jsx: "react",
			module: "commonjs",
			target: "es5",
			sourceMap: true
		}))
		.pipe(gulp.dest('temp/dist/app'))
})

gulp.task('compile-changed-ts-to-es6', () => {
	return gulp.src('app/**/*.{ts,tsx}')
		.pipe($if(opts.compileOnlyChangedTs, changed(opts.clientBundle)))
		.pipe(gulp.dest(opts.clientBundle))
		.pipe(ts({
			jsx: "react",
			module: "es6",
			target: "es5",
			moduleResolution: "node",
			//allowSyntheticDefaultImports: true,
		}))
		.pipe(gulp.dest(opts.clientBundle))
})

// Run Server/Client
gulp.task('compile-changed-ts', done => {
	opts.compileOnlyChangedTs = true
	seq(
		'compile-ts',
		done
	)
})

gulp.task('compile-and-bundle-changed-ts', done => {
	seq(
		['compile-changed-ts', 'compile-changed-ts-to-es6'],
		'bundle-client-js',
		done
	)
})

gulp.task('compile-changed-pcss', done => {
	opts.compileOnlyChangedPcss = true
	seq(
		'compile-postcss',
		done
	)
})

gulp.task('bundle-client-js', (done) => {
	seq(
		'replace-import',
		'rollup',
		done
	)
})

gulp.task('replace-import', () => {
	let replacements = [
		"CSSModules",
		"isoFetch"
	]
	return gulp.src('temp/client/**/*.js')
		.pipe(replace(new RegExp(`import \\* as (${replacements.join('|')})`, 'g'), "import $1"))
		.pipe(gulp.dest(opts.clientBundle))
})

gulp.task('rollup', () => {
	return rollup({
		entry: 'temp/client/client.js',
		context: 'window',
		plugins: [
			nodeResolve({
				browser: true,
				preferBuiltins: true,
			}),
			json(),
			commonjs({
				namedExports: {
					'node_modules/react/react.js' : ['Component', 'Children', 'createElement', 'PropTypes'],
					'node_modules/react-dom/index.js' : ['render']
				}
			}),
			nodeGlobals(),
			nodeBuiltins(),
		]
	}).then(bundle => {
		return bundle.write({
			format: 'iife',
			dest: 'temp/dist/static/bundle.js'
		});
	})
})

var server;

gulp.task('start-test-server', (done) => {
	var started = false
	server = nodemon({
		script: path.join(__dirname, 'temp/dist/index.js'),
		watch: [
			"app"
		],
		ignore: [
			"*test.ts"
		],
		ext: 'ts tsx pcss',
		verbose: true,
		tasks: changedFiles => {
			let exts = _.map(changedFiles, (p) => {
				return path.extname(p)
			})
			exts = _.uniq(exts)

			let tasks = []
			if(_.includes(exts, '.pcss')) {
				tasks.push('compile-changed-pcss')
			}

			if(_.includes(exts, '.ts') || _.includes(exts, '.tsx')) {
				tasks.push('compile-and-bundle-changed-ts')
			}
			tasks.push('reload-client')

			return tasks
		}
	})
	.on('start', () => {
		if(!started) {
			started = true
			done()
		}
	})
})

gulp.task('restart-server', done => {
	server.emit('restart')
	done()
})

gulp.task('init-browser-sync', done => {
	bs.init({
		proxy: {
			target: "localhost:3000",
			ws: true,
		},
		files: "temp/dist/**/*",
		browser: ["firefox", "chrome", "iexplore", path.join(__dirname, ".bin", "edge.exe")], /* edge launcher from https://github.com/MicrosoftEdge/edge-launcher */
		port: 5101,
	})
	done()
})

gulp.task('reload-client', () => {
	return bs.reload({stream: true})
})

gulp.task('run-unit-test', (done) => {
	var test = require('./test/current.js')
	test = path.join(__dirname, opts.distApp, test)
	console.log(test)
	exec(`node ${test}|.\\node_modules\\.bin\\tap-spec`, (err, stdout, stderr) => {
		console.log(stdout)
		console.log(stderr)
		done()
	})
})<commit_msg>Rearranged functions.<commit_after>const gulp = require('gulp')
const concat = require('gulp-concat')
const ts = require('gulp-typescript')
const nodemon = require('gulp-nodemon')
const seq = require('run-sequence')
const clean = require('gulp-clean')
const replace = require('gulp-replace')
const $if = require('gulp-if')
const changed = require('gulp-changed')
const watch = require('gulp-watch')

const postcss = require('gulp-postcss')
const modules = require('postcss-modules')

const rollup = require('rollup').rollup
const commonjs = require('rollup-plugin-commonjs')
const nodeResolve = require('rollup-plugin-node-resolve')
const nodeGlobals = require('rollup-plugin-node-globals')
const nodeBuiltins = require('rollup-plugin-node-builtins')
const json = require('rollup-plugin-json')

const bs = require('browser-sync').create()

const _ = require('lodash')

const fs = require('fs-path')
const path = require('path')
const exec = require('child_process').exec;

var opts = {
	compileOnlyChangedTs: true,
	compileOnlyChangedPcss: true,
	dist: 'temp/dist',
	distApp: 'temp/dist/app',
	clientBundle: 'temp/client',
}

//
// Basic Commands
//

gulp.task('test', (done) => {
	seq(
		'compile-changed-pcss',
		'compile-ts',
		'run-unit-test',
		done
	)
})

gulp.task('run', done => {
	seq(
		'compile',
		'move-index-js',
		'start-test-server',
		'init-browser-sync',
		done
	)
})

gulp.task('compile', (done) => {
	opts.compileOnlyChangedTs = false
	opts.compileOnlyChangedPcss = false
	seq(
		'clean',
		'compile-postcss',
		'compile-ts',
		done
	)
})

gulp.task('clean', () => {
	return gulp.src('temp/**/*.*', {read: false})
		.pipe(clean())
})

//
// Compilers
//

// Just copy index.js
gulp.task('move-index-js', () => {
	return gulp.src('index.js')
		.pipe(gulp.dest('temp/dist'))
})

// Compile PostCSS
var cssTsFiles = []

gulp.task('compile-postcss', (done) => {
	seq(
		'pcss-dependency',
		'compile-pcss',
		['create-css-ts', 'create-style-css'],
		'compile-css-ts',
		done
	)
})

gulp.task('pcss-dependency', () => {
	return gulp.src('app/common/{global,base}.pcss')
		.pipe(gulp.dest(opts.distApp + '/common'))
})

gulp.task('compile-pcss', () => {
	cssTsFiles = []
	return gulp.src('app/**/*.pcss')
		.pipe($if(opts.compileOnlyChangedPcss, changed(opts.distApp)))
		.pipe(gulp.dest(opts.distApp))
		.pipe(postcss([
			modules({
				generateScopedName: '[local]_[hash:base64:5]',
				getJSON: (filePath, obj) => {
					const relativeFilePath = filePath.substr(__dirname.length)
					const tsFilePath = (path.join(__dirname, relativeFilePath) + '.ts').replace("pcss", "css")
					let file = {}
					file.path = tsFilePath
					file.content =
						"const styles = " + JSON.stringify(obj) + "\n" +
						"export default styles"
					cssTsFiles.push(file)
				}
			}),
			require('postcss-utilities'),
			require('precss'),
			require('postcss-short'),
			require('postcss-cssnext'),
		]))
		.pipe(gulp.dest(opts.clientBundle))
})

gulp.task('create-css-ts', () => {
	cssTsFiles.forEach(file => {
		fs.writeFileSync(file.path, file.content)
		let path = file.path.replace('dist\\app', 'client')
		fs.writeFileSync(path, file.content)
		path = path.replace(".ts", ".js")
		fs.writeFileSync(path, file.content)
	})
	return "Done"
})

gulp.task('compile-css-ts', () => {
	return gulp.src('temp/dist/app/**/*.css.ts')
		.pipe(ts({
			module: "commonjs",
			target: "es5",
		}))
		.pipe(gulp.dest('temp/dist/app'))
})

gulp.task('create-style-css', () => {
	return gulp.src(opts.clientBundle + '/**/*.pcss')
		.pipe(concat('style.css'))
		.pipe(gulp.dest('temp/dist/static'))
})

// Compile TS
gulp.task('compile-ts', () => {
	return gulp.src('app/**/*.{ts,tsx}')
		.pipe($if(opts.compileOnlyChangedTs, changed(opts.distApp)))
		.pipe(gulp.dest(opts.distApp))
		.pipe(ts({
			jsx: "react",
			module: "commonjs",
			target: "es5",
			sourceMap: true
		}))
		.pipe(gulp.dest('temp/dist/app'))
})

gulp.task('compile-changed-ts-to-es6', () => {
	return gulp.src('app/**/*.{ts,tsx}')
		.pipe($if(opts.compileOnlyChangedTs, changed(opts.clientBundle)))
		.pipe(gulp.dest(opts.clientBundle))
		.pipe(ts({
			jsx: "react",
			module: "es6",
			target: "es5",
			moduleResolution: "node",
			//allowSyntheticDefaultImports: true,
		}))
		.pipe(gulp.dest(opts.clientBundle))
})

//
// Run Server/Client
//

// Client Js bundle

gulp.task('bundle-client-js', (done) => {
	seq(
		'replace-import',
		'rollup',
		done
	)
})

gulp.task('replace-import', () => {
	let replacements = [
		"CSSModules",
		"isoFetch"
	]
	return gulp.src('temp/client/**/*.js')
		.pipe(replace(new RegExp(`import \\* as (${replacements.join('|')})`, 'g'), "import $1"))
		.pipe(gulp.dest(opts.clientBundle))
})

gulp.task('rollup', () => {
	return rollup({
		entry: 'temp/client/client.js',
		context: 'window',
		plugins: [
			nodeResolve({
				browser: true,
				preferBuiltins: true,
			}),
			json(),
			commonjs({
				namedExports: {
					'node_modules/react/react.js' : ['Component', 'Children', 'createElement', 'PropTypes'],
					'node_modules/react-dom/index.js' : ['render']
				}
			}),
			nodeGlobals(),
			nodeBuiltins(),
		]
	}).then(bundle => {
		return bundle.write({
			format: 'iife',
			dest: 'temp/dist/static/bundle.js'
		});
	})
})

// nodemon server

var server;

gulp.task('start-test-server', (done) => {
	var started = false
	server = nodemon({
		script: path.join(__dirname, 'temp/dist/index.js'),
		watch: [
			"app"
		],
		ignore: [
			"*test.ts"
		],
		ext: 'ts tsx pcss',
		verbose: true,
		tasks: changedFiles => {
			let exts = _.map(changedFiles, (p) => {
				return path.extname(p)
			})
			exts = _.uniq(exts)

			let tasks = []
			if(_.includes(exts, '.pcss')) {
				tasks.push('compile-changed-pcss')
			}

			if(_.includes(exts, '.ts') || _.includes(exts, '.tsx')) {
				tasks.push('compile-and-bundle-changed-ts')
			}
			tasks.push('reload-client')

			return tasks
		}
	})
	.on('start', () => {
		if(!started) {
			started = true
			done()
		}
	})
})

gulp.task('compile-changed-ts', done => {
	opts.compileOnlyChangedTs = true
	seq(
		'compile-ts',
		done
	)
})

gulp.task('compile-and-bundle-changed-ts', done => {
	seq(
		['compile-changed-ts', 'compile-changed-ts-to-es6'],
		'bundle-client-js',
		done
	)
})

gulp.task('compile-changed-pcss', done => {
	opts.compileOnlyChangedPcss = true
	seq(
		'compile-postcss',
		done
	)
})

gulp.task('restart-server', done => {
	server.emit('restart')
	done()
})

// Browser Sync

gulp.task('init-browser-sync', done => {
	bs.init({
		proxy: {
			target: "localhost:3000",
			ws: true,
		},
		files: "temp/dist/**/*",
		browser: ["firefox", "chrome", "iexplore", path.join(__dirname, ".bin", "edge.exe")], /* edge launcher from https://github.com/MicrosoftEdge/edge-launcher */
		port: 5101,
	})
	done()
})

gulp.task('reload-client', () => {
	return bs.reload({stream: true})
})

//
// Unit Tests
//
gulp.task('run-unit-test', (done) => {
	var test = require('./test/current.js')
	test = path.join(__dirname, opts.distApp, test)
	console.log(test)
	exec(`node ${test}|.\\node_modules\\.bin\\tap-spec`, (err, stdout, stderr) => {
		console.log(stdout)
		console.log(stderr)
		done()
	})
})<|endoftext|>


<commit_before>module.exports = {

  getFirst: function (images) {
    var image;

    if(images && images.length > 0){
      image = images[0];
    }else{
      image = "https://d1luk0418egahw.cloudfront.net/static/images/guide/NoImage_592x444.jpg"
    }

    return image;
  },

  getSize: function(image, type){
    var filename = image.split('.');
    var name = filename[0];
    var ext = filename[1];
    var final =  name + "-" + type + "." + ext;

    return final;
  },

  upload: function(req, product, callback){
    var Jimp = require("jimp");
    var rootPath = sails.config.appPath + '/assets/' + sails.config.views.theme;

      req.file('imagesUploader').upload({
        dirname: rootPath + '/images/upload'
      },
      function whenDone(err, uploadedFiles) {
        if (err) {
          sails.log.error(err);
          callback(err, null);
        }else{

          async.each(uploadedFiles, function(uploadedFile, callback) {
            var filePath = uploadedFile.fd.split('/');
            var filename = filePath[filePath.length-1].split('.');
            var name = filename[0];
            var ext = filename[1];
            var file = name + "." + ext;

            product.images.push('/images/upload/' + file);

            //CROP & THUMB
            Jimp.read(rootPath + '/images/upload/' + file, function (err, img) {
              var imgThumb = img.clone();
              var imgCrop = img.clone();

              imgThumb.resize(400, Jimp.AUTO).write(rootPath + '/images/upload/' + name + "-thumb-med." + ext);
              imgThumb.resize(150, Jimp.AUTO).write(rootPath + '/images/upload/' + name + "-thumb-small." + ext);
              imgCrop.cover(400,400).write(rootPath + '/images/upload/' + name + "-crop-med." + ext);
              imgCrop.cover(150,150).write(rootPath + '/images/upload/' + name + "-crop-small." + ext);
              callback();
            });

          }, function(err){
              if( err ) {
                console.log('Image & crop ERROR');
              }
          });

          product.save();

          callback(null, product);
        }
      });
  }

}

/*var fs = require('fs');

module.exports = {

  getFirst: function (images) {
    var image;

    if(images && images.length > 0){
      image = images[0];
    }else{
      image = "https://d1luk0418egahw.cloudfront.net/static/images/guide/NoImage_592x444.jpg"
    }

    return image;
  },

  getSize: function(image, type){
    var filename = image.split('.');
    var name = filename[0];
    var ext = filename[1];
    var final =  name + "-" + type + "." + ext;

    return final;
  },

  upload: function(req, product, callback){
    var Jimp = require("jimp");
    var rootPath = sails.config.appPath + '/assets/' + sails.config.views.theme;

      req.file('imagesUploader').upload({
        dirname: rootPath + '/images/upload'
      },
      function whenDone(err, uploadedFiles) {
        if (err) {
          sails.log.error(err);
          callback(err, null);
        }else{

          async.each(uploadedFiles, function(uploadedFile, callback) {
            var filePath = uploadedFile.fd.split('/');
            var filename = filePath[filePath.length-1].split('.');
            var name = filename[0];
            var ext = filename[1];
            var file = name + "." + ext;

            product.images.push('/images/upload/' + file);

            //CROP & THUMB
            Jimp.read(rootPath + '/images/upload/' + file, function (err, img) {
              var imgThumb = img.clone();
              var imgCrop = img.clone();

              var fname = rootPath + '/images/upload/' + name + "-thumb-med." + ext;
              imgThumb.resize(400, Jimp.AUTO).write(fname);
              fs.createReadStream(fname).pipe(fs.createWriteStream(fname.replace('/assets/' + sails.config.views.theme+'/','/.tmp/public/')));

              fname = rootPath + '/images/upload/' + name + "-thumb-small." + ext;
              imgThumb.resize(150, Jimp.AUTO).write(fname);
              fs.createReadStream(fname).pipe(fs.createWriteStream(fname.replace('/assets/' + sails.config.views.theme+'/','/.tmp/public/')));

              fname = rootPath + '/images/upload/' + name + "-crop-med." + ext;
              imgCrop.cover(400,400).write(fname);
              fs.createReadStream(fname).pipe(fs.createWriteStream(fname.replace('/assets/' + sails.config.views.theme+'/','/.tmp/public/')));

              fname = rootPath + '/images/upload/' + name + "-crop-small." + ext;
              imgCrop.cover(150,150).write(fname);
              fs.createReadStream(fname).pipe(fs.createWriteStream(fname.replace('/assets/' + sails.config.views.theme+'/','/.tmp/public/')));
              callback();
            });

          }, function(err){
              if( err ) {
                console.log('Image & crop ERROR');
              }
          });

          product.save();

          callback(null, product);
        }
      });
  }

}*/<commit_msg>updating image<commit_after>/*module.exports = {

  getFirst: function (images) {
    var image;

    if(images && images.length > 0){
      image = images[0];
    }else{
      image = "https://d1luk0418egahw.cloudfront.net/static/images/guide/NoImage_592x444.jpg"
    }

    return image;
  },

  getSize: function(image, type){
    var filename = image.split('.');
    var name = filename[0];
    var ext = filename[1];
    var final =  name + "-" + type + "." + ext;

    return final;
  },

  upload: function(req, product, callback){
    var Jimp = require("jimp");
    var rootPath = sails.config.appPath + '/assets/' + sails.config.views.theme;

      req.file('imagesUploader').upload({
        dirname: rootPath + '/images/upload'
      },
      function whenDone(err, uploadedFiles) {
        if (err) {
          sails.log.error(err);
          callback(err, null);
        }else{

          async.each(uploadedFiles, function(uploadedFile, callback) {
            var filePath = uploadedFile.fd.split('/');
            var filename = filePath[filePath.length-1].split('.');
            var name = filename[0];
            var ext = filename[1];
            var file = name + "." + ext;

            product.images.push('/images/upload/' + file);

            //CROP & THUMB
            Jimp.read(rootPath + '/images/upload/' + file, function (err, img) {
              var imgThumb = img.clone();
              var imgCrop = img.clone();

              imgThumb.resize(400, Jimp.AUTO).write(rootPath + '/images/upload/' + name + "-thumb-med." + ext);
              imgThumb.resize(150, Jimp.AUTO).write(rootPath + '/images/upload/' + name + "-thumb-small." + ext);
              imgCrop.cover(400,400).write(rootPath + '/images/upload/' + name + "-crop-med." + ext);
              imgCrop.cover(150,150).write(rootPath + '/images/upload/' + name + "-crop-small." + ext);
              callback();
            });

          }, function(err){
              if( err ) {
                console.log('Image & crop ERROR');
              }
          });

          product.save();

          callback(null, product);
        }
      });
  }

}*/

var fs = require('fs');

module.exports = {

  getFirst: function (images) {
    var image;

    if(images && images.length > 0){
      image = images[0];
    }else{
      image = "https://d1luk0418egahw.cloudfront.net/static/images/guide/NoImage_592x444.jpg"
    }

    return image;
  },

  getSize: function(image, type){
    var filename = image.split('.');
    var name = filename[0];
    var ext = filename[1];
    var final =  name + "-" + type + "." + ext;

    return final;
  },

  upload: function(req, product, callback){
    var Jimp = require("jimp");
    var rootPath = sails.config.appPath + '/assets/' + sails.config.views.theme;

      req.file('imagesUploader').upload({
        dirname: rootPath + '/images/upload'
      },
      function whenDone(err, uploadedFiles) {
        if (err) {
          sails.log.error(err);
          callback(err, null);
        }else{

          async.each(uploadedFiles, function(uploadedFile, callback) {
            var filePath = uploadedFile.fd.split('/');
            var filename = filePath[filePath.length-1].split('.');
            var name = filename[0];
            var ext = filename[1];
            var file = name + "." + ext;

            product.images.push('/images/upload/' + file);

            //CROP & THUMB
            Jimp.read(rootPath + '/images/upload/' + file, function (err, img) {
              var imgThumb = img.clone();
              var imgCrop = img.clone();

              var fname1 = rootPath + '/images/upload/' + name + "-thumb-med." + ext;
              imgThumb.resize(400, Jimp.AUTO).write(fname1, function(){
                fs.createReadStream(fname1).pipe(fs.createWriteStream(fname1.replace('/assets/'+sails.config.views.theme+'/', '/.tmp/public/')));
              };

              var fname2 = rootPath + '/images/upload/' + name + "-thumb-small." + ext;
              imgThumb.resize(150, Jimp.AUTO).write(fname2, function(){
                fs.createReadStream(fname2).pipe(fs.createWriteStream(fname2.replace('/assets/'+sails.config.views.theme+'/', '/.tmp/public/')));
              });

              var fname3 = rootPath + '/images/upload/' + name + "-crop-med." + ext;
              imgCrop.cover(400,400).write(fname3, function(){
                fs.createReadStream(fname3).pipe(fs.createWriteStream(fname3.replace('/assets/'+sails.config.views.theme+'/', '/.tmp/public/')));
              });

              var fname4 = rootPath + '/images/upload/' + name + "-crop-small." + ext;
              imgCrop.cover(150,150).write(fname4, function(){
                fs.createReadStream(fname4).pipe(fs.createWriteStream(fname4.replace('/assets/'+sails.config.views.theme+'/', '/.tmp/public/')));
              });
              callback();
            });

          }, function(err){
              if( err ) {
                console.log('Image & crop ERROR');
              }
          });

          product.save();

          callback(null, product);
        }
      });
  }

}<|endoftext|>