<commit_before>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class coinmarketcap extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'coinmarketcap',
            'name': 'CoinMarketCap',
            'rateLimit': 10000,
            'version': 'v1',
            'countries': [ 'US' ],
            'has': {
                'cancelOrder': false,
                'CORS': true,
                'createLimitOrder': false,
                'createMarketOrder': false,
                'createOrder': false,
                'editOrder': false,
                'privateAPI': false,
                'fetchBalance': false,
                'fetchCurrencies': true,
                'fetchL2OrderBook': false,
                'fetchMarkets': true,
                'fetchOHLCV': false,
                'fetchOrderBook': false,
                'fetchTicker': true,
                'fetchTickers': true,
                'fetchTrades': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/87182086-1cd4cd00-c2ec-11ea-9ec4-d0cf2a2abf62.jpg',
                'api': {
                    'public': 'https://api.coinmarketcap.com',
                    'files': 'https://files.coinmarketcap.com',
                    'charts': 'https://graph.coinmarketcap.com',
                },
                'www': 'https://coinmarketcap.com',
                'doc': 'https://coinmarketcap.com/api',
            },
            'requiredCredentials': {
                'apiKey': false,
                'secret': false,
            },
            'api': {
                'files': {
                    'get': [
                        'generated/stats/global.json',
                    ],
                },
                'graphs': {
                    'get': [
                        'currencies/{name}/',
                    ],
                },
                'public': {
                    'get': [
                        'ticker/',
                        'ticker/{id}/',
                        'global/',
                    ],
                },
            },
            'currencyCodes': [
                'AUD',
                'BRL',
                'CAD',
                'CHF',
                'CNY',
                'EUR',
                'GBP',
                'HKD',
                'IDR',
                'INR',
                'JPY',
                'KRW',
                'MXN',
                'RUB',
                'USD',
                'BTC',
                'ETH',
                'LTC',
            ],
        });
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        throw new ExchangeError ('Fetching order books is not supported by the API of ' + this.id);
    }

    currencyCode (base, name) {
        const currencies = {
            'ACChain': 'ACChain',
            'AdCoin': 'AdCoin',
            'BatCoin': 'BatCoin',
            'BigONE Token': 'BigONE Token', // conflict with Harmony (ONE)
            'Bitgem': 'Bitgem',
            'BlazeCoin': 'BlazeCoin',
            'BlockCAT': 'BlockCAT',
            'Blocktrade Token': 'Blocktrade Token',
            'BOX Token': 'BOX Token', // conflict with BOX (ContentBox)
            'Catcoin': 'Catcoin',
            'CanYaCoin': 'CanYaCoin', // conflict with CAN (Content and AD Network)
            'CryptoBossCoin': 'CryptoBossCoin', // conflict with CBC (CashBet Coin)
            'Comet': 'Comet', // conflict with CMT (CyberMiles)
            'CPChain': 'CPChain',
            'CrowdCoin': 'CrowdCoin', // conflict with CRC CryCash
            'Cryptaur': 'Cryptaur', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081
            'Cubits': 'Cubits', // conflict with QBT (Qbao)
            'DAO.Casino': 'DAO.Casino', // conflict with BET (BetaCoin)
            'DefiBox': 'DefiBox', // conflict with BOX (ContentBox)
            'E-Dinar Coin': 'E-Dinar Coin', // conflict with EDR Endor Protocol and EDRCoin
            'EDRcoin': 'EDRcoin', // conflict with EDR Endor Protocol and E-Dinar Coin
            'ENTCash': 'ENTCash', // conflict with ENT (Eternity)
            'FairCoin': 'FairCoin', // conflict with FAIR (FairGame) https://github.com/ccxt/ccxt/pull/5865
            'Fabric Token': 'Fabric Token',
            // 'GET Protocol': 'GET Protocol',
            'GHOSTPRISM': 'GHOSTPRISM', // conflict with GHOST
            'Global Tour Coin': 'Global Tour Coin', // conflict with GTC (Game.com)
            'GuccioneCoin': 'GuccioneCoin', // conflict with GCC (Global Cryptocurrency)
            'HarmonyCoin': 'HarmonyCoin', // conflict with HMC (Hi Mutual Society)
            'Harvest Masternode Coin': 'Harvest Masternode Coin', // conflict with HC (HyperCash)
            'HOT Token': 'HOT Token',
            'Hydro Protocol': 'Hydro Protocol', // conflict with HOT (Holo)
            'Huncoin': 'Huncoin', // conflict with HNC (Helleniccoin)
            'iCoin': 'iCoin',
            'Infinity Economics': 'Infinity Economics', // conflict with XIN (Mixin)
            'IQ.cash': 'IQ.cash', // conflict with IQ (Everipedia)
            'KingN Coin': 'KingN Coin', // conflict with KNC (Kyber Network)
            'LiteBitcoin': 'LiteBitcoin', // conflict with LBTC (LightningBitcoin)
            'Maggie': 'Maggie',
            'Menlo One': 'Menlo One', // conflict with Harmony (ONE)
            'Mobilian Coin': 'Mobilian Coin', // conflict with Membrana (MBN)
            'Monarch': 'Monarch', // conflict with MyToken (MT)
            'MTC Mesh Network': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025
            'IOTA': 'IOTA', // a special case, most exchanges list it as IOTA, therefore we change just the Coinmarketcap instead of changing them all
            'NetCoin': 'NetCoin',
            'PCHAIN': 'PCHAIN', // conflict with PAI (Project Pai)
            'Penta': 'Penta', // conflict with PNT (pNetwork)
            'Plair': 'Plair', // conflict with PLA (PLANET)
            'PlayChip': 'PlayChip', // conflict with PLA (PLANET)
            'Polcoin': 'Polcoin',
            'PutinCoin': 'PutinCoin', // conflict with PUT (Profile Utility Token)
            'Rcoin': 'Rcoin', // conflict with RCN (Ripio Credit Network)
            // https://github.com/ccxt/ccxt/issues/6081
            // https://github.com/ccxt/ccxt/issues/3365
            // https://github.com/ccxt/ccxt/issues/2873
            'SBTCT': 'SiamBitcoin', // conflict with sBTC
            'Super Bitcoin': 'Super Bitcoin', // conflict with sBTC
            'TerraCredit': 'TerraCredit', // conflict with CREDIT (PROXI)
            'Themis': 'Themis', // conflict with GET (Guaranteed Entrance Token, GET Protocol)
            'UNI COIN': 'UNI COIN', // conflict with UNI (Uniswap)
            'UNICORN Token': 'UNICORN Token', // conflict with UNI (Uniswap)
            'Universe': 'Universe', // conflict with UNI (Uniswap)
        };
        return this.safeValue (currencies, name, base);
    }

    async fetchMarkets (params = {}) {
        const request = {
            'limit': 0,
        };
        const response = await this.publicGetTicker (this.extend (request, params));
        const result = [];
        for (let i = 0; i < response.length; i++) {
            const market = response[i];
            const currencies = this.currencyCodes;
            for (let j = 0; j < currencies.length; j++) {
                const quote = currencies[j];
                const quoteId = quote.toLowerCase ();
                const baseId = market['id'];
                const base = this.currencyCode (market['symbol'], market['name']);
                const symbol = base + '/' + quote;
                const id = baseId + '/' + quoteId;
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': market,
                    'active': undefined,
                    'precision': this.precision,
                    'limits': this.limits,
                });
            }
        }
        return result;
    }

    async fetchGlobal (currency = 'USD') {
        await this.loadMarkets ();
        const request = {};
        if (currency) {
            request['convert'] = currency;
        }
        return await this.publicGetGlobal (request);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.safeTimestamp (ticker, 'last_updated');
        if (timestamp === undefined) {
            timestamp = this.milliseconds ();
        }
        const change = this.safeNumber (ticker, 'percent_change_24h');
        let last = undefined;
        let symbol = undefined;
        let volume = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
            const priceKey = 'price_' + market['quoteId'];
            last = this.safeNumber (ticker, priceKey);
            const volumeKey = '24h_volume_' + market['quoteId'];
            volume = this.safeNumber (ticker, volumeKey);
        }
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': undefined,
            'low': undefined,
            'bid': undefined,
            'bidVolume': undefined,
            'ask': undefined,
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': change,
            'average': undefined,
            'baseVolume': undefined,
            'quoteVolume': volume,
            'info': ticker,
        };
    }

    async fetchTickers (currency = 'USD', params = {}) {
        await this.loadMarkets ();
        const request = {
            'limit': 10000,
        };
        if (currency) {
            request['convert'] = currency;
        }
        const response = await this.publicGetTicker (this.extend (request, params));
        const result = {};
        for (let t = 0; t < response.length; t++) {
            const ticker = response[t];
            const currencyId = currency.toLowerCase ();
            const id = ticker['id'] + '/' + currencyId;
            let symbol = id;
            let market = undefined;
            if (id in this.markets_by_id) {
                market = this.markets_by_id[id];
                symbol = market['symbol'];
            }
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'convert': market['quote'],
            'id': market['baseId'],
        };
        const response = await this.publicGetTickerId (this.extend (request, params));
        const ticker = response[0];
        return this.parseTicker (ticker, market);
    }

    async fetchCurrencies (params = {}) {
        const request = {
            'limit': 0,
        };
        const response = await this.publicGetTicker (this.extend (request, params));
        const result = {};
        for (let i = 0; i < response.length; i++) {
            const currency = response[i];
            const id = this.safeString (currency, 'symbol');
            const name = this.safeString (currency, 'name');
            // todo: will need to rethink the fees
            // to add support for multiple withdrawal/deposit methods and
            // differentiated fees for each particular method
            const code = this.currencyCode (id, name);
            result[code] = {
                'id': id,
                'code': code,
                'info': currency,
                'name': name,
                'active': true,
                'fee': undefined,
                'precision': undefined,
                'limits': {
                    'amount': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'price': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'cost': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'withdraw': {
                        'min': undefined,
                        'max': undefined,
                    },
                },
            };
        }
        return result;
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams (path, params);
        const query = this.omit (params, this.extractParams (path));
        if (Object.keys (query).length) {
            url += '?' + this.urlencode (query);
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        const response = await this.fetch2 (path, api, method, params, headers, body);
        if ('error' in response) {
            if (response['error']) {
                throw new ExchangeError (this.id + ' ' + this.json (response));
            }
        }
        return response;
    }
};
<commit_msg>cmc request rate limiter config and context<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class coinmarketcap extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'coinmarketcap',
            'name': 'CoinMarketCap',
            'rateLimit': 10000,
            'version': 'v1',
            'countries': [ 'US' ],
            'has': {
                'cancelOrder': false,
                'CORS': true,
                'createLimitOrder': false,
                'createMarketOrder': false,
                'createOrder': false,
                'editOrder': false,
                'privateAPI': false,
                'fetchBalance': false,
                'fetchCurrencies': true,
                'fetchL2OrderBook': false,
                'fetchMarkets': true,
                'fetchOHLCV': false,
                'fetchOrderBook': false,
                'fetchTicker': true,
                'fetchTickers': true,
                'fetchTrades': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/51840849/87182086-1cd4cd00-c2ec-11ea-9ec4-d0cf2a2abf62.jpg',
                'api': {
                    'public': 'https://api.coinmarketcap.com',
                    'files': 'https://files.coinmarketcap.com',
                    'charts': 'https://graph.coinmarketcap.com',
                },
                'www': 'https://coinmarketcap.com',
                'doc': 'https://coinmarketcap.com/api',
            },
            'requiredCredentials': {
                'apiKey': false,
                'secret': false,
            },
            'api': {
                'files': {
                    'get': [
                        'generated/stats/global.json',
                    ],
                },
                'graphs': {
                    'get': [
                        'currencies/{name}/',
                    ],
                },
                'public': {
                    'get': [
                        'ticker/',
                        'ticker/{id}/',
                        'global/',
                    ],
                },
            },
            'currencyCodes': [
                'AUD',
                'BRL',
                'CAD',
                'CHF',
                'CNY',
                'EUR',
                'GBP',
                'HKD',
                'IDR',
                'INR',
                'JPY',
                'KRW',
                'MXN',
                'RUB',
                'USD',
                'BTC',
                'ETH',
                'LTC',
            ],
        });
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        throw new ExchangeError ('Fetching order books is not supported by the API of ' + this.id);
    }

    currencyCode (base, name) {
        const currencies = {
            'ACChain': 'ACChain',
            'AdCoin': 'AdCoin',
            'BatCoin': 'BatCoin',
            'BigONE Token': 'BigONE Token', // conflict with Harmony (ONE)
            'Bitgem': 'Bitgem',
            'BlazeCoin': 'BlazeCoin',
            'BlockCAT': 'BlockCAT',
            'Blocktrade Token': 'Blocktrade Token',
            'BOX Token': 'BOX Token', // conflict with BOX (ContentBox)
            'Catcoin': 'Catcoin',
            'CanYaCoin': 'CanYaCoin', // conflict with CAN (Content and AD Network)
            'CryptoBossCoin': 'CryptoBossCoin', // conflict with CBC (CashBet Coin)
            'Comet': 'Comet', // conflict with CMT (CyberMiles)
            'CPChain': 'CPChain',
            'CrowdCoin': 'CrowdCoin', // conflict with CRC CryCash
            'Cryptaur': 'Cryptaur', // conflict with CPT = Contents Protocol https://github.com/ccxt/ccxt/issues/4920 and https://github.com/ccxt/ccxt/issues/6081
            'Cubits': 'Cubits', // conflict with QBT (Qbao)
            'DAO.Casino': 'DAO.Casino', // conflict with BET (BetaCoin)
            'DefiBox': 'DefiBox', // conflict with BOX (ContentBox)
            'E-Dinar Coin': 'E-Dinar Coin', // conflict with EDR Endor Protocol and EDRCoin
            'EDRcoin': 'EDRcoin', // conflict with EDR Endor Protocol and E-Dinar Coin
            'ENTCash': 'ENTCash', // conflict with ENT (Eternity)
            'FairCoin': 'FairCoin', // conflict with FAIR (FairGame) https://github.com/ccxt/ccxt/pull/5865
            'Fabric Token': 'Fabric Token',
            // 'GET Protocol': 'GET Protocol',
            'GHOSTPRISM': 'GHOSTPRISM', // conflict with GHOST
            'Global Tour Coin': 'Global Tour Coin', // conflict with GTC (Game.com)
            'GuccioneCoin': 'GuccioneCoin', // conflict with GCC (Global Cryptocurrency)
            'HarmonyCoin': 'HarmonyCoin', // conflict with HMC (Hi Mutual Society)
            'Harvest Masternode Coin': 'Harvest Masternode Coin', // conflict with HC (HyperCash)
            'HOT Token': 'HOT Token',
            'Hydro Protocol': 'Hydro Protocol', // conflict with HOT (Holo)
            'Huncoin': 'Huncoin', // conflict with HNC (Helleniccoin)
            'iCoin': 'iCoin',
            'Infinity Economics': 'Infinity Economics', // conflict with XIN (Mixin)
            'IQ.cash': 'IQ.cash', // conflict with IQ (Everipedia)
            'KingN Coin': 'KingN Coin', // conflict with KNC (Kyber Network)
            'LiteBitcoin': 'LiteBitcoin', // conflict with LBTC (LightningBitcoin)
            'Maggie': 'Maggie',
            'Menlo One': 'Menlo One', // conflict with Harmony (ONE)
            'Mobilian Coin': 'Mobilian Coin', // conflict with Membrana (MBN)
            'Monarch': 'Monarch', // conflict with MyToken (MT)
            'MTC Mesh Network': 'MTC Mesh Network', // conflict with MTC Docademic doc.com Token https://github.com/ccxt/ccxt/issues/6081 https://github.com/ccxt/ccxt/issues/3025
            'IOTA': 'IOTA', // a special case, most exchanges list it as IOTA, therefore we change just the Coinmarketcap instead of changing them all
            'NetCoin': 'NetCoin',
            'PCHAIN': 'PCHAIN', // conflict with PAI (Project Pai)
            'Penta': 'Penta', // conflict with PNT (pNetwork)
            'Plair': 'Plair', // conflict with PLA (PLANET)
            'PlayChip': 'PlayChip', // conflict with PLA (PLANET)
            'Polcoin': 'Polcoin',
            'PutinCoin': 'PutinCoin', // conflict with PUT (Profile Utility Token)
            'Rcoin': 'Rcoin', // conflict with RCN (Ripio Credit Network)
            // https://github.com/ccxt/ccxt/issues/6081
            // https://github.com/ccxt/ccxt/issues/3365
            // https://github.com/ccxt/ccxt/issues/2873
            'SBTCT': 'SiamBitcoin', // conflict with sBTC
            'Super Bitcoin': 'Super Bitcoin', // conflict with sBTC
            'TerraCredit': 'TerraCredit', // conflict with CREDIT (PROXI)
            'Themis': 'Themis', // conflict with GET (Guaranteed Entrance Token, GET Protocol)
            'UNI COIN': 'UNI COIN', // conflict with UNI (Uniswap)
            'UNICORN Token': 'UNICORN Token', // conflict with UNI (Uniswap)
            'Universe': 'Universe', // conflict with UNI (Uniswap)
        };
        return this.safeValue (currencies, name, base);
    }

    async fetchMarkets (params = {}) {
        const request = {
            'limit': 0,
        };
        const response = await this.publicGetTicker (this.extend (request, params));
        const result = [];
        for (let i = 0; i < response.length; i++) {
            const market = response[i];
            const currencies = this.currencyCodes;
            for (let j = 0; j < currencies.length; j++) {
                const quote = currencies[j];
                const quoteId = quote.toLowerCase ();
                const baseId = market['id'];
                const base = this.currencyCode (market['symbol'], market['name']);
                const symbol = base + '/' + quote;
                const id = baseId + '/' + quoteId;
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': market,
                    'active': undefined,
                    'precision': this.precision,
                    'limits': this.limits,
                });
            }
        }
        return result;
    }

    async fetchGlobal (currency = 'USD') {
        await this.loadMarkets ();
        const request = {};
        if (currency) {
            request['convert'] = currency;
        }
        return await this.publicGetGlobal (request);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.safeTimestamp (ticker, 'last_updated');
        if (timestamp === undefined) {
            timestamp = this.milliseconds ();
        }
        const change = this.safeNumber (ticker, 'percent_change_24h');
        let last = undefined;
        let symbol = undefined;
        let volume = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
            const priceKey = 'price_' + market['quoteId'];
            last = this.safeNumber (ticker, priceKey);
            const volumeKey = '24h_volume_' + market['quoteId'];
            volume = this.safeNumber (ticker, volumeKey);
        }
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': undefined,
            'low': undefined,
            'bid': undefined,
            'bidVolume': undefined,
            'ask': undefined,
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': change,
            'average': undefined,
            'baseVolume': undefined,
            'quoteVolume': volume,
            'info': ticker,
        };
    }

    async fetchTickers (currency = 'USD', params = {}) {
        await this.loadMarkets ();
        const request = {
            'limit': 10000,
        };
        if (currency) {
            request['convert'] = currency;
        }
        const response = await this.publicGetTicker (this.extend (request, params));
        const result = {};
        for (let t = 0; t < response.length; t++) {
            const ticker = response[t];
            const currencyId = currency.toLowerCase ();
            const id = ticker['id'] + '/' + currencyId;
            let symbol = id;
            let market = undefined;
            if (id in this.markets_by_id) {
                market = this.markets_by_id[id];
                symbol = market['symbol'];
            }
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'convert': market['quote'],
            'id': market['baseId'],
        };
        const response = await this.publicGetTickerId (this.extend (request, params));
        const ticker = response[0];
        return this.parseTicker (ticker, market);
    }

    async fetchCurrencies (params = {}) {
        const request = {
            'limit': 0,
        };
        const response = await this.publicGetTicker (this.extend (request, params));
        const result = {};
        for (let i = 0; i < response.length; i++) {
            const currency = response[i];
            const id = this.safeString (currency, 'symbol');
            const name = this.safeString (currency, 'name');
            // todo: will need to rethink the fees
            // to add support for multiple withdrawal/deposit methods and
            // differentiated fees for each particular method
            const code = this.currencyCode (id, name);
            result[code] = {
                'id': id,
                'code': code,
                'info': currency,
                'name': name,
                'active': true,
                'fee': undefined,
                'precision': undefined,
                'limits': {
                    'amount': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'price': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'cost': {
                        'min': undefined,
                        'max': undefined,
                    },
                    'withdraw': {
                        'min': undefined,
                        'max': undefined,
                    },
                },
            };
        }
        return result;
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams (path, params);
        const query = this.omit (params, this.extractParams (path));
        if (Object.keys (query).length) {
            url += '?' + this.urlencode (query);
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined, context = {}, config = {}) {
        const response = await this.fetch2 (path, api, method, params, headers, body, context, config);
        if ('error' in response) {
            if (response['error']) {
                throw new ExchangeError (this.id + ' ' + this.json (response));
            }
        }
        return response;
    }
};
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const Cu = Components.utils;

Cu.import("resource://react/lib/ical.1.2.2.js");

const description = "IndexedDB based Addressbook";
const UUID = "{7FBFCC76-48B6-11E6-9353-C50CE7BF5E87}";
const contractID = "@maddrbook/Addressbook;1";

const DB_NAME = 'addrbook';
const CONTACTS_STORE_NAME = 'contacts';
const DB_VERSION = 2;

const DB_ERR_NOT_CONN = "Connection to the database has not been opened, please make sure you called Addressbook.open()";

/**
* @constructor
* @param idb - IndexedDB Factory
**/
function Addressbook(idb) {
  this.indexedDB = idb;
};

/**
* Creates and opens databse.
* @param idb - IndexedDB Factory
**/
Addressbook.open = function(idb) {
  return new Addressbook(idb).open();
}

Addressbook.prototype = {
  // properties required for XPCOM registration:
  classDescription: description,
  classID:          Components.ID(UUID),
  contractID:       contractID,

/**
* Open connection with db.
* @returns {Promise} - promise of open connection to db.
**/
  open: function() {
    let addrbook = this;
    let indexedDB = this.indexedDB;

    return new Promise(function(resolve, reject) {
      var request = indexedDB.open(DB_NAME, DB_VERSION);

      request.onsuccess = function(event) {
        addrbook._onsuccess(event);
        resolve(addrbook);
      };

      request.onerror = function(event) {
        addrbook._onerror(event);
        reject(event.target.error);
      };

      request.onupgradeneeded = function(event) {
        addrbook._onupgradeneeded(event);
      };
    });
  },

  _onsuccess: function(event) {
    this._db = event.target.result;

    if (this.onsuccess !== undefined) {
      this.onsuccess(event);
    }
  },
  _onerror: function(event) {

    if (this.onerror !== undefined) {
      this.onerror(event);
    }
  },
  _onupgradeneeded: function(event) {
    var db = event.target.result;

    // setup our object stores
    var contactsStore = db.createObjectStore(CONTACTS_STORE_NAME,
        { keyPath: "uuid", autoIncrement: true });

  /**
  * Set up indexes for db
  **/
    contactsStore.createIndex("name", "name", { unique: false });
  /**
  * Set up seed data for mock db
  **/
    contactsStore.add({name: "Simon Perreault", email: "simon.perreault@viagenie.ca" , jcards: [
      ["vcard", [
        ["version", {}, "text", "4.0"],
        ["fn", {}, "text", "Simon Perreault"],
        ["n", {}, "text", ["Perreault", "Simon", "", "", ["ing. jr", "M.Sc."]] ],
        ["bday", {}, "date-and-or-time", "--02-03"],
        ["anniversary", {}, "date-and-or-time", "2009-08-08T14:30:00-05:00" ],
        ["gender", {}, "text", "M"],
        ["lang", { "pref": "1" }, "language-tag", "fr"],
        ["lang", { "pref": "2" }, "language-tag", "en"],
        ["org", { "type": "work" }, "text", "Viagenie"],
        ["adr", { "type": "work" }, "text", [ "", "Suite D2-630", "2875 Laurier", "Quebec", "QC", "G1V 2M2", "Canada" ] ],
        ["tel", { "type": ["work", "voice"], "pref": "1" }, "uri", "tel:+1-418-656-9254;ext=102" ],
        ["tel", { "type": ["work", "cell", "voice", "video", "text"] }, "uri", "tel:+1-418-262-6501" ],
        ["email", { "type": "work" }, "text", "simon.perreault@viagenie.ca" ],
        ["geo", { "type": "work" }, "uri", "geo:46.772673,-71.282945"],
        ["key", { "type": "work" }, "uri", "http://www.viagenie.ca/simon.perreault/simon.asc" ],
        ["tz", {}, "utc-offset", "-05:00"],
        ["url", { "type": "home" }, "uri", "http://nomis80.org"]
      ]]
    ]});

    contactsStore.add({name: "Bob Perreault", email: "bob.perreault@viagenie.ca" , jcards: [
      ["vcard", [
        ["version", {}, "text", "4.0"],
        ["fn", {}, "text", "Bob Perreault"],
        ["n", {}, "text", ["Perreault", "Bob", "", "", ["ing. jr", "M.Sc."]] ],
        ["bday", {}, "date-and-or-time", "--02-03"],
        ["anniversary", {}, "date-and-or-time", "2009-08-08T14:30:00-05:00" ],
        ["gender", {}, "text", "M"],
        ["lang", { "pref": "1" }, "language-tag", "fr"],
        ["lang", { "pref": "2" }, "language-tag", "en"],
        ["org", { "type": "work" }, "text", "Viagenie"],
        ["adr", { "type": "work" }, "text", [ "", "Suite D2-630", "2875 Laurier", "Quebec", "QC", "G1V 2M2", "Canada" ] ],
        ["tel", { "type": ["work", "voice"], "pref": "1" }, "uri", "tel:+1-418-656-9254;ext=102" ],
        ["tel", { "type": ["work", "cell", "voice", "video", "text"] }, "uri", "tel:+1-418-262-6501" ],
        ["email", { "type": "work" }, "text", "bob.perreault@viagenie.ca" ],
        ["geo", { "type": "work" }, "uri", "geo:46.772673,-71.282945"],
        ["key", { "type": "work" }, "uri", "http://www.viagenie.ca/bob.perreault/bob.asc" ],
        ["tz", {}, "utc-offset", "-05:00"],
        ["url", { "type": "home" }, "uri", "http://nomis80.org"]
      ]]
    ]});
  },

  add: function(rawContact) {
    return this._contactRequest("readwrite", function(transaction) {
      return transaction.add(rawContact);
    });
  },

  update: function(contact) {
    return this._contactRequest("readwrite",function(transaction) {
      return transaction.put(contact.toJSON());
    });
  },

  /**
  * Return all contacts in db.
  * @returns {Promise} of an array of all contact objects in the db.
  **/
  getAll: function() {
    return this._contactRequest("readonly",
        function(transaction) {
          return transaction.getAll();
        })
    .then(function(rawContacts) {
      return rawContacts.map(function(rawContact) {
        return new Contact(rawContact);
      });
    });
  },

  /**
  * Return all contacts in db.
  * @returns {Promise} of an array of all contact objects in the db.
  **/
  getAllNameIdAndPhoto: function() {
    return this._contactRequest("readonly",
        function(transaction) {
          return transaction.getAll();
        })
    .then(function(rawContacts) {
      return rawContacts.map(function(rawContact) {
        return {name: rawContact.name, id: rawContact.uuid, photo: ContactParser.getPhotoURL(rawContact.photo)};
      });
    });
  },

  /**
  * Return a contact.
  * @param id - id of contact required.
  * @return {Promise} of a contact
  **/
  getById: function(id) {
    return this._contactRequest("readonly",
        function(transaction) {
          return  transaction.get(id);
        })
    .then(function(rawContact) {
      return new Contact(rawContact);
    });
  },
  /**
  * Delete contact by id
  * @param {Integer} id - id of contact to be deleted.
  * @returns {Promise} to delete contact of input id
  **/
  deleteById: function(id) {
    return this._contactRequest("readwrite",function(transaction) { return  transaction.delete(id); } );
  },
  /**
  * Returns all names and IDs in the db.
  * @returns {Promise} - of all names and IDs in db.
  **/
  getNameAndId: function() {

    let db = this._db;

    return new Promise(function(resolve, reject) {

      // check to see if db exists
      if (db === undefined) {
        reject(DB_ERR_NOT_CONN);
        return;
      }

      // setup transaction
      var transaction = db.transaction([CONTACTS_STORE_NAME], "readonly")
        .objectStore(CONTACTS_STORE_NAME)
        .index("name");

      // create request
      var request = transaction.openKeyCursor();

      // initalise results
      var results = [];

      // setup response functions
      request.onsuccess = function(event) {
        var cursor = event.target.result;
        if (cursor) {
          results.push({ uuid: cursor.primaryKey, name: cursor.key });
          cursor.continue();
        } else {
          resolve(results);
        }
      };

      request.onerror = function(event) {
        reject(event.target.error);
      };
    });
  },

  /**
  * @param {string} access -  level of access to db needed.
  * @param {function} requestFn - takes an IDB transaction
  * @returns {Promise} - the result of the request function
  **/
  _contactRequest: function(access, requestFn) {

    let db = this._db;

    return new Promise(function(resolve, reject) {

      // check to see if db exists
      if (db === undefined) {
        reject(DB_ERR_NOT_CONN);
        return;
      }

      // setup transaction
      var transaction = db.transaction([CONTACTS_STORE_NAME], access)
        .objectStore(CONTACTS_STORE_NAME);

      // create request
      var request = requestFn(transaction);

      // setup response functions
      request.onerror = function(event) {
        reject(event.target.error);
      };
      request.onsuccess = function(event) {
        resolve(event.target.result);
      };
    });
  }
};

/**
 * @constructor
 */
function Contact(rawContact) {
  this.uuid = rawContact.uuid;
  this.name = rawContact.name;
  this.photo = rawContact.photo;
  this.jcards = this._convertFromRawJCard(rawContact.jcards);
};

Contact.prototype = {

  _convertFromRawJCard: function(jcards) {
    return jcards.map(function(jcard) {
      return new ICAL.Component(jcard);
    });
  },

  _convertToRawJCard: function() {

    return this.jcards.map(function(jcard) {
      return jcard.toJSON();
    });
  },

  toJSON: function() {
    return {
      uuid : this.uuid,
      name : this.name,
      photo: this.photo,
      jcards: this._convertToRawJCard()
    };
  }
};

// vim: set sw=2 ts=2 expandtab ft=javascript:
<commit_msg>add search functionality to database<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const Cu = Components.utils;

Cu.import("resource://react/lib/ical.1.2.2.js");

const description = "IndexedDB based Addressbook";
const UUID = "{7FBFCC76-48B6-11E6-9353-C50CE7BF5E87}";
const contractID = "@maddrbook/Addressbook;1";

const DB_NAME = 'addrbook';
const CONTACTS_STORE_NAME = 'contacts';
const DB_VERSION = 2;

const DB_ERR_NOT_CONN = "Connection to the database has not been opened, please make sure you called Addressbook.open()";

/**
* @constructor
* @param idb - IndexedDB Factory
**/
function Addressbook(idb) {
  this.indexedDB = idb;
};

/**
* Creates and opens databse.
* @param idb - IndexedDB Factory
**/
Addressbook.open = function(idb) {
  return new Addressbook(idb).open();
}

Addressbook.prototype = {
  // properties required for XPCOM registration:
  classDescription: description,
  classID:          Components.ID(UUID),
  contractID:       contractID,

/**
* Open connection with db.
* @returns {Promise} - promise of open connection to db.
**/
  open: function() {
    let addrbook = this;
    let indexedDB = this.indexedDB;

    return new Promise(function(resolve, reject) {
      var request = indexedDB.open(DB_NAME, DB_VERSION);

      request.onsuccess = function(event) {
        addrbook._onsuccess(event);
        resolve(addrbook);
      };

      request.onerror = function(event) {
        addrbook._onerror(event);
        reject(event.target.error);
      };

      request.onupgradeneeded = function(event) {
        addrbook._onupgradeneeded(event);
      };
    });
  },

  _onsuccess: function(event) {
    this._db = event.target.result;

    if (this.onsuccess !== undefined) {
      this.onsuccess(event);
    }
  },
  _onerror: function(event) {

    if (this.onerror !== undefined) {
      this.onerror(event);
    }
  },
  _onupgradeneeded: function(event) {
    var db = event.target.result;

    // setup our object stores
    var contactsStore = db.createObjectStore(CONTACTS_STORE_NAME,
        { keyPath: "uuid", autoIncrement: true });

  /**
  * Set up indexes for db
  **/
    contactsStore.createIndex("name", "name", { unique: false });
  /**
  * Set up seed data for mock db
  **/
    contactsStore.add({name: "Simon Perreault", email: "simon.perreault@viagenie.ca" , jcards: [
      ["vcard", [
        ["version", {}, "text", "4.0"],
        ["fn", {}, "text", "Simon Perreault"],
        ["n", {}, "text", ["Perreault", "Simon", "", "", ["ing. jr", "M.Sc."]] ],
        ["bday", {}, "date-and-or-time", "--02-03"],
        ["anniversary", {}, "date-and-or-time", "2009-08-08T14:30:00-05:00" ],
        ["gender", {}, "text", "M"],
        ["lang", { "pref": "1" }, "language-tag", "fr"],
        ["lang", { "pref": "2" }, "language-tag", "en"],
        ["org", { "type": "work" }, "text", "Viagenie"],
        ["adr", { "type": "work" }, "text", [ "", "Suite D2-630", "2875 Laurier", "Quebec", "QC", "G1V 2M2", "Canada" ] ],
        ["tel", { "type": ["work", "voice"], "pref": "1" }, "uri", "tel:+1-418-656-9254;ext=102" ],
        ["tel", { "type": ["work", "cell", "voice", "video", "text"] }, "uri", "tel:+1-418-262-6501" ],
        ["email", { "type": "work" }, "text", "simon.perreault@viagenie.ca" ],
        ["geo", { "type": "work" }, "uri", "geo:46.772673,-71.282945"],
        ["key", { "type": "work" }, "uri", "http://www.viagenie.ca/simon.perreault/simon.asc" ],
        ["tz", {}, "utc-offset", "-05:00"],
        ["url", { "type": "home" }, "uri", "http://nomis80.org"]
      ]]
    ]});

    contactsStore.add({name: "Bob Perreault", email: "bob.perreault@viagenie.ca" , jcards: [
      ["vcard", [
        ["version", {}, "text", "4.0"],
        ["fn", {}, "text", "Bob Perreault"],
        ["n", {}, "text", ["Perreault", "Bob", "", "", ["ing. jr", "M.Sc."]] ],
        ["bday", {}, "date-and-or-time", "--02-03"],
        ["anniversary", {}, "date-and-or-time", "2009-08-08T14:30:00-05:00" ],
        ["gender", {}, "text", "M"],
        ["lang", { "pref": "1" }, "language-tag", "fr"],
        ["lang", { "pref": "2" }, "language-tag", "en"],
        ["org", { "type": "work" }, "text", "Viagenie"],
        ["adr", { "type": "work" }, "text", [ "", "Suite D2-630", "2875 Laurier", "Quebec", "QC", "G1V 2M2", "Canada" ] ],
        ["tel", { "type": ["work", "voice"], "pref": "1" }, "uri", "tel:+1-418-656-9254;ext=102" ],
        ["tel", { "type": ["work", "cell", "voice", "video", "text"] }, "uri", "tel:+1-418-262-6501" ],
        ["email", { "type": "work" }, "text", "bob.perreault@viagenie.ca" ],
        ["geo", { "type": "work" }, "uri", "geo:46.772673,-71.282945"],
        ["key", { "type": "work" }, "uri", "http://www.viagenie.ca/bob.perreault/bob.asc" ],
        ["tz", {}, "utc-offset", "-05:00"],
        ["url", { "type": "home" }, "uri", "http://nomis80.org"]
      ]]
    ]});
  },

  add: function(rawContact) {
    return this._contactRequest("readwrite", function(transaction) {
      return transaction.add(rawContact);
    });
  },

  update: function(contact) {
    return this._contactRequest("readwrite",function(transaction) {
      return transaction.put(contact.toJSON());
    });
  },

  /**
  * Return all contacts in db.
  * @returns {Promise} of an array of all contact objects in the db.
  **/
  getAll: function() {
    return this._contactRequest("readonly",
        function(transaction) {
          return transaction.getAll();
        })
    .then(function(rawContacts) {
      return rawContacts.map(function(rawContact) {
        return new Contact(rawContact);
      });
    });
  },

  /**
  * Return all contacts in db.
  * @returns {Promise} of an array of all contact objects in the db.
  **/
  getAllNameIdAndPhoto: function() {
    return this._contactRequest("readonly",
        function(transaction) {
          return transaction.getAll();
        })
    .then(function(rawContacts) {
      return rawContacts.map(function(rawContact) {
        return {name: rawContact.name, id: rawContact.uuid, photo: ContactParser.getPhotoURL(rawContact.photo)};
      });
    });
  },

  /**
  * Return a contact.
  * @param id - id of contact required.
  * @return {Promise} of a contact
  **/
  getById: function(id) {
    return this._contactRequest("readonly",
        function(transaction) {
          return  transaction.get(id);
        })
    .then(function(rawContact) {
      return new Contact(rawContact);
    });
  },
  /**
  * Delete contact by id
  * @param {Integer} id - id of contact to be deleted.
  * @returns {Promise} to delete contact of input id
  **/
  deleteById: function(id) {
    return this._contactRequest("readwrite",function(transaction) { return  transaction.delete(id); } );
  },
  /**
  * Returns all names and IDs in the db.
  * @returns {Promise} - of all names and IDs in db.
  **/
  getNameAndId: function() {
    return this._contactNameCursor(function(cursor) {
      return { uuid: cursor.primaryKey, name: cursor.key };
    });
  },

  searchByName: function(name) {
    return this._contactNameCursor(function(cursor) {
      // all lower case so case does not matter in search
      if (cursor.key.toLowerCase().indexOf(name.toLowerCase()) !== -1) {
        return { uuid: cursor.primaryKey, name: cursor.key };
      }
    });
  },

  /**
   * A general cursor function that takes a filter function with a single argument of the current
   * cursor element. Any value returned from the filter function is returned in the promise.
   *
   * This is a cursor over the 'name' index
   *
   * @param {function} a filter function to choose which items are returned
   * @returns {Promise} with the value of an array filled with the filtered values
  **/
  _contactNameCursor: function(filter) {
    let db = this._db;

    return new Promise(function(resolve, reject) {

      // check to see if db exists
      if (db === undefined) {
        reject(DB_ERR_NOT_CONN);
        return;
      }

      // setup transaction
      var transaction = db.transaction([CONTACTS_STORE_NAME], "readonly")
        .objectStore(CONTACTS_STORE_NAME)
        .index("name");

      // create request
      var request = transaction.openKeyCursor();

      // initalise results
      var results = [];

      // setup response functions
      request.onsuccess = function(event) {
        var cursor = event.target.result;

        if (cursor) {
          var result = filter(cursor);
          if (result) {
            results.push(result);
          }
          cursor.continue();
        } else {
          resolve(results);
        }
      };

      request.onerror = function(event) {
        reject(event.target.error);
      };
    });
  },

  /**
  * @param {string} access -  level of access to db needed.
  * @param {function} requestFn - takes an IDB transaction
  * @returns {Promise} - the result of the request function
  **/
  _contactRequest: function(access, requestFn) {

    let db = this._db;

    return new Promise(function(resolve, reject) {

      // check to see if db exists
      if (db === undefined) {
        reject(DB_ERR_NOT_CONN);
        return;
      }

      // setup transaction
      var transaction = db.transaction([CONTACTS_STORE_NAME], access)
        .objectStore(CONTACTS_STORE_NAME);

      // create request
      var request = requestFn(transaction);

      // setup response functions
      request.onerror = function(event) {
        reject(event.target.error);
      };
      request.onsuccess = function(event) {
        resolve(event.target.result);
      };
    });
  }
};

/**
 * @constructor
 */
function Contact(rawContact) {
  this.uuid = rawContact.uuid;
  this.name = rawContact.name;
  this.photo = rawContact.photo;
  this.jcards = this._convertFromRawJCard(rawContact.jcards);
};

Contact.prototype = {

  _convertFromRawJCard: function(jcards) {
    return jcards.map(function(jcard) {
      return new ICAL.Component(jcard);
    });
  },

  _convertToRawJCard: function() {

    return this.jcards.map(function(jcard) {
      return jcard.toJSON();
    });
  },

  toJSON: function() {
    return {
      uuid : this.uuid,
      name : this.name,
      photo: this.photo,
      jcards: this._convertToRawJCard()
    };
  }
};

// vim: set sw=2 ts=2 expandtab ft=javascript:
<|endoftext|>


<commit_before>/**
 * Wrapper to manage services via PM2
 */
var _ = require('lodash');
var path = require('path');
var pm2 = require('pm2');
require('colors');

function Runner() {

}

Runner.prototype.init = function(bosco, next) {
	this.bosco = bosco;
	pm2.connect(next);
}

Runner.prototype.disconnect = function(next) {
    pm2.disconnect(next);
}

/**
 * List running services
 */
Runner.prototype.listRunning = function(detailed, next) {
	pm2.list(function(err, list) {
		var filteredList = _.filter(list, function(pm2Process){ return pm2Process.pm2_env.status === 'online' })

		if(!detailed) return next(err, _.pluck(filteredList,'name'));
		next(err, filteredList);
	});
}

/**
 * List services that have been created but are not running
 */
Runner.prototype.listNotRunning = function(detailed, next) {
	pm2.list(function(err, list) {
		var filteredList = _.filter(list, function(pm2Process){ return pm2Process.pm2_env.status !== 'online' })

		if(!detailed) return next(err, _.pluck(filteredList,'name'));
		next(err, filteredList);
	});
}

/**
 * Start a specific service
 * options = {cmd, cwd, name}
 */
Runner.prototype.start = function(options, next) {

	var self = this;
	// Remove node from the start script as not req'd for PM2
	var startCmd = options.service.start, startArr, start, ext;

	if(startCmd.split(' ')[0] == 'node') {
		startArr = startCmd.split(' ');
		startArr.shift();
		start = startArr.join(' ');

		ext = path.extname(startCmd);

		if(!path.extname(start)) {
			ext = '.js';
			start = start + '.js';
		}
	} else {
		start = startCmd;
	}

	var executeCommand = false;

	if (ext != '.js') {
		executeCommand = true;
	}

	// Node 0.10.x has a problem with cluster mode
	if (process.version.match(/0.10/)) {
		executeCommand = true;
	}

	if(!self.bosco.exists(options.cwd + '/' + start)) {
		self.bosco.warn('Can\'t start ' + options.name.red + ', as I can\'t find script: ' + start.red);
		return next();
	}
    self.bosco.log('Starting ' + options.name + ' ...');
	pm2.start(start, { name: options.name, cwd: options.cwd, watch: options.watch, executeCommand: executeCommand }, next);
}

/**
 * List running services
 */
Runner.prototype.stop = function(options, next) {
    var self = this;
	self.bosco.log('Stopping ' + options.name);
	pm2.stop(options.name, function(err) {
        if(err) return next(err);
 		pm2.delete(options.name, function(err) {
		  next(err);
		});
	});
}

module.exports = new Runner();
<commit_msg>Add force to node run<commit_after>/**
 * Wrapper to manage services via PM2
 */
var _ = require('lodash');
var path = require('path');
var pm2 = require('pm2');
require('colors');

function Runner() {

}

Runner.prototype.init = function(bosco, next) {
	this.bosco = bosco;
	pm2.connect(next);
}

Runner.prototype.disconnect = function(next) {
    pm2.disconnect(next);
}

/**
 * List running services
 */
Runner.prototype.listRunning = function(detailed, next) {
	pm2.list(function(err, list) {
		var filteredList = _.filter(list, function(pm2Process){ return pm2Process.pm2_env.status === 'online' })

		if(!detailed) return next(err, _.pluck(filteredList,'name'));
		next(err, filteredList);
	});
}

/**
 * List services that have been created but are not running
 */
Runner.prototype.listNotRunning = function(detailed, next) {
	pm2.list(function(err, list) {
		var filteredList = _.filter(list, function(pm2Process){ return pm2Process.pm2_env.status !== 'online' })

		if(!detailed) return next(err, _.pluck(filteredList,'name'));
		next(err, filteredList);
	});
}

/**
 * Start a specific service
 * options = {cmd, cwd, name}
 */
Runner.prototype.start = function(options, next) {

	var self = this;
	// Remove node from the start script as not req'd for PM2
	var startCmd = options.service.start, startArr, start, ext;

	if(startCmd.split(' ')[0] == 'node') {
		startArr = startCmd.split(' ');
		startArr.shift();
		start = startArr.join(' ');

		ext = path.extname(startCmd);

		if(!path.extname(start)) {
			ext = '.js';
			start = start + '.js';
		}
	} else {
		start = startCmd;
	}

	var executeCommand = false;

	if (ext != '.js') {
		executeCommand = true;
	}

	// Node 0.10.x has a problem with cluster mode
	if (process.version.match(/0.10/)) {
		executeCommand = true;
	}

	if(!self.bosco.exists(options.cwd + '/' + start)) {
		self.bosco.warn('Can\'t start ' + options.name.red + ', as I can\'t find script: ' + start.red);
		return next();
	}
    self.bosco.log('Starting ' + options.name + ' ...');
	pm2.start(start, { name: options.name, cwd: options.cwd, watch: options.watch, executeCommand: executeCommand, force: true }, next);
}

/**
 * List running services
 */
Runner.prototype.stop = function(options, next) {
    var self = this;
	self.bosco.log('Stopping ' + options.name);
	pm2.stop(options.name, function(err) {
        if(err) return next(err);
 		pm2.delete(options.name, function(err) {
		  next(err);
		});
	});
}

module.exports = new Runner();
<|endoftext|>


<commit_before>/**
 * CurrentSurvey component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * External dependencies
 */
import { useMount } from 'react-use';
import { Slide } from '@material-ui/core';

/**
 * WordPress dependencies
 */
import { useCallback, useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { CORE_FORMS } from '../../googlesitekit/datastore/forms/constants';
import { CORE_USER } from '../../googlesitekit/datastore/user/constants';
import SurveyCompletion from './SurveyCompletion';
import SurveyQuestionRating from './SurveyQuestionRating';
import SurveyQuestionOpenText from './SurveyQuestionOpenText';
import SurveyQuestionMultiSelect from './SurveyQuestionMultiSelect';
import SurveyQuestionSingleSelect from './SurveyQuestionSingleSelect';
import SurveyTerms from './SurveyTerms';
const { useDispatch, useSelect } = Data;

const ComponentMap = {
	rating: SurveyQuestionRating,
	open_text: SurveyQuestionOpenText,
	multi_select: SurveyQuestionMultiSelect,
	single_select: SurveyQuestionSingleSelect,
};

const SURVEY_ANSWER_DELAY_MS = 300;

export default function CurrentSurvey() {
	const [ hasSentSurveyShownEvent, setHasSentSurveyShownEvent ] = useState(
		false
	);
	const [ hasSentCompletionEvent, setHasSentCompletionEvent ] = useState(
		false
	);
	const [ animateSurvey, setAnimateSurvey ] = useState( false );
	const [ hasAnsweredQuestion, setHasAnsweredQuestion ] = useState( false );

	const completions = useSelect( ( select ) =>
		select( CORE_USER ).getCurrentSurveyCompletions()
	);
	const questions = useSelect( ( select ) =>
		select( CORE_USER ).getCurrentSurveyQuestions()
	);
	const surveySession = useSelect( ( select ) =>
		select( CORE_USER ).getCurrentSurveySession()
	);
	const isTrackingEnabled = useSelect( ( select ) =>
		select( CORE_USER ).isTrackingEnabled()
	);

	const formName = surveySession
		? `survey-${ surveySession.session_id }`
		: null;
	const shouldHide = useSelect( ( select ) =>
		select( CORE_FORMS ).getValue( formName, 'hideSurvey' )
	);
	const answers = useSelect( ( select ) =>
		select( CORE_FORMS ).getValue( formName, 'answers' )
	);

	const { setValues } = useDispatch( CORE_FORMS );
	const { sendSurveyEvent } = useDispatch( CORE_USER );

	useEffect( () => {
		if ( questions?.length && ! hasSentSurveyShownEvent ) {
			setHasSentSurveyShownEvent( true );
			sendSurveyEvent( 'survey_shown' );
		}
	}, [ questions, hasSentSurveyShownEvent, sendSurveyEvent ] );

	const currentQuestionOrdinal =
		Math.max( 0, ...( answers || [] ).map( ( a ) => a.question_ordinal ) ) +
		1;
	const currentQuestion = questions?.find(
		( { question_ordinal: questionOrdinal } ) =>
			questionOrdinal === currentQuestionOrdinal
	);

	const answerQuestion = useCallback(
		( answer ) => {
			if ( hasAnsweredQuestion ) {
				return;
			}
			setHasAnsweredQuestion( true );

			sendSurveyEvent( 'question_answered', {
				// eslint-disable-next-line camelcase
				question_ordinal: currentQuestion?.question_ordinal,
				answer,
			} );

			setTimeout( () => {
				setValues( formName, {
					answers: [
						...( answers || [] ),
						{
							// eslint-disable-next-line camelcase
							question_ordinal: currentQuestion?.question_ordinal,
							answer,
						},
					],
				} );

				setHasAnsweredQuestion( false );
			}, SURVEY_ANSWER_DELAY_MS );
		},
		[
			answers,
			currentQuestion,
			formName,
			sendSurveyEvent,
			setValues,
			hasAnsweredQuestion,
		]
	);

	// Check to see if a completion trigger has been met.
	let triggeredCompletion;
	// We only have trigger conditions for questions that are answered with
	// ordinal values right now.
	const ordinalAnswerMap = answers?.length
		? answers.reduce( ( acc, answer ) => {
				return {
					...acc,
					[ answer.question_ordinal ]:
						answer.answer.answer.answer_ordinal,
				};
		  }, {} )
		: [];

	if ( questions?.length && currentQuestionOrdinal > questions?.length ) {
		// Use Array.some to avoid looping through all completions; once the first
		// matching completion has been found, treat the survey as complete.
		completions?.some( ( completion ) => {
			completion.trigger_condition.some( ( condition ) => {
				// If a question was answered with the appropriate value, a completion
				// trigger has been fulfilled and we should treat this survey as
				// complete.
				if (
					condition.answer_ordinal.includes(
						ordinalAnswerMap[ condition.question_ordinal ]
					)
				) {
					// eslint-disable-line camelcase
					triggeredCompletion = completion;
					return true;
				}

				return false;
			} );

			if ( triggeredCompletion ) {
				return true;
			}

			return false;
		} );

		// If no specific trigger has been found but all questions are answered, use
		// the first available trigger.
		if ( ! triggeredCompletion ) {
			triggeredCompletion = completions[ 0 ];
		}
	}

	const ctaOnClick = useCallback( () => {
		sendSurveyEvent( 'follow_up_link_clicked', {
			// eslint-disable-next-line camelcase
			completion_ordinal: triggeredCompletion?.completion_ordinal,
		} );
		sendSurveyEvent( 'survey_closed' );

		setValues( formName, { hideSurvey: true } );
	}, [ formName, sendSurveyEvent, setValues, triggeredCompletion ] );

	const dismissSurvey = useCallback( () => {
		sendSurveyEvent( 'survey_closed' );

		setAnimateSurvey( false );
	}, [ sendSurveyEvent ] );

	const handleAnimationOnExited = useCallback( () => {
		setValues( formName, { hideSurvey: true } );
	}, [ formName, setValues ] );

	useEffect( () => {
		if ( triggeredCompletion && ! hasSentCompletionEvent ) {
			setHasSentCompletionEvent( true );
			sendSurveyEvent( 'completion_shown', {
				// eslint-disable-next-line camelcase
				completion_ordinal: triggeredCompletion?.completion_ordinal,
			} );
		}
	}, [ hasSentCompletionEvent, sendSurveyEvent, triggeredCompletion ] );

	useMount( () => {
		setAnimateSurvey( true );
	} );

	if (
		shouldHide ||
		! questions ||
		! completions ||
		isTrackingEnabled === undefined
	) {
		return null;
	}

	if ( triggeredCompletion ) {
		return (
			<Slide
				direction="up"
				in={ animateSurvey }
				onExited={ handleAnimationOnExited }
			>

					<SurveyCompletion
						dismissSurvey={ dismissSurvey }
						ctaOnClick={ ctaOnClick }
						ctaText={ triggeredCompletion.follow_up_text }
						ctaURL={ triggeredCompletion.follow_up_url }
						title={ triggeredCompletion.completion_title }
					>
						{ triggeredCompletion.completion_text }
					</SurveyCompletion>

			</Slide>
		);
	}

	const SurveyQuestionComponent =
		// eslint-disable-next-line camelcase
		ComponentMap[ currentQuestion?.question_type ];
	if ( ! SurveyQuestionComponent ) {
		return null;
	}

	return (
		<Slide
			direction="up"
			in={ animateSurvey }
			onExited={ handleAnimationOnExited }
		>

				<SurveyQuestionComponent
					key={ currentQuestion.question_text }
					answerQuestion={ answerQuestion }
					choices={ currentQuestion.question?.answer_choice } // eslint-disable-line camelcase
					dismissSurvey={ dismissSurvey }
					question={ currentQuestion.question_text }
					subtitle={ currentQuestion.subtitle }
					placeholder={ currentQuestion.placeholder }
					minChoices={ currentQuestion.min_choices }
					maxChoices={ currentQuestion.max_choices }
					submitButtonText={
						questions?.length === currentQuestionOrdinal
							? __( 'Submit', 'google-site-kit' )
							: __( 'Next', 'google-site-kit' )
					}
				/>

				{ isTrackingEnabled === false &&
					currentQuestion?.question_ordinal === 1 && ( // eslint-disable-line camelcase

							<SurveyTerms />

					) }

		</Slide>
	);
}
<commit_msg>Use switch statement.<commit_after>/**
 * CurrentSurvey component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * External dependencies
 */
import { useMount } from 'react-use';
import { Slide } from '@material-ui/core';

/**
 * WordPress dependencies
 */
import { useCallback, useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { CORE_FORMS } from '../../googlesitekit/datastore/forms/constants';
import { CORE_USER } from '../../googlesitekit/datastore/user/constants';
import SurveyCompletion from './SurveyCompletion';
import SurveyQuestionRating from './SurveyQuestionRating';
import SurveyQuestionOpenText from './SurveyQuestionOpenText';
import SurveyQuestionMultiSelect from './SurveyQuestionMultiSelect';
import SurveyQuestionSingleSelect from './SurveyQuestionSingleSelect';
import SurveyTerms from './SurveyTerms';
const { useDispatch, useSelect } = Data;

const SURVEY_ANSWER_DELAY_MS = 300;

export default function CurrentSurvey() {
	const [ hasSentSurveyShownEvent, setHasSentSurveyShownEvent ] = useState(
		false
	);
	const [ hasSentCompletionEvent, setHasSentCompletionEvent ] = useState(
		false
	);
	const [ animateSurvey, setAnimateSurvey ] = useState( false );
	const [ hasAnsweredQuestion, setHasAnsweredQuestion ] = useState( false );

	const completions = useSelect( ( select ) =>
		select( CORE_USER ).getCurrentSurveyCompletions()
	);
	const questions = useSelect( ( select ) =>
		select( CORE_USER ).getCurrentSurveyQuestions()
	);
	const surveySession = useSelect( ( select ) =>
		select( CORE_USER ).getCurrentSurveySession()
	);
	const isTrackingEnabled = useSelect( ( select ) =>
		select( CORE_USER ).isTrackingEnabled()
	);

	const formName = surveySession
		? `survey-${ surveySession.session_id }`
		: null;
	const shouldHide = useSelect( ( select ) =>
		select( CORE_FORMS ).getValue( formName, 'hideSurvey' )
	);
	const answers = useSelect( ( select ) =>
		select( CORE_FORMS ).getValue( formName, 'answers' )
	);

	const { setValues } = useDispatch( CORE_FORMS );
	const { sendSurveyEvent } = useDispatch( CORE_USER );

	useEffect( () => {
		if ( questions?.length && ! hasSentSurveyShownEvent ) {
			setHasSentSurveyShownEvent( true );
			sendSurveyEvent( 'survey_shown' );
		}
	}, [ questions, hasSentSurveyShownEvent, sendSurveyEvent ] );

	const currentQuestionOrdinal =
		Math.max( 0, ...( answers || [] ).map( ( a ) => a.question_ordinal ) ) +
		1;
	const currentQuestion = questions?.find(
		( { question_ordinal: questionOrdinal } ) =>
			questionOrdinal === currentQuestionOrdinal
	);

	const answerQuestion = useCallback(
		( answer ) => {
			if ( hasAnsweredQuestion ) {
				return;
			}
			setHasAnsweredQuestion( true );

			sendSurveyEvent( 'question_answered', {
				// eslint-disable-next-line camelcase
				question_ordinal: currentQuestion?.question_ordinal,
				answer,
			} );

			setTimeout( () => {
				setValues( formName, {
					answers: [
						...( answers || [] ),
						{
							// eslint-disable-next-line camelcase
							question_ordinal: currentQuestion?.question_ordinal,
							answer,
						},
					],
				} );

				setHasAnsweredQuestion( false );
			}, SURVEY_ANSWER_DELAY_MS );
		},
		[
			answers,
			currentQuestion,
			formName,
			sendSurveyEvent,
			setValues,
			hasAnsweredQuestion,
		]
	);

	// Check to see if a completion trigger has been met.
	let triggeredCompletion;
	// We only have trigger conditions for questions that are answered with
	// ordinal values right now.
	const ordinalAnswerMap = answers?.length
		? answers.reduce( ( acc, answer ) => {
				return {
					...acc,
					[ answer.question_ordinal ]:
						answer.answer.answer.answer_ordinal,
				};
		  }, {} )
		: [];

	if ( questions?.length && currentQuestionOrdinal > questions?.length ) {
		// Use Array.some to avoid looping through all completions; once the first
		// matching completion has been found, treat the survey as complete.
		completions?.some( ( completion ) => {
			completion.trigger_condition.some( ( condition ) => {
				// If a question was answered with the appropriate value, a completion
				// trigger has been fulfilled and we should treat this survey as
				// complete.
				if (
					condition.answer_ordinal.includes(
						ordinalAnswerMap[ condition.question_ordinal ]
					)
				) {
					// eslint-disable-line camelcase
					triggeredCompletion = completion;
					return true;
				}

				return false;
			} );

			if ( triggeredCompletion ) {
				return true;
			}

			return false;
		} );

		// If no specific trigger has been found but all questions are answered, use
		// the first available trigger.
		if ( ! triggeredCompletion ) {
			triggeredCompletion = completions[ 0 ];
		}
	}

	const ctaOnClick = useCallback( () => {
		sendSurveyEvent( 'follow_up_link_clicked', {
			// eslint-disable-next-line camelcase
			completion_ordinal: triggeredCompletion?.completion_ordinal,
		} );
		sendSurveyEvent( 'survey_closed' );

		setValues( formName, { hideSurvey: true } );
	}, [ formName, sendSurveyEvent, setValues, triggeredCompletion ] );

	const dismissSurvey = useCallback( () => {
		sendSurveyEvent( 'survey_closed' );

		setAnimateSurvey( false );
	}, [ sendSurveyEvent ] );

	const handleAnimationOnExited = useCallback( () => {
		setValues( formName, { hideSurvey: true } );
	}, [ formName, setValues ] );

	useEffect( () => {
		if ( triggeredCompletion && ! hasSentCompletionEvent ) {
			setHasSentCompletionEvent( true );
			sendSurveyEvent( 'completion_shown', {
				// eslint-disable-next-line camelcase
				completion_ordinal: triggeredCompletion?.completion_ordinal,
			} );
		}
	}, [ hasSentCompletionEvent, sendSurveyEvent, triggeredCompletion ] );

	useMount( () => {
		setAnimateSurvey( true );
	} );

	if (
		shouldHide ||
		! questions ||
		! completions ||
		isTrackingEnabled === undefined
	) {
		return null;
	}

	if ( triggeredCompletion ) {
		return (
			<Slide
				direction="up"
				in={ animateSurvey }
				onExited={ handleAnimationOnExited }
			>

					<SurveyCompletion
						dismissSurvey={ dismissSurvey }
						ctaOnClick={ ctaOnClick }
						ctaText={ triggeredCompletion.follow_up_text }
						ctaURL={ triggeredCompletion.follow_up_url }
						title={ triggeredCompletion.completion_title }
					>
						{ triggeredCompletion.completion_text }
					</SurveyCompletion>

			</Slide>
		);
	}

	let surveyQuestionComponent;

	switch (
		currentQuestion?.question_type // eslint-disable-line camelcase
	) {
		case 'rating':
			surveyQuestionComponent = (
				<SurveyQuestionRating
					key={ currentQuestion.question_text }
					answerQuestion={ answerQuestion }
					choices={ currentQuestion.question?.answer_choice } // eslint-disable-line camelcase
					dismissSurvey={ dismissSurvey }
					question={ currentQuestion.question_text }
					subtitle={ currentQuestion.subtitle }
					placeholder={ currentQuestion.placeholder }
					minChoices={ currentQuestion.min_choices }
					maxChoices={ currentQuestion.max_choices }
					submitButtonText={
						questions?.length === currentQuestionOrdinal
							? __( 'Submit', 'google-site-kit' )
							: __( 'Next', 'google-site-kit' )
					}
				/>
			);
			break;
		case 'open_text':
			surveyQuestionComponent = (
				<SurveyQuestionOpenText
					key={ currentQuestion.question_text }
					answerQuestion={ answerQuestion }
					choices={ currentQuestion.question?.answer_choice } // eslint-disable-line camelcase
					dismissSurvey={ dismissSurvey }
					question={ currentQuestion.question_text }
					subtitle={ currentQuestion.subtitle }
					placeholder={ currentQuestion.placeholder }
					minChoices={ currentQuestion.min_choices }
					maxChoices={ currentQuestion.max_choices }
					submitButtonText={
						questions?.length === currentQuestionOrdinal
							? __( 'Submit', 'google-site-kit' )
							: __( 'Next', 'google-site-kit' )
					}
				/>
			);
			break;
		case 'multi_select':
			surveyQuestionComponent = (
				<SurveyQuestionMultiSelect
					key={ currentQuestion.question_text }
					answerQuestion={ answerQuestion }
					choices={ currentQuestion.question?.answer_choice } // eslint-disable-line camelcase
					dismissSurvey={ dismissSurvey }
					question={ currentQuestion.question_text }
					subtitle={ currentQuestion.subtitle }
					placeholder={ currentQuestion.placeholder }
					minChoices={ currentQuestion.min_choices }
					maxChoices={ currentQuestion.max_choices }
					submitButtonText={
						questions?.length === currentQuestionOrdinal
							? __( 'Submit', 'google-site-kit' )
							: __( 'Next', 'google-site-kit' )
					}
				/>
			);
			break;
		case 'single_select':
			surveyQuestionComponent = (
				<SurveyQuestionSingleSelect
					key={ currentQuestion.question_text }
					answerQuestion={ answerQuestion }
					choices={ currentQuestion.question?.answer_choice } // eslint-disable-line camelcase
					dismissSurvey={ dismissSurvey }
					question={ currentQuestion.question_text }
					subtitle={ currentQuestion.subtitle }
					placeholder={ currentQuestion.placeholder }
					minChoices={ currentQuestion.min_choices }
					maxChoices={ currentQuestion.max_choices }
					submitButtonText={
						questions?.length === currentQuestionOrdinal
							? __( 'Submit', 'google-site-kit' )
							: __( 'Next', 'google-site-kit' )
					}
				/>
			);
			break;

		default:
			break;
	}

	if ( ! surveyQuestionComponent ) {
		return null;
	}

	return (
		<Slide
			direction="up"
			in={ animateSurvey }
			onExited={ handleAnimationOnExited }
		>

				{ surveyQuestionComponent }

				{ isTrackingEnabled === false &&
					currentQuestion?.question_ordinal === 1 && ( // eslint-disable-line camelcase

							<SurveyTerms />

					) }

		</Slide>
	);
}
<|endoftext|>


<commit_before>(function () {
    var empty = { branches: [] };

    function findPullRequestLinks() {
	var links = document.getElementsByTagName("a");
	return links;
    }
    function handleBranches(items){
	console.log(items.branches);
    }

    function init(){
	console.log("Content Script");
	chrome.storage.sync.get(empty, handleBranches);
    }

    init();
}());
<commit_msg>Rewrite branch URLs that match patterns set from options<commit_after>/*global jQuery */
(function ($) {
    var empty = { branches: [] };

    function rewriteLink(element, branches){
	var $element = $(element),
	    href = $element.attr('href').split("/"),
	    targetBranchPosition = (href.length - 1),
	    targetVal = href[targetBranchPosition],
	    currentBranch,
	    regex,
	    i;

	for(i=0; i< branches.length; i++){
	    currentBranch = branches[i];
	    regex = RegExp(currentBranch.pattern);
	    if (regex.test(targetVal)) {
		href[targetBranchPosition] = currentBranch.target + "..." + targetVal;
	    }
	}

	//Write back
	$element.attr('href', href.join("/"));
    }

    function handleBranches(items){

	function handleLink(index, element){
	    return rewriteLink(element, items.branches);
	}
	$(".recently-pushed-branch-actions a").each(handleLink);
    }

    function init(){
	//This call is async, items is only populated in the callback
	chrome.storage.sync.get(empty, handleBranches);
    }

    init();
}(jQuery));
<|endoftext|>


<commit_before>var fs = require('fs');
var http = require('http');
var url = require('url');
var Promise = require('promise');
var gpio = require('rpi-gpio');
var async = require('async');
var signalR = require('signalr-client');

var config = JSON.parse(fs.readFileSync('config.json'));

async.parallel([
    function(c) {
        gpio.setup(config.redPin, gpio.DIR_OUT, c);
    },
    function(c) {
        gpio.setup(config.greenPin, gpio.DIR_OUT, c);
    }
], function (e, results) {
    console.log('gpio setup');
    console.log(e);
    console.log(results);
    setPins(false, false);
    updateIn(1);
});

function getStatus() {
    return new Promise(function(resolve, reject) {
        var options = url.parse(config.apiServer + "/room/" + config.room + "/status");
        options.method = "GET";
        return http.request(options, function(e) {
            data = "";
            e.on('data', function(c) { data += String(c); });
            e.on('end', function() { resolve(data); });
        }).end();
    })
}

var updateTimeout = null;
function updateIn(delay) {
    if(null != updateTimeout){
        clearTimeout(updateTimeout);
    }
    updateTimeout = setTimeout(function() {
        getStatus().then(function(data) {
            var status = JSON.parse(data).Status;
            switch(status) {
                case 0:
                    green();
                    break;
                case 1:
                    red();
                    break;
                case 2:
                    red();
                    break;
                default:
                    console.log('invalid status: ' + status);
                    break;
            }
        });
    }, delay);
}

function red() {
    console.log('red');
    setPins(true, false);
}
function green() {
    console.log('green');
    setPins(false, true);
}
function setPins(red, green) {
    async.parallel([
        function(c) {
            gpio.write(config.redPin, red, c);
        },
        function(c) {
            gpio.write(config.greenPin, green, c);
        }
    ], function (e, results) {
        console.log('set pins');
        console.log(e);
        console.log(results);
    });
}

setInterval(function() {
    updateIn(5000);
}, 5 * 60 * 1000);

var client  = new signalR.client(
    config.signalRServer,
    ['UpdateHub']
);
client.on('UpdateHub', 'Update', function(room) {
    console.log('got notification of change to ' + room);
    if(config.room == room) {
        updateIn(1);
    }
});

// wait
<commit_msg>Use NextChangeSeconds<commit_after>var fs = require('fs');
var http = require('http');
var url = require('url');
var Promise = require('promise');
var gpio = require('rpi-gpio');
var async = require('async');
var signalR = require('signalr-client');

var config = JSON.parse(fs.readFileSync('config.json'));

async.parallel([
    function(c) {
        gpio.setup(config.redPin, gpio.DIR_OUT, c);
    },
    function(c) {
        gpio.setup(config.greenPin, gpio.DIR_OUT, c);
    }
], function (e, results) {
    console.log('gpio setup');
    console.log(e);
    console.log(results);
    setPins(false, false);
    updateIn(1);
});

function getStatus() {
    return new Promise(function(resolve, reject) {
        var options = url.parse(config.apiServer + "/room/" + config.room + "/status");
        options.method = "GET";
        return http.request(options, function(e) {
            data = "";
            e.on('data', function(c) { data += String(c); });
            e.on('end', function() { resolve(data); });
        }).end();
    })
}

var updateTimeout = null;
function updateIn(delay) {
    if(null != updateTimeout){
        clearTimeout(updateTimeout);
    }
    updateTimeout = setTimeout(function() {
        getStatus().then(function(data) {
            var obj = JSON.parse(data);
            var status = obj.Status;
            switch(status) {
                case 0:
                    green();
                    break;
                case 1:
                    red();
                    break;
                case 2:
                    red();
                    break;
                default:
                    console.log('invalid status: ' + status);
                    break;
            }
            if(obj.NextChangeSeconds) {
                updateIn((obj.NextChangeSeconds + 1) * 1000); // server advises us to check back at this time
            }
        });
    }, delay);
}

function red() {
    console.log('red');
    setPins(true, false);
}
function green() {
    console.log('green');
    setPins(false, true);
}
function setPins(red, green) {
    async.parallel([
        function(c) {
            gpio.write(config.redPin, red, c);
        },
        function(c) {
            gpio.write(config.greenPin, green, c);
        }
    ], function (e, results) {
        console.log('set pins');
        console.log(e);
        console.log(results);
    });
}

setInterval(function() {
    updateIn(5000);
}, 5 * 60 * 1000);

var client  = new signalR.client(
    config.signalRServer,
    ['UpdateHub']
);
client.on('UpdateHub', 'Update', function(room) {
    console.log('got notification of change to ' + room);
    if(config.room == room) {
        updateIn(1);
    }
});

// wait
<|endoftext|>


<commit_before>var deviceScripts = {
	manualRouter: {
		onPacketReceived: function(device, packet) {
			for (var i = 0; i < device.rules.length; i++) {
				if (device.rules[i].dstip == packet.network.dstip) {
					sendPacket(device.id, device.rules[i].portNum, packet);
				}
			}
		}
	},

	hub: {
		onPacketReceived: function (device, packet) {
			//.
		}
	},

    ping: {
		onPacketReceived: function(device, packet) {
                    if(packet.hasOwnProperty("transport") && packet["transport"].hasOwnProperty("proto")){
                        if(packet.transport.proto == "ICMP"){
                            var new_packet = {
                                network: {
                                    srcip: packet.network.dstip,
                                    dstip: packet.network.srcip
                                },
                                transport: {
                                    proto: "ICMP"
                                }
                            };
                            sendPacket(device.id, 0, new_packet);
                        }
                    }
		}
    },
    modem: {
        onPacketReceived: function(device, packet) {
            if(packet.network.dstip == device.id){//look up ip in NAT table
                var new_packet = {};
                for (var i = 0; i < packetFields.length; i++) {
                    if(packet.hasOwnProperty(packetFields[i].layer)){
                        new_packet[packetFields[i].layer] = {};
                        for (var j = 0; j < packetFields[i].fields.length; j++) {
                            if(packet[packetFields[i].layer].hasOwnProperty(packetFields[i].fields[j])){
                                new_packet[packetFields[i].layer][ packetFields[i].fields[j] ] = packet[packetFields[i].layer][ packetFields[i].fields[j] ];
                            }
                        }
                    }
                }
                new_packet.network.dstip = getPortRecipient(device.id, 0);
                sendPacket(device.id, 0, new_packet);
            } else { //replace src ip with device IP and save in NAT table
                var new_packet = {};
                for (var i = 0; i < packetFields.length; i++) {
                    if(packet.hasOwnProperty(packetFields[i].layer)){
                        new_packet[packetFields[i].layer] = {};
                        for (var j = 0; j < packetFields[i].fields.length; j++) {
                            if(packet[packetFields[i].layer].hasOwnProperty(packetFields[i].fields[j])){
                                new_packet[packetFields[i].layer][ packetFields[i].fields[j] ] = packet[packetFields[i].layer][ packetFields[i].fields[j] ];
                            }
                        }
                    }
                }
                new_packet.network.srcip = device.id;
                sendPacket(device.id, 1, new_packet);
            }

        }
    },
    switch: {
        onPacketReceived: function(device, packet, portNum) {
            var found = false;
            for (var i = 0; (i < device.rules.length) && !found; i++) {
                if (device.rules[i].dstip == packet.network.dstip) {
                    sendPacket(device.id, device.rules[i].portNum, packet);
                    found = true;
                }
            }
            if(!found){
                //broadcast packet to all ports except where it was received
                for(var i=0; i<device.ports.length; i++){
                    sendPacket(device.id, i, packet);
                }
            }
            //update rules with info from this packet
            var found = false;
            for (var i = 0; (i < device.rules.length) && !found; i++) {
                if (device.rules[i].dstip == packet.network.srcip) {
                    device.rules[i].portNum = portNum;
                    found = true;
                }
            }
            if(!found){
                device.rules[device.rules.length] = {
                    dstip: packet.network.srcip,
                    portNum: portNum
                }
            }
        }
    }

}

<commit_msg>debugging switch script<commit_after>var deviceScripts = {
	manualRouter: {
		onPacketReceived: function(device, packet) {
			for (var i = 0; i < device.rules.length; i++) {
				if (device.rules[i].dstip == packet.network.dstip) {
					sendPacket(device.id, device.rules[i].portNum, packet);
				}
			}
		}
	},

	hub: {
		onPacketReceived: function (device, packet) {
			//.
		}
	},

    ping: {
		onPacketReceived: function(device, packet) {
                    if(packet.hasOwnProperty("transport") && packet["transport"].hasOwnProperty("proto")){
                        if(packet.transport.proto == "ICMP"){
                            var new_packet = {
                                network: {
                                    srcip: packet.network.dstip,
                                    dstip: packet.network.srcip
                                },
                                transport: {
                                    proto: "ICMP"
                                }
                            };
                            sendPacket(device.id, 0, new_packet);
                        }
                    }
		}
    },
    modem: {
        onPacketReceived: function(device, packet) {
            if(packet.network.dstip == device.id){//look up ip in NAT table
                var new_packet = {};
                for (var i = 0; i < packetFields.length; i++) {
                    if(packet.hasOwnProperty(packetFields[i].layer)){
                        new_packet[packetFields[i].layer] = {};
                        for (var j = 0; j < packetFields[i].fields.length; j++) {
                            if(packet[packetFields[i].layer].hasOwnProperty(packetFields[i].fields[j])){
                                new_packet[packetFields[i].layer][ packetFields[i].fields[j] ] = packet[packetFields[i].layer][ packetFields[i].fields[j] ];
                            }
                        }
                    }
                }
                new_packet.network.dstip = getPortRecipient(device.id, 0);
                sendPacket(device.id, 0, new_packet);
            } else { //replace src ip with device IP and save in NAT table
                var new_packet = {};
                for (var i = 0; i < packetFields.length; i++) {
                    if(packet.hasOwnProperty(packetFields[i].layer)){
                        new_packet[packetFields[i].layer] = {};
                        for (var j = 0; j < packetFields[i].fields.length; j++) {
                            if(packet[packetFields[i].layer].hasOwnProperty(packetFields[i].fields[j])){
                                new_packet[packetFields[i].layer][ packetFields[i].fields[j] ] = packet[packetFields[i].layer][ packetFields[i].fields[j] ];
                            }
                        }
                    }
                }
                new_packet.network.srcip = device.id;
                sendPacket(device.id, 1, new_packet);
            }

        }
    },
    switch: {
        onPacketReceived: function(device, packet, portNum) {
            var found = false;
            for (var i = 0; (i < device.rules.length) && !found; i++) {
                if (device.rules[i].dstip == packet.network.dstip) {
                    sendPacket(device.id, device.rules[i].portNum, packet);
                    found = true;
                }
            }
            if(!found){
                //broadcast packet to all ports except where it was received
                for(var i=0; i<device.ports.length; i++){
                    if(i != portNum){
                        sendPacket(device.id, i, packet);
                    }
                }
            }
            //update rules with info from this packet
            var found = false;
            for (var i = 0; (i < device.rules.length) && !found; i++) {
                if (device.rules[i].dstip == packet.network.srcip) {
                    device.rules[i].portNum = portNum;
                    found = true;
                }
            }
            if(!found){
                device.rules[device.rules.length] = {
                    dstip: packet.network.srcip,
                    portNum: portNum
                }
            }
        }
    }

}

<|endoftext|>


<commit_before>/**
 * Created by Awesome on 3/1/2016.
 */

// use strict
'use strict';

// bind methods
var controller = require(global.appRoot + '/bin/bundles/core/controller');

/**
 * build admin controller
 *
 * @mount /admin
 */
class adminController extends controller {
    /**
     * construct admin controller
     *
     * @param props
     */
    constructor(props) {
        // run super
        super(props);

        // bind methods
        this.indexAction = this.indexAction.bind(this);
    }

    /**
     * index action
     *
     * @param req
     * @param res
     *
     * @name     ADMIN_HOME
     * @route    {get} /
     * @menu     {MAIN}  Admin
     * @menu     {ADMIN} Admin Home
     * @acl      {test:['admin'],fail:{redirect:"/"}}
     * @priority 10
     */
    indexAction(req, res) {
        res.render('admin', {
            'layout' : 'admin.layout.hbs'
        });
    }
}

/**
 * export admin controller
 *
 * @type {adminController}
 */
module.exports = adminController;<commit_msg>move acl to mount in user controller<commit_after>/**
 * Created by Awesome on 3/1/2016.
 */

// use strict
'use strict';

// bind methods
var controller = require(global.appRoot + '/bin/bundles/core/controller');

/**
 * build admin controller
 *
 * @mount /admin
 * @acl   {test:['admin'],fail:{redirect:"/"}}
 */
class adminController extends controller {
    /**
     * construct admin controller
     *
     * @param props
     */
    constructor(props) {
        // run super
        super(props);

        // bind methods
        this.indexAction = this.indexAction.bind(this);
    }

    /**
     * index action
     *
     * @param req
     * @param res
     *
     * @name     ADMIN_HOME
     * @route    {get} /
     * @menu     {MAIN}  Admin
     * @menu     {ADMIN} Admin Home
     * @priority 10
     */
    indexAction(req, res) {
        res.render('admin', {
            'layout' : 'admin.layout.hbs'
        });
    }
}

/**
 * export admin controller
 *
 * @type {adminController}
 */
module.exports = adminController;<|endoftext|>