<commit_before>import debounce                                                               from 'lodash.debounce';
import { action, observable, reaction, runInAction }                          from 'mobx';
import BinarySocket                                                           from '_common/base/socket_base';
import { localize }                                                           from '_common/localize';
import { cloneObject, isEmptyObject }                                         from '_common/utility';
import { getMinPayout, isCryptocurrency }                                     from '_common/base/currency_base';
import { WS }                                                                 from 'Services';
import GTM                                                                    from 'Utils/gtm';
import URLHelper                                                              from 'Utils/URL/url_helper';
import { processPurchase }                                                    from './Actions/purchase';
import * as Symbol                                                            from './Actions/symbol';
import { allowed_query_string_variables, non_proposal_query_string_variable } from './Constants/query_string';
import getValidationRules                                                     from './Constants/validation_rules';
import { setChartBarrier }                                                    from './Helpers/chart';
import ContractType                                                           from './Helpers/contract_type';
import { convertDurationLimit }                                               from './Helpers/duration';
import { processTradeParams }                                                 from './Helpers/process';
import { createProposalRequests, getProposalInfo, getProposalParametersName } from './Helpers/proposal';
import { pickDefaultSymbol }                                                  from './Helpers/symbol';
import BaseStore                                                              from '../../base_store';

export default class TradeStore extends BaseStore {
    // Control values
    @observable is_trade_component_mounted = false;
    @observable is_purchase_enabled        = false;
    @observable is_trade_enabled           = false;

    // Underlying
    @observable symbol;

    // Contract Type
    @observable contract_expiry_type = '';
    @observable contract_start_type  = '';
    @observable contract_type        = '';
    @observable contract_types_list  = {};
    @observable form_components      = [];
    @observable trade_types          = {};

    // Amount
    @observable amount          = 10;
    @observable basis           = '';
    @observable basis_list      = [];

    // Duration
    @observable duration            = 5;
    @observable duration_unit       = '';
    @observable duration_units_list = [];
    @observable duration_min_max    = {};
    @observable expiry_date         = '';
    @observable expiry_time         = '09:40';
    @observable expiry_type         = 'duration';

    // Barrier
    @observable barrier_1     = '';
    @observable barrier_2     = '';
    @observable barrier_count = 0;

    // Start Time
    @observable start_date       = Number(0); // Number(0) refers to 'now'
    @observable start_dates_list = [];
    @observable start_time       = '12:30';
    @observable sessions         = [];

    // Last Digit
    @observable last_digit = 5;

    // Purchase
    @observable proposal_info = {};
    @observable purchase_info = {};

    // Chart
    chart_id = 1;

    debouncedProposal = debounce(this.requestProposal, 500);
    @action.bound
    init = async () => {
        // To be sure that the website_status response has been received before processing trading page.
        await BinarySocket.wait('website_status');
    };

    proposal_requests = {};

    constructor({ root_store }) {
        URLHelper.pruneQueryString(allowed_query_string_variables);

        super({
            root_store,
            session_storage_properties: allowed_query_string_variables,
            validation_rules          : getValidationRules(),
        });

        Object.defineProperty(
            this,
            'is_query_string_applied',
            {
                enumerable: false,
                value     : false,
                writable  : true,
            },
        );
        // Adds intercept to change min_max value of duration validation
        reaction(
            () => [this.contract_expiry_type, this.duration_min_max, this.duration_unit, this.expiry_type],
            () => {
                this.changeDurationValidationRules();
            },
        );
    }

    @action.bound
    refresh() {
        this.symbol = null;
        WS.forgetAll('proposal');
    }

    @action.bound
    async prepareTradeStore() {
        let query_string_values = this.updateQueryString();
        this.smart_chart        = this.root_store.modules.smart_chart;
        const active_symbols    = await WS.activeSymbols();
        if (!active_symbols.active_symbols || active_symbols.active_symbols.length === 0) {
            this.root_store.common.showError(localize('Trading is unavailable at this time.'));
        }

        // Checks for finding out that the current account has access to the defined symbol in quersy string or not.
        const is_invalid_symbol = !!query_string_values.symbol &&
            !active_symbols.active_symbols.find(s => s.symbol === query_string_values.symbol);

        // Changes the symbol in query string to default symbol since the account doesn't have access to the defined symbol.
        if (is_invalid_symbol) {
            this.root_store.ui.addToastMessage({
                message: localize('Certain trade parameters have been changed due to your account settings.'),
                type   : 'info',
            });
            URLHelper.setQueryParam({ 'symbol': pickDefaultSymbol(active_symbols.active_symbols) });
            query_string_values = this.updateQueryString();
        }

        if (!this.symbol) {
            // TODO remove duplicates
            if (is_invalid_symbol) {
                URLHelper.setQueryParam({ 'symbol': pickDefaultSymbol(active_symbols.active_symbols) });
                query_string_values = this.updateQueryString();
            }

            await this.processNewValuesAsync({
                symbol: pickDefaultSymbol(active_symbols.active_symbols),
                ...query_string_values,
            });
        }

        if (this.symbol) {
            ContractType.buildContractTypesConfig(query_string_values.symbol || this.symbol).then(action(async () => {
                await this.processNewValuesAsync({
                    ...ContractType.getContractValues(this),
                    ...ContractType.getContractCategories(),
                    ...query_string_values,
                });
            }));
        }
    }

    @action.bound
    onChange(e) {
        const { name, value } = e.target;
        if (name === 'currency') {
            this.root_store.client.selectCurrency(value);
        } else if (!(name in this)) {
            throw new Error(`Invalid Argument: ${name}`);
        }

        this.processNewValuesAsync({ [name]: value }, true);
    }

    @action.bound
    onHoverPurchase(is_over, contract_type) {
        this.smart_chart.updateBarrierShade(is_over, contract_type);
    }

    @action.bound
    onPurchase(proposal_id, price, type) {
        if (proposal_id) {
            processPurchase(proposal_id, price).then(action((response) => {
                if (this.proposal_info[type].id !== proposal_id) {
                    throw new Error('Proposal ID does not match.');
                }
                if (response.buy) {
                    const contract_data = {
                        ...this.proposal_requests[type],
                        ...this.proposal_info[type],
                        buy_price: response.buy.buy_price,
                    };
                    GTM.pushPurchaseData(contract_data, this.root_store);
                }
                WS.forgetAll('proposal');
                this.purchase_info = response;
            }));
        }
    }

    @action.bound
    onClickNewTrade(e) {
        this.requestProposal();
        e.preventDefault();
    }

    /**
     * Updates the store with new values
     * @param  {Object} new_state - new values to update the store with
     * @return {Object} returns the object having only those values that are updated
     */
    @action.bound
    updateStore(new_state) {
        Object.keys(cloneObject(new_state)).forEach((key) => {
            // TODO prevent setting currency when logged in, but not when user is guest.
            if (key === 'root_store' || ['validation_rules', 'validation_errors', 'currency'].indexOf(key) > -1) return;
            if (JSON.stringify(this[key]) === JSON.stringify(new_state[key])) {
                delete new_state[key];
            } else {
                if (key === 'symbol') {
                    this.is_purchase_enabled = false;
                    this.is_trade_enabled    = false;
                }

                // Add changes to queryString of the url
                if (
                    allowed_query_string_variables.indexOf(key) !== -1 &&
                    this.is_trade_component_mounted
                ) {
                    URLHelper.setQueryParam({ [key]: new_state[key] });
                }

                this[key] = new_state[key];

                // validation is done in mobx intercept (base_store.js)
                // when barrier_1 is set, it is compared with store.barrier_2 (which is not updated yet)
                if (key === 'barrier_2' && new_state.barrier_1) {
                    this.barrier_1 = new_state.barrier_1; // set it again, after barrier_2 is updated in store
                }
            }
        });

        return new_state;
    }

    async processNewValuesAsync(obj_new_values = {}, is_changed_by_user = false) {
        // Sets the default value to Amount when Currency has changed from Fiat to Crypto and vice versa.
        // The source of default values is the website_status response.
        if (is_changed_by_user &&
            /\bcurrency\b/.test(Object.keys(obj_new_values)) &&
            isCryptocurrency(obj_new_values.currency) !== isCryptocurrency(this.currency)
        ) {
            obj_new_values.amount = obj_new_values.amount || getMinPayout(obj_new_values.currency);
        }

        const new_state = this.updateStore(cloneObject(obj_new_values));

        if (is_changed_by_user || /\b(symbol|contract_types_list)\b/.test(Object.keys(new_state))) {
            if ('symbol' in new_state) {
                await Symbol.onChangeSymbolAsync(new_state.symbol);
            }

            this.updateStore({ // disable purchase button(s), clear contract info
                is_purchase_enabled: false,
                proposal_info      : {},
            });

            if (!this.smart_chart.is_contract_mode) {
                const is_barrier_changed = 'barrier_1' in new_state || 'barrier_2' in new_state;
                if (is_barrier_changed) {
                    this.smart_chart.updateBarriers(this.barrier_1, this.barrier_2);
                } else {
                    this.smart_chart.removeBarriers();
                }
            }

            const snapshot            = await processTradeParams(this, new_state);
            const query_string_values = this.updateQueryString();
            snapshot.is_trade_enabled = true;

            this.updateStore({
                ...snapshot,
                ...(this.is_query_string_applied ? {} : query_string_values), // Applies the query string values again to set barriers.
            });

            this.is_query_string_applied = true;

            if (/\bcontract_type\b/.test(Object.keys(new_state))) {
                this.validateAllProperties();
            }

            this.debouncedProposal();
        }
    }

    @action.bound
    requestProposal() {
        const requests = createProposalRequests(this);

        if (Object.values(this.validation_errors).some(e => e.length)) {
            this.proposal_info = {};
            this.purchase_info = {};
            WS.forgetAll('proposal');
            return;
        }

        if (!isEmptyObject(requests)) {
            const proper_proposal_params_for_query_string = getProposalParametersName(requests);

            URLHelper.pruneQueryString(
                [
                    ...proper_proposal_params_for_query_string,
                    ...non_proposal_query_string_variable,
                ],
            );

            this.proposal_requests = requests;
            this.proposal_info     = {};
            this.purchase_info     = {};

            WS.forgetAll('proposal').then(() => {
                Object.keys(this.proposal_requests).forEach((type) => {
                    WS.subscribeProposal(this.proposal_requests[type], this.onProposalResponse);
                });
            });
        }
    }

    @action.bound
    onProposalResponse(response) {
        const contract_type = response.echo_req.contract_type;
        this.proposal_info  = {
            ...this.proposal_info,
            [contract_type]: getProposalInfo(this, response),
        };

        if (!this.smart_chart.is_contract_mode) {
            setChartBarrier(this.smart_chart, response, this.onChartBarrierChange);
        }

        this.is_purchase_enabled = true;
    }

    @action.bound
    onChartBarrierChange(barrier_1, barrier_2) {
        this.processNewValuesAsync({ barrier_1, barrier_2 }, true);
    }

    @action.bound
    updateQueryString() {

        // Update the url's query string by default values of the store
        const query_params = URLHelper.updateQueryString(
            this,
            allowed_query_string_variables,
            this.is_trade_component_mounted,
        );

        // update state values from query string
        const config = {};
        [...query_params].forEach(param => config[param[0]] = param[1]);
        return config;
    }

    @action.bound
    changeDurationValidationRules() {
        if (this.expiry_type === 'endtime') {
            this.validation_errors.duration = [];
            return;
        }

        const index  = this.validation_rules.duration.rules.findIndex(item => item[0] === 'number');
        const limits = this.duration_min_max[this.contract_expiry_type] || false;

        if (limits) {
            const duration_options = {
                min: convertDurationLimit(+limits.min, this.duration_unit),
                max: convertDurationLimit(+limits.max, this.duration_unit),
            };

            if (index > -1) {
                this.validation_rules.duration.rules[index][1] = duration_options;
            } else {
                this.validation_rules.duration.rules.push(['number', duration_options]);
            }
            this.validateProperty('duration', this.duration);
        }
    }

    @action.bound
    async accountSwitcherListener() {
        await this.refresh();
        await this.prepareTradeStore();
        this.debouncedProposal();
    }

    @action.bound
    async onMount() {
        this.onSwitchAccount(this.accountSwitcherListener);
        await this.prepareTradeStore();
        this.debouncedProposal();
        runInAction(() => {
            this.is_trade_component_mounted = true;
        });
        this.updateQueryString();
    }

    @action.bound
    onUnmount() {
        this.disposeSwitchAccount();
        WS.forgetAll('proposal');
        this.is_trade_component_mounted = false;
    }
}
<commit_msg>Remove duplicatesˆ<commit_after>import debounce                                                               from 'lodash.debounce';
import { action, observable, reaction, runInAction }                          from 'mobx';
import BinarySocket                                                           from '_common/base/socket_base';
import { localize }                                                           from '_common/localize';
import { cloneObject, isEmptyObject }                                         from '_common/utility';
import { getMinPayout, isCryptocurrency }                                     from '_common/base/currency_base';
import { WS }                                                                 from 'Services';
import GTM                                                                    from 'Utils/gtm';
import URLHelper                                                              from 'Utils/URL/url_helper';
import { processPurchase }                                                    from './Actions/purchase';
import * as Symbol                                                            from './Actions/symbol';
import { allowed_query_string_variables, non_proposal_query_string_variable } from './Constants/query_string';
import getValidationRules                                                     from './Constants/validation_rules';
import { setChartBarrier }                                                    from './Helpers/chart';
import ContractType                                                           from './Helpers/contract_type';
import { convertDurationLimit }                                               from './Helpers/duration';
import { processTradeParams }                                                 from './Helpers/process';
import { createProposalRequests, getProposalInfo, getProposalParametersName } from './Helpers/proposal';
import { pickDefaultSymbol }                                                  from './Helpers/symbol';
import BaseStore                                                              from '../../base_store';

export default class TradeStore extends BaseStore {
    // Control values
    @observable is_trade_component_mounted = false;
    @observable is_purchase_enabled        = false;
    @observable is_trade_enabled           = false;

    // Underlying
    @observable symbol;

    // Contract Type
    @observable contract_expiry_type = '';
    @observable contract_start_type  = '';
    @observable contract_type        = '';
    @observable contract_types_list  = {};
    @observable form_components      = [];
    @observable trade_types          = {};

    // Amount
    @observable amount          = 10;
    @observable basis           = '';
    @observable basis_list      = [];

    // Duration
    @observable duration            = 5;
    @observable duration_unit       = '';
    @observable duration_units_list = [];
    @observable duration_min_max    = {};
    @observable expiry_date         = '';
    @observable expiry_time         = '09:40';
    @observable expiry_type         = 'duration';

    // Barrier
    @observable barrier_1     = '';
    @observable barrier_2     = '';
    @observable barrier_count = 0;

    // Start Time
    @observable start_date       = Number(0); // Number(0) refers to 'now'
    @observable start_dates_list = [];
    @observable start_time       = '12:30';
    @observable sessions         = [];

    // Last Digit
    @observable last_digit = 5;

    // Purchase
    @observable proposal_info = {};
    @observable purchase_info = {};

    // Chart
    chart_id = 1;

    debouncedProposal = debounce(this.requestProposal, 500);
    @action.bound
    init = async () => {
        // To be sure that the website_status response has been received before processing trading page.
        await BinarySocket.wait('website_status');
    };

    proposal_requests = {};

    constructor({ root_store }) {
        URLHelper.pruneQueryString(allowed_query_string_variables);

        super({
            root_store,
            session_storage_properties: allowed_query_string_variables,
            validation_rules          : getValidationRules(),
        });

        Object.defineProperty(
            this,
            'is_query_string_applied',
            {
                enumerable: false,
                value     : false,
                writable  : true,
            },
        );
        // Adds intercept to change min_max value of duration validation
        reaction(
            () => [this.contract_expiry_type, this.duration_min_max, this.duration_unit, this.expiry_type],
            () => {
                this.changeDurationValidationRules();
            },
        );
    }

    @action.bound
    refresh() {
        this.symbol = null;
        WS.forgetAll('proposal');
    }

    @action.bound
    async prepareTradeStore() {
        let query_string_values = this.updateQueryString();
        this.smart_chart        = this.root_store.modules.smart_chart;
        const active_symbols    = await WS.activeSymbols();
        if (!active_symbols.active_symbols || active_symbols.active_symbols.length === 0) {
            this.root_store.common.showError(localize('Trading is unavailable at this time.'));
        }

        // Checks for finding out that the current account has access to the defined symbol in quersy string or not.
        const is_invalid_symbol = !!query_string_values.symbol &&
            !active_symbols.active_symbols.find(s => s.symbol === query_string_values.symbol);

        // Changes the symbol in query string to default symbol since the account doesn't have access to the defined symbol.
        if (is_invalid_symbol) {
            this.root_store.ui.addToastMessage({
                message: localize('Certain trade parameters have been changed due to your account settings.'),
                type   : 'info',
            });
            URLHelper.setQueryParam({ 'symbol': pickDefaultSymbol(active_symbols.active_symbols) });
            query_string_values = this.updateQueryString();
        }

        if (!this.symbol) {
            await this.processNewValuesAsync({
                symbol: pickDefaultSymbol(active_symbols.active_symbols),
                ...query_string_values,
            });
        }

        if (this.symbol) {
            ContractType.buildContractTypesConfig(query_string_values.symbol || this.symbol).then(action(async () => {
                await this.processNewValuesAsync({
                    ...ContractType.getContractValues(this),
                    ...ContractType.getContractCategories(),
                    ...query_string_values,
                });
            }));
        }
    }

    @action.bound
    onChange(e) {
        const { name, value } = e.target;
        if (name === 'currency') {
            this.root_store.client.selectCurrency(value);
        } else if (!(name in this)) {
            throw new Error(`Invalid Argument: ${name}`);
        }

        this.processNewValuesAsync({ [name]: value }, true);
    }

    @action.bound
    onHoverPurchase(is_over, contract_type) {
        this.smart_chart.updateBarrierShade(is_over, contract_type);
    }

    @action.bound
    onPurchase(proposal_id, price, type) {
        if (proposal_id) {
            processPurchase(proposal_id, price).then(action((response) => {
                if (this.proposal_info[type].id !== proposal_id) {
                    throw new Error('Proposal ID does not match.');
                }
                if (response.buy) {
                    const contract_data = {
                        ...this.proposal_requests[type],
                        ...this.proposal_info[type],
                        buy_price: response.buy.buy_price,
                    };
                    GTM.pushPurchaseData(contract_data, this.root_store);
                }
                WS.forgetAll('proposal');
                this.purchase_info = response;
            }));
        }
    }

    @action.bound
    onClickNewTrade(e) {
        this.requestProposal();
        e.preventDefault();
    }

    /**
     * Updates the store with new values
     * @param  {Object} new_state - new values to update the store with
     * @return {Object} returns the object having only those values that are updated
     */
    @action.bound
    updateStore(new_state) {
        Object.keys(cloneObject(new_state)).forEach((key) => {
            if (key === 'root_store' || ['validation_rules', 'validation_errors', 'currency'].indexOf(key) > -1) return;
            if (JSON.stringify(this[key]) === JSON.stringify(new_state[key])) {
                delete new_state[key];
            } else {
                if (key === 'symbol') {
                    this.is_purchase_enabled = false;
                    this.is_trade_enabled    = false;
                }

                // Add changes to queryString of the url
                if (
                    allowed_query_string_variables.indexOf(key) !== -1 &&
                    this.is_trade_component_mounted
                ) {
                    URLHelper.setQueryParam({ [key]: new_state[key] });
                }

                this[key] = new_state[key];

                // validation is done in mobx intercept (base_store.js)
                // when barrier_1 is set, it is compared with store.barrier_2 (which is not updated yet)
                if (key === 'barrier_2' && new_state.barrier_1) {
                    this.barrier_1 = new_state.barrier_1; // set it again, after barrier_2 is updated in store
                }
            }
        });

        return new_state;
    }

    async processNewValuesAsync(obj_new_values = {}, is_changed_by_user = false) {
        // Sets the default value to Amount when Currency has changed from Fiat to Crypto and vice versa.
        // The source of default values is the website_status response.
        if (is_changed_by_user &&
            /\bcurrency\b/.test(Object.keys(obj_new_values)) &&
            isCryptocurrency(obj_new_values.currency) !== isCryptocurrency(this.currency)
        ) {
            obj_new_values.amount = obj_new_values.amount || getMinPayout(obj_new_values.currency);
        }

        const new_state = this.updateStore(cloneObject(obj_new_values));

        if (is_changed_by_user || /\b(symbol|contract_types_list)\b/.test(Object.keys(new_state))) {
            if ('symbol' in new_state) {
                await Symbol.onChangeSymbolAsync(new_state.symbol);
            }

            this.updateStore({ // disable purchase button(s), clear contract info
                is_purchase_enabled: false,
                proposal_info      : {},
            });

            if (!this.smart_chart.is_contract_mode) {
                const is_barrier_changed = 'barrier_1' in new_state || 'barrier_2' in new_state;
                if (is_barrier_changed) {
                    this.smart_chart.updateBarriers(this.barrier_1, this.barrier_2);
                } else {
                    this.smart_chart.removeBarriers();
                }
            }

            const snapshot            = await processTradeParams(this, new_state);
            const query_string_values = this.updateQueryString();
            snapshot.is_trade_enabled = true;

            this.updateStore({
                ...snapshot,
                ...(this.is_query_string_applied ? {} : query_string_values), // Applies the query string values again to set barriers.
            });

            this.is_query_string_applied = true;

            if (/\bcontract_type\b/.test(Object.keys(new_state))) {
                this.validateAllProperties();
            }

            this.debouncedProposal();
        }
    }

    @action.bound
    requestProposal() {
        const requests = createProposalRequests(this);

        if (Object.values(this.validation_errors).some(e => e.length)) {
            this.proposal_info = {};
            this.purchase_info = {};
            WS.forgetAll('proposal');
            return;
        }

        if (!isEmptyObject(requests)) {
            const proper_proposal_params_for_query_string = getProposalParametersName(requests);

            URLHelper.pruneQueryString(
                [
                    ...proper_proposal_params_for_query_string,
                    ...non_proposal_query_string_variable,
                ],
            );

            this.proposal_requests = requests;
            this.proposal_info     = {};
            this.purchase_info     = {};

            WS.forgetAll('proposal').then(() => {
                Object.keys(this.proposal_requests).forEach((type) => {
                    WS.subscribeProposal(this.proposal_requests[type], this.onProposalResponse);
                });
            });
        }
    }

    @action.bound
    onProposalResponse(response) {
        const contract_type = response.echo_req.contract_type;
        this.proposal_info  = {
            ...this.proposal_info,
            [contract_type]: getProposalInfo(this, response),
        };

        if (!this.smart_chart.is_contract_mode) {
            setChartBarrier(this.smart_chart, response, this.onChartBarrierChange);
        }

        this.is_purchase_enabled = true;
    }

    @action.bound
    onChartBarrierChange(barrier_1, barrier_2) {
        this.processNewValuesAsync({ barrier_1, barrier_2 }, true);
    }

    @action.bound
    updateQueryString() {

        // Update the url's query string by default values of the store
        const query_params = URLHelper.updateQueryString(
            this,
            allowed_query_string_variables,
            this.is_trade_component_mounted,
        );

        // update state values from query string
        const config = {};
        [...query_params].forEach(param => config[param[0]] = param[1]);
        return config;
    }

    @action.bound
    changeDurationValidationRules() {
        if (this.expiry_type === 'endtime') {
            this.validation_errors.duration = [];
            return;
        }

        const index  = this.validation_rules.duration.rules.findIndex(item => item[0] === 'number');
        const limits = this.duration_min_max[this.contract_expiry_type] || false;

        if (limits) {
            const duration_options = {
                min: convertDurationLimit(+limits.min, this.duration_unit),
                max: convertDurationLimit(+limits.max, this.duration_unit),
            };

            if (index > -1) {
                this.validation_rules.duration.rules[index][1] = duration_options;
            } else {
                this.validation_rules.duration.rules.push(['number', duration_options]);
            }
            this.validateProperty('duration', this.duration);
        }
    }

    @action.bound
    async accountSwitcherListener() {
        await this.refresh();
        await this.prepareTradeStore();
        this.debouncedProposal();
    }

    @action.bound
    async onMount() {
        this.onSwitchAccount(this.accountSwitcherListener);
        await this.prepareTradeStore();
        this.debouncedProposal();
        runInAction(() => {
            this.is_trade_component_mounted = true;
        });
        this.updateQueryString();
    }

    @action.bound
    onUnmount() {
        this.disposeSwitchAccount();
        WS.forgetAll('proposal');
        this.is_trade_component_mounted = false;
    }
}
<|endoftext|>


<commit_before>var Cookies = require('../../lib/js-cookie');

var email_rot13 = function(str) {
    return str.replace(/[a-zA-Z]/g, function(c) {
        var c2 = c.charCodeAt(0) + 13;
        return String.fromCharCode((c <= 'Z' ? 90 : 122) >= c2 ? c2 : c2 - 26);
    });
};

// returns true if internet explorer browser
var isIE = function() {
    return /(msie|trident|edge)/i.test(window.navigator.userAgent) && !window.opera;
};

// hide and show hedging value if trading purpose is set to hedging
var detect_hedging = function($purpose, $hedging) {
    $purpose.change(function() {
        if ($purpose.val() === 'Hedging') {
            $hedging.removeClass('invisible');
        } else if ($hedging.is(':visible')) {
            $hedging.addClass('invisible');
        }
    });
};

var jqueryuiTabsToDropdown = function($container) {
    var $ddl = $('<select/>');
    $container.find('li a').each(function() {
        $ddl.append($('<option/>', { text: $(this).text(), value: $(this).attr('href') }));
    });
    $ddl.change(function() {
        $container.find('li a[href="' + $(this).val() + '"]').click();
    });
    return $ddl;
};

// use function to generate elements and append them
// element is select and element to append is option
var appendTextValueChild = function(element, text, value, disabled, el_class) {
    if (element && !element.nodeName) {
        if (typeof element === 'string') {
            element = document.getElementById(element);
        } else {
            element = undefined;
        }
    }
    if (!element) return;
    var option = document.createElement('option');
    option.text = text;
    // setting null value helps with detecting required error
    // on 'Please select' options
    // that have no value of their own
    option.value = value || '';
    if (disabled === 'disabled') {
        option.setAttribute('disabled', 'disabled');
    }
    if (el_class) {
        option.className = el_class;
    }
    element.appendChild(option);
};

var isValidDate = function(day, month, year) {
    // Assume not leap year by default (note zero index for Jan)
    var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

    // If evenly divisible by 4 and not evenly divisible by 100,
    // or is evenly divisible by 400, then a leap year
    if (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) {
        daysInMonth[1] = 29;
    }
    return day <= daysInMonth[--month];
};

/*
 * function to check if element is visible or not
 *
 * alternative to jquery $('#id').is(':visible')
 */
var isVisible = function(elem) {
    'use strict';

    if (!elem || (elem.offsetWidth === 0 && elem.offsetHeight === 0)) {
        return false;
    }

    return true;
};

/*
 * function to check if browser supports the type date/time
 * send a wrong val in case browser 'pretends' to support
 */
function checkInput(type, wrongVal) {
    var input = document.createElement('input');
    input.setAttribute('type', type);
    input.setAttribute('value', wrongVal);
    return (input.value !== wrongVal);
}

/*
 * function to check if new date is selected using native picker
 * if yes, update the data-value. if no, return false.
 */
function dateValueChanged(element, type) {
    if (element.getAttribute('data-value') === element.value) {
        return false;
    }
    if (element.getAttribute('type') === type) {
        element.setAttribute('data-value', element.value);
    }
    return true;
}

module.exports = {
    getLoginToken         : function() { return Cookies.get('login'); },
    email_rot13           : email_rot13,
    isIE                  : isIE,
    detect_hedging        : detect_hedging,
    jqueryuiTabsToDropdown: jqueryuiTabsToDropdown,
    appendTextValueChild  : appendTextValueChild,
    isValidDate           : isValidDate,
    isVisible             : isVisible,
    checkInput            : checkInput,
    dateValueChanged      : dateValueChanged,
};
<commit_msg>add functions to common_functions to check if selector exist in dom and functions to set/get innerHTML or textContent only if selector exists<commit_after>var Cookies = require('../../lib/js-cookie');

var email_rot13 = function(str) {
    return str.replace(/[a-zA-Z]/g, function(c) {
        var c2 = c.charCodeAt(0) + 13;
        return String.fromCharCode((c <= 'Z' ? 90 : 122) >= c2 ? c2 : c2 - 26);
    });
};

// returns true if internet explorer browser
var isIE = function() {
    return /(msie|trident|edge)/i.test(window.navigator.userAgent) && !window.opera;
};

// hide and show hedging value if trading purpose is set to hedging
var detect_hedging = function($purpose, $hedging) {
    $purpose.change(function() {
        if ($purpose.val() === 'Hedging') {
            $hedging.removeClass('invisible');
        } else if ($hedging.is(':visible')) {
            $hedging.addClass('invisible');
        }
    });
};

var jqueryuiTabsToDropdown = function($container) {
    var $ddl = $('<select/>');
    $container.find('li a').each(function() {
        $ddl.append($('<option/>', { text: $(this).text(), value: $(this).attr('href') }));
    });
    $ddl.change(function() {
        $container.find('li a[href="' + $(this).val() + '"]').click();
    });
    return $ddl;
};

// use function to generate elements and append them
// element is select and element to append is option
var appendTextValueChild = function(element, text, value, disabled, el_class) {
    if (element && !element.nodeName) {
        if (typeof element === 'string') {
            element = document.getElementById(element);
        } else {
            element = undefined;
        }
    }
    if (!element) return;
    var option = document.createElement('option');
    option.text = text;
    // setting null value helps with detecting required error
    // on 'Please select' options
    // that have no value of their own
    option.value = value || '';
    if (disabled === 'disabled') {
        option.setAttribute('disabled', 'disabled');
    }
    if (el_class) {
        option.className = el_class;
    }
    element.appendChild(option);
};

var isValidDate = function(day, month, year) {
    // Assume not leap year by default (note zero index for Jan)
    var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

    // If evenly divisible by 4 and not evenly divisible by 100,
    // or is evenly divisible by 400, then a leap year
    if (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) {
        daysInMonth[1] = 29;
    }
    return day <= daysInMonth[--month];
};

/*
 * function to check if element is visible or not
 *
 * alternative to jquery $('#id').is(':visible')
 */
var isVisible = function(elem) {
    'use strict';

    if (!elem || (elem.offsetWidth === 0 && elem.offsetHeight === 0)) {
        return false;
    }

    return true;
};

/*
 * function to check if browser supports the type date/time
 * send a wrong val in case browser 'pretends' to support
 */
function checkInput(type, wrongVal) {
    var input = document.createElement('input');
    input.setAttribute('type', type);
    input.setAttribute('value', wrongVal);
    return (input.value !== wrongVal);
}

/*
 * function to check if new date is selected using native picker
 * if yes, update the data-value. if no, return false.
 */
function dateValueChanged(element, type) {
    if (element.getAttribute('data-value') === element.value) {
        return false;
    }
    if (element.getAttribute('type') === type) {
        element.setAttribute('data-value', element.value);
    }
    return true;
}

function selectorExists(element) {
  if (typeof(element) != 'undefined' && element != null)
  {
    return true;
  }
  else {
    return false;
  }
}

function elementTextContent(element, text) {
  if (selectorExists(element)) {
    if (text) element.textContent = text;
    else return element.textContent;
  }
}

function elementInnerHtml(element, text) {
  if (selectorExists(element)) {
    if (text) element.innerHTML = text;
    else return element.innerHTML;
  }
}

module.exports = {
    getLoginToken         : function() { return Cookies.get('login'); },
    email_rot13           : email_rot13,
    isIE                  : isIE,
    detect_hedging        : detect_hedging,
    jqueryuiTabsToDropdown: jqueryuiTabsToDropdown,
    appendTextValueChild  : appendTextValueChild,
    isValidDate           : isValidDate,
    isVisible             : isVisible,
    checkInput            : checkInput,
    dateValueChanged      : dateValueChanged,
    selectorExists        : selectorExists,
    elementTextContent    : elementTextContent,
    elementInnerHtml      : elementInnerHtml,
};
<|endoftext|>


<commit_before>var account_transferws = (function(){
    "use strict";
    var $form ;
    var account_from , account_to ,account_bal;
    var currType, MLTBal,MFBal,MLCurrType,MFCurrType;

    var init = function(){
        $form = $('#account_transfer');
        $("#success_form").hide();
        $("#client_message").hide();
        account_bal = 0;

        BinarySocket.send({"authorize": $.cookie('login'), "passthrough": {"value": "initValues"}});

        $form.find("button").on("click", function(e){
            e.preventDefault();
            e.stopPropagation();

            if(validateForm() === false){
                return false;
            }

            BinarySocket.send({"authorize": $.cookie('login'), "passthrough": {"value": "transfer_between_accounts"}});
        });

        $form.find("#transfer_account_transfer").on("change",function(){
            var accounts = $("#transfer_account_transfer option:selected").text();
            var matches = accounts
                            .split('(')
                            .filter(function(v){
                                return v.indexOf(')') > -1})
                            .map( function(value) {
                                return value.split(')')[0]
                        });

            account_from = matches[0];
            account_to = matches[1];

            if(account_from.substring(0,2) == "MF"){
                account_bal = MFBal;
                currType = MFCurrType;
            }else if(account_from.substring(0,2) == "ML"){
                account_bal = MLTBal;
                currType = MLCurrType;
            }

            $form.find("#currencyType").html(currType);
        });
    };
    var validateForm =function(){

        var amt = $form.find("#acc_transfer_amount").val();
        var isValid = true;

        if(amt.length <=0 ){
            $form.find("#invalid_amount").text(text.localize("Invalid amount. Minimum transfer amount is 0.10, and up to 2 decimal places."));
            isValid = false;
        }

        if((/USD/.test(currType) === false) && (/EUR/.test(currType) === false) )
        {
            $form.find("#invalid_amount").text(text.localize("Invalid currency."));
            isValid = false;
        }

        return isValid;
    };

    var apiResponse = function(response){
        var type = response.msg_type;
        if (type === "transfer_between_accounts" || (type === "error" && "transfer_between_accounts" in response.echo_req)){
           responseMessage(response);

        }
        else if(type === "balance" || (type === "error" && "balance" in response.echo_req))
        {
            responseMessage(response);
        }
        else if(type === "authorize" || (type === "error" && "authorize" in response.echo_req))
        {
            isAuthorized(response);
        }
    };

    var isAuthorized =  function(response){
        if(response.echo_req.passthrough){
            var option= response.echo_req.passthrough.value ;
            var amt = $form.find("#acc_transfer_amount").val();

            switch(option){
                case   "initValues":
                        BinarySocket.send({
                            "transfer_between_accounts": "1",
                            "passthrough" : {"value" : "set_client"}
                        });
                        break;
                case   "transfer_between_accounts" :
                        BinarySocket.send({
                            "transfer_between_accounts": "1",
                            "account_from": account_from,
                            "account_to": account_to,
                            "currency": currType,
                            "amount": amt
                        });
                        break;
            }

        }
    };

    var responseMessage = function(response) {
        var resvalue ;
        var str;

        if("error" in response) {
                if("message" in response.error) {
                    $("#client_message").show();
                    $("#client_message p").html(text.localize(response.error.message));
                    $("#success_form").hide();
                    $form.hide();
                    return false;
                }
                return false;
        }
        else if ("transfer_between_accounts" in response){

            if(response.echo_req.passthrough.value == "get_new_balance"){

                $.each(response.accounts,function(key,value){
                    $form.hide();
                    $("#success_form").show();
                    $("#client_message").hide();

                    if(value.loginid == account_from){
                        $("#loginid_1").html(value.loginid);
                        $("#balance_1").html(value.balance);
                    }
                    else if(value.loginid == account_to){
                        $("#loginid_2").html(value.loginid);
                        $("#balance_2").html(value.balance);

                    }
                });
            }
            else if(response.echo_req.passthrough.value =="set_client"){
                var optionMF, optionML ,str, bal1,bal2;
                var firstbal,secondbal,firstacct,secondacct,firstCurrType,firstbal,secondbal,SecondCurrType;
                $.each(response.accounts, function(index,value){
                    if(index == 0){
                        firstbal = value["balance"];
                        firstCurrType = value["currency"];
                        firstacct  = value["loginid"];
                    }
                    else{
                        secondbal = value["balance"];
                        SecondCurrType = value["currency"];
                        secondacct = value["loginid"];
                    }

                    if(value["loginid"].substring(0,2) == "MF"){
                        MFBal = value["balance"];
                        MFCurrType  = value["currency"];
                    }
                    else if(value["loginid"].substring(0,2) == "ML")
                    {
                        MLTBal = value["balance"];
                        MLCurrType = value["currency"];
                    }

                    if($.isEmptyObject(firstbal) || (firstbal == 0))
                    {
                        account_from = secondacct;
                        firstbal = secondbal;
                        currType = SecondCurrType;

                        account_to = firstacct;
                        secondbal = firstbal;
                    }
                    else{
                        account_from = firstacct;
                        firstbal = firstbal;

                        secondbal = secondbal;

                        account_to = secondacct;
                        currType = firstCurrType;
                    }

                });

                account_bal = firstbal;

                if((firstbal <=0) && (account_to !== undefined) ){
                    $("#client_message").show();
                    $("#success_form").hide();
                    $form.hide();
                    return false;
                }
                else if(account_to == undefined || account_from === undefined || $.isEmptyObject(account_to))
                {
                    $("#client_message").show();
                    $("#client_message p").html(text.localize("The account transfer is unavailable for your account."));
                    $("#success_form").hide();
                    $form.hide();
                    return false;
                }
                else if(account_to == secondacct && account_from == firstacct){
                    $form.find("#currencyType").html(currType);

                    if(account_from.substring(0,2) == "MF"){

                        optionMF = $form.find("#transfer_account_transfer option[value='ftg']");
                        str = text.localize("from financial account (" + account_from + ") to gaming account (" + account_to + ")");
                        optionMF.text(str);
                        optionMF.attr('selected', 'selected');
                        if(secondbal > 0){
                            str  = text.localize("from gaming account (" + account_to + ") to financial account (" + account_from + ")");
                            optionML  = $form.find("#transfer_account_transfer option[value='gtf']");
                            optionML.text(str);

                        }
                        else{
                            optionML  = $form.find("#transfer_account_transfer option[value='gtf']");
                            optionML.remove();
                        }
                    }
                    else if(account_from.substring(0,2) == "ML")
                    {
                        str  = text.localize("from gaming account (" + account_from + ") to financial account (" + account_to + ")");
                        optionML  = $form.find("#transfer_account_transfer option[value='gtf']");
                        optionML.text(str);
                        optionML.attr('selected', 'selected');

                        if(secondbal > 0){
                            optionMF = $form.find("#transfer_account_transfer option[value='ftg']");
                            str = text.localize("from financial account (" + account_to + ") to gaming account (" + account_from + ")");
                            optionMF.text(str);
                        }
                        else{
                            optionMF = $form.find("#transfer_account_transfer option[value='ftg']");
                            optionMF.remove();

                        }
                    }

                }
                else if(account_to == firstacct && account_from == secondacct)
                {
                    $form.find("#currencyType").html(currType);
                    if(account_from.substring(0,2) =="MF"){
                        optionMF = $form.find("#transfer_account_transfer option[value='ftg']");
                        str = text.localize("from financial account (" + account_from + ") to gaming account (" + account_to + ")");
                        optionMF.text(str);
                        optionMF.attr('selected', 'selected');
                        optionML  = $form.find("#transfer_account_transfer option[value='gtf']");
                        optionML.remove();
                    }
                    else if(account_from.substring(0,2) == "ML")
                    {
                        str  = text.localize("from gaming account (" + account_from + ") to financial account (" + account_to+ ")");
                        optionML  = $form.find("#transfer_account_transfer option[value='gtf']");
                        optionML.text(str);
                        optionML.attr('selected', 'selected');

                        optionMF = $form.find("#transfer_account_transfer option[value='ftg']");
                        optionMF.remove();
                    }

                }

            }
            else{
                BinarySocket.send({
                    "transfer_between_accounts": "1",
                    "passthrough" : {"value" : "get_new_balance"}
                });

            }
        }

    };

    return {
        init : init,
        apiResponse : apiResponse
    };

})();

pjax_config_page("cashier/account_transferws", function() {
    return {
        onLoad: function() {
        	if (!getCookieItem('login')) {
                window.location.href = page.url.url_for('login');
                return;
            }
            if((/VRT/.test($.cookie('loginid')))){
                window.location.href = ("/");
            }

        	BinarySocket.init({
                onmessage: function(msg){
                    var response = JSON.parse(msg.data);
                    if (response) {
                        account_transferws.apiResponse(response);
                    }
                }
            });

            account_transferws.init();
        }
    };
});<commit_msg>adding support for available currencies<commit_after>var account_transferws = (function(){
    "use strict";
    var $form ;
    var account_from , account_to ,account_bal;
    var currType, MLTBal,MFBal,MLCurrType,MFCurrType;
    var availableCurr= {} ;

    var init = function(){
        $form = $('#account_transfer');
        $("#success_form").hide();
        $("#client_message").hide();
        account_bal = 0;

        BinarySocket.send({"authorize": $.cookie('login'), "passthrough": {"value": "initValues"}});
        BinarySocket.send({"authorize": $.cookie('login'), "passthrough": {"value": "payout_currencies"}})

        $form.find("button").on("click", function(e){
            e.preventDefault();
            e.stopPropagation();

            if(validateForm() === false){
                return false;
            }

            BinarySocket.send({"authorize": $.cookie('login'), "passthrough": {"value": "transfer_between_accounts"}});
        });

        $form.find("#transfer_account_transfer").on("change",function(){
            var accounts = $("#transfer_account_transfer option:selected").text();
            var matches = accounts
                            .split('(')
                            .filter(function(v){
                                return v.indexOf(')') > -1})
                            .map( function(value) {
                                return value.split(')')[0]
                        });

            account_from = matches[0];
            account_to = matches[1];

            if(account_from.substring(0,2) == "MF"){
                account_bal = MFBal;
                currType = MFCurrType;
            }else if(account_from.substring(0,2) == "ML"){
                account_bal = MLTBal;
                currType = MLCurrType;
            }

            $form.find("#currencyType").html(currType);
        });
    };
    var validateForm =function(){

        var amt = $form.find("#acc_transfer_amount").val();
        var isValid = true;

        if(amt.length <=0 ){
            $form.find("#invalid_amount").text(text.localize("Invalid amount. Minimum transfer amount is 0.10, and up to 2 decimal places."));
            isValid = false;
        }
        if($.inArray(currType, availableCurr) == -1)
        {
            $form.find("#invalid_amount").text(text.localize("Invalid currency."));
            isValid = false;
        }

        return isValid;
    };

    var apiResponse = function(response){
        var type = response.msg_type;
        if (type === "transfer_between_accounts" || (type === "error" && "transfer_between_accounts" in response.echo_req)){
           responseMessage(response);

        }
        else if(type === "payout_currencies" || (type === "error" && "payout_currencies" in response.echo_req))
        {
            responseMessage(response);
        }
        else if(type === "authorize" || (type === "error" && "authorize" in response.echo_req))
        {
            isAuthorized(response);
        }
    };

    var isAuthorized =  function(response){
        if(response.echo_req.passthrough){
            var option= response.echo_req.passthrough.value ;
            var amt = $form.find("#acc_transfer_amount").val();

            switch(option){
                case   "initValues":
                        BinarySocket.send({
                            "transfer_between_accounts": "1",
                            "passthrough" : {"value" : "set_client"}
                        });
                        break;
                case   "transfer_between_accounts" :
                        BinarySocket.send({
                            "transfer_between_accounts": "1",
                            "account_from": account_from,
                            "account_to": account_to,
                            "currency": currType,
                            "amount": amt
                        });
                        break;
                case   "payout_currencies" :
                        BinarySocket.send({
                            "payout_currencies": "1"
                        });
                        break;
            }

        }
    };

    var responseMessage = function(response) {
        var resvalue ;
        var str;
        if("error" in response) {
                if("message" in response.error) {
                    console.log("Its a backend error");
                    $("#client_message").show();
                    $("#client_message p").html(text.localize(response.error.message));
                    $("#success_form").hide();
                    $form.hide();
                    return false;
                }
                return false;
        }
        else if("payout_currencies" in response){

            availableCurr = response.payout_currencies;

        }
        else if ("transfer_between_accounts" in response){

            if(response.echo_req.passthrough.value == "get_new_balance"){

                $.each(response.accounts,function(key,value){
                    $form.hide();
                    $("#success_form").show();
                    $("#client_message").hide();

                    if(value.loginid == account_from){
                        $("#loginid_1").html(value.loginid);
                        $("#balance_1").html(value.balance);
                    }
                    else if(value.loginid == account_to){
                        $("#loginid_2").html(value.loginid);
                        $("#balance_2").html(value.balance);

                    }
                });
            }
            else if(response.echo_req.passthrough.value =="set_client"){
                var optionMF, optionML ,str, bal1,bal2;
                var firstbal,secondbal,firstacct,secondacct,firstCurrType,firstbal,secondbal,SecondCurrType;
                $.each(response.accounts, function(index,value){
                    if(index == 0){
                        firstbal = value["balance"];
                        firstCurrType = value["currency"];
                        firstacct  = value["loginid"];
                    }
                    else{
                        secondbal = value["balance"];
                        SecondCurrType = value["currency"];
                        secondacct = value["loginid"];
                    }

                    if(value["loginid"].substring(0,2) == "MF"){
                        MFBal = value["balance"];
                        MFCurrType  = value["currency"];
                    }
                    else if(value["loginid"].substring(0,2) == "ML")
                    {
                        MLTBal = value["balance"];
                        MLCurrType = value["currency"];
                    }

                    if($.isEmptyObject(firstbal) || (firstbal == 0))
                    {
                        account_from = secondacct;
                        firstbal = secondbal;
                        currType = SecondCurrType;

                        account_to = firstacct;
                        secondbal = firstbal;
                    }
                    else{
                        account_from = firstacct;
                        firstbal = firstbal;

                        secondbal = secondbal;

                        account_to = secondacct;
                        currType = firstCurrType;
                    }

                });

                account_bal = firstbal;

                if((firstbal <=0) && (account_to !== undefined) ){
                    $("#client_message").show();
                    $("#success_form").hide();
                    $form.hide();
                    return false;
                }
                else if(account_to == undefined || account_from === undefined || $.isEmptyObject(account_to))
                {
                    $("#client_message").show();
                    $("#client_message p").html(text.localize("The account transfer is unavailable for your account."));
                    $("#success_form").hide();
                    $form.hide();
                    return false;
                }
                else if(account_to == secondacct && account_from == firstacct){
                    $form.find("#currencyType").html(currType);

                    if(account_from.substring(0,2) == "MF"){

                        optionMF = $form.find("#transfer_account_transfer option[value='ftg']");
                        str = text.localize("from financial account (" + account_from + ") to gaming account (" + account_to + ")");
                        optionMF.text(str);
                        optionMF.attr('selected', 'selected');
                        if(secondbal > 0){
                            str  = text.localize("from gaming account (" + account_to + ") to financial account (" + account_from + ")");
                            optionML  = $form.find("#transfer_account_transfer option[value='gtf']");
                            optionML.text(str);

                        }
                        else{
                            optionML  = $form.find("#transfer_account_transfer option[value='gtf']");
                            optionML.remove();
                        }
                    }
                    else if(account_from.substring(0,2) == "ML")
                    {
                        str  = text.localize("from gaming account (" + account_from + ") to financial account (" + account_to + ")");
                        optionML  = $form.find("#transfer_account_transfer option[value='gtf']");
                        optionML.text(str);
                        optionML.attr('selected', 'selected');

                        if(secondbal > 0){
                            optionMF = $form.find("#transfer_account_transfer option[value='ftg']");
                            str = text.localize("from financial account (" + account_to + ") to gaming account (" + account_from + ")");
                            optionMF.text(str);
                        }
                        else{
                            optionMF = $form.find("#transfer_account_transfer option[value='ftg']");
                            optionMF.remove();

                        }
                    }

                }
                else if(account_to == firstacct && account_from == secondacct)
                {
                    $form.find("#currencyType").html(currType);
                    if(account_from.substring(0,2) =="MF"){
                        optionMF = $form.find("#transfer_account_transfer option[value='ftg']");
                        str = text.localize("from financial account (" + account_from + ") to gaming account (" + account_to + ")");
                        optionMF.text(str);
                        optionMF.attr('selected', 'selected');
                        optionML  = $form.find("#transfer_account_transfer option[value='gtf']");
                        optionML.remove();
                    }
                    else if(account_from.substring(0,2) == "ML")
                    {
                        str  = text.localize("from gaming account (" + account_from + ") to financial account (" + account_to+ ")");
                        optionML  = $form.find("#transfer_account_transfer option[value='gtf']");
                        optionML.text(str);
                        optionML.attr('selected', 'selected');

                        optionMF = $form.find("#transfer_account_transfer option[value='ftg']");
                        optionMF.remove();
                    }

                }

            }
            else{
                BinarySocket.send({
                    "transfer_between_accounts": "1",
                    "passthrough" : {"value" : "get_new_balance"}
                });

            }
        }

    };

    return {
        init : init,
        apiResponse : apiResponse
    };

})();

pjax_config_page("cashier/account_transferws", function() {
    return {
        onLoad: function() {
        	if (!getCookieItem('login')) {
                window.location.href = page.url.url_for('login');
                return;
            }
            if((/VRT/.test($.cookie('loginid')))){
                window.location.href = ("/");
            }

        	BinarySocket.init({
                onmessage: function(msg){
                    var response = JSON.parse(msg.data);
                    if (response) {
                        account_transferws.apiResponse(response);
                    }
                }
            });

            account_transferws.init();
        }
    };
});<|endoftext|>


<commit_before>import Entry from 'admin-config/lib/Entry';

export default function maEmbeddedListField() {
    return {
        scope: {
            'field': '&',
            'value': '=',
            'datastore': '&'
        },
        restrict: 'E',
        link: {
            pre: function(scope) {
                const field = scope.field();
                const targetEntity = field.targetEntity();
                const targetEntityName = targetEntity.name();
                const targetFields = field.targetFields();
                const sortField = field.sortField();
                const sortDir = field.sortDir() === 'DESC' ? -1 : 1;
                var filterFunc;
                if (field.permanentFilters()) {
                    const filters = field.permanentFilters();
                    const filterKeys = Object.keys(filters);
                    filterFunc = (entry) => {
                        return filterKeys.reduce((isFiltered, key) => isFiltered && entry.values[key] === filters[key], true);
                    };
                } else {
                    filterFunc = () => true;
                }
                scope.fields = targetFields;
                scope.entries = Entry
                    .createArrayFromRest(scope.value || [], targetFields, targetEntityName, targetEntity.identifier().name())
                    .sort((entry1, entry2) => {
                        // use < and > instead of substraction to sort strings properly
                        if (entry1.values[sortField] > entry2.values[sortField]) return sortDir;
                        if (entry1.values[sortField] < entry2.values[sortField]) return -1 * sortDir;
                        return 0;
                    })
                    .filter(filterFunc);
                scope.addNew = () => scope.entries.push(Entry.createForFields(targetFields));
                scope.remove = entry => {
                    scope.entries = scope.entries.filter(e => e !== entry);
                };
                scope.$watch('entries', (newEntries, oldEntries) => {
                    if (newEntries === oldEntries) return;
                    scope.value = newEntries.map(e => e.transformToRest(targetFields));
                }, true);
            }
        },
        template: `

    <ng-form ng-repeat="entry in entries track by $index" class="subentry" name="subform_{{$index}}" ng-init="formName = 'subform_' + $index">

                 Remove

            <ma-field field="::field" value="entry.values[field.name()]" entry="entry" entity="::entity" form="formName" datastore="::datastore()"></ma-field>

    </ng-form>

             Add new {{ field().name() }}

`
    };
}

maEmbeddedListField.$inject = [];
<commit_msg>Fix non editable fields in embedded lists<commit_after>import Entry from 'admin-config/lib/Entry';

export default function maEmbeddedListField() {
    return {
        scope: {
            'field': '&',
            'value': '=',
            'datastore': '&'
        },
        restrict: 'E',
        link: {
            pre: function(scope) {
                const field = scope.field();
                const targetEntity = field.targetEntity();
                const targetEntityName = targetEntity.name();
                const targetFields = field.targetFields();
                const sortField = field.sortField();
                const sortDir = field.sortDir() === 'DESC' ? -1 : 1;
                var filterFunc;
                if (field.permanentFilters()) {
                    const filters = field.permanentFilters();
                    const filterKeys = Object.keys(filters);
                    filterFunc = (entry) => {
                        return filterKeys.reduce((isFiltered, key) => isFiltered && entry.values[key] === filters[key], true);
                    };
                } else {
                    filterFunc = () => true;
                }
                scope.fields = targetFields;
                scope.targetEntity = targetEntity;
                scope.entries = Entry
                    .createArrayFromRest(scope.value || [], targetFields, targetEntityName, targetEntity.identifier().name())
                    .sort((entry1, entry2) => {
                        // use < and > instead of substraction to sort strings properly
                        if (entry1.values[sortField] > entry2.values[sortField]) return sortDir;
                        if (entry1.values[sortField] < entry2.values[sortField]) return -1 * sortDir;
                        return 0;
                    })
                    .filter(filterFunc);
                scope.addNew = () => scope.entries.push(Entry.createForFields(targetFields));
                scope.remove = entry => {
                    scope.entries = scope.entries.filter(e => e !== entry);
                };
                scope.$watch('entries', (newEntries, oldEntries) => {
                    if (newEntries === oldEntries) return;
                    scope.value = newEntries.map(e => e.transformToRest(targetFields));
                }, true);
            }
        },
        template: `

    <ng-form ng-repeat="entry in entries track by $index" class="subentry" name="subform_{{$index}}" ng-init="formName = 'subform_' + $index">

                 Remove

            <ma-field field="::field" value="entry.values[field.name()]" entry="entry" entity="::targetEntity" form="formName" datastore="::datastore()"></ma-field>

    </ng-form>

             Add new {{ field().name() }}

`
    };
}

maEmbeddedListField.$inject = [];
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var path = require('path');
var hostname = require('os').hostname();
var Auth = require('sivart-GCE/Auth');
var gcloud = require('gcloud');
var WriteData = require('sivart-data/WriteBuildData');
var storage = gcloud.storage(Auth);

// Get info
var logDir = process.env.SIVART_BASE_LOG_DIR;
var repoName = process.env.TRAVIS_REPO_SLUG;
var branch = process.env.TRAVIS_BRANCH;
var buildId = process.env.SIVART_BUILD_ID;
var buildNumber = process.env.SIVART_BUILD_NUMBER;

var writeData = new WriteData(repoName);
var bucketname = writeData.getBucketName();

fs.writeFileSync(path.join(logDir, 'environment.json'), JSON.stringify(process.env));

// Save files
var basepath = path.join(branch, String(buildId), String(buildNumber));

// Store files
storage.createBucket(bucketname, function(err, bucket) {
  if (err) {
    bucket = storage.bucket(hostname);
  }
  var files = fs.readdirSync(logDir);
  files.forEach(function(file) {
    fs.createReadStream(path.join(logDir, file)).pipe(bucket.file(path.join(basepath, file)).createWriteStream());
  });
  fs.createReadStream('/tmp/user-script.sh').pipe(bucket.file(path.join(basepath, 'user-script.sh')).createWriteStream());
  fs.createReadStream('/tmp/user-script.log').pipe(bucket.file(path.join(basepath, 'user-script.log')).createWriteStream());
  fs.createReadStream('/var/log/startupscript.log').pipe(bucket.file(path.join(basepath, 'startupscript.log')).createWriteStream());
});
<commit_msg>fixup savefiles<commit_after>'use strict';

var fs = require('fs');
var path = require('path');
var WriteData = require('sivart-data/WriteBuildData');

// Get info
var logDir = process.env.SIVART_BASE_LOG_DIR;
var repoName = process.env.TRAVIS_REPO_SLUG;
var branch = process.env.TRAVIS_BRANCH;
var buildId = process.env.SIVART_BUILD_ID;
var buildNumber = process.env.SIVART_BUILD_NUMBER;
var writeData = new WriteData(repoName);

// Save environment (for internal debugging)
fs.writeFileSync(path.join(logDir, 'environment.json'), JSON.stringify(process.env));

// Save all files in logDir...
var files = fs.readdirSync(logDir).map(function(file) {
  return path.join(logDir, file);
});

// ..and save these files too
files.push(
  '/tmp/user-script.sh',
  '/tmp/user-script.log',
  '/var/log/startupscript.log'
);

// Save files to here within bucket
//  branch name / build id / build number
var basepath = path.join(branch, String(buildId), String(buildNumber));
writeData.writeFilesToBucket(basepath, files, function() {});
<|endoftext|>


<commit_before>import { createHash } from 'node:crypto'
import { createWriteStream, unlinkSync } from 'node:fs'
import { readFile, writeFile } from 'node:fs/promises'
import { platform } from 'node:os'
import { dirname, join, sep } from 'node:path'
import { log, progress } from '@serverless/utils/log.js'
import { Lambda } from 'aws-sdk'
import { execa } from 'execa'
import { ensureDir, pathExists } from 'fs-extra'
import jszip from 'jszip'
import pRetry from 'p-retry'
import DockerImage from './DockerImage.js'

const { stringify } = JSON
const { entries, hasOwn, keys } = Object

export default class DockerContainer {
  #containerId = null

  #dockerOptions = null

  #env = null

  #functionKey = null

  #handler = null

  #image = null

  #imageNameTag = null

  #lambda = null

  #layers = null

  #port = null

  #provider = null

  #runtime = null

  #servicePath = null

  constructor(
    env,
    functionKey,
    handler,
    runtime,
    layers,
    provider,
    servicePath,
    dockerOptions,
  ) {
    this.#dockerOptions = dockerOptions
    this.#env = env
    this.#functionKey = functionKey
    this.#handler = handler
    this.#imageNameTag = this.#baseImage(runtime)
    this.#image = new DockerImage(this.#imageNameTag)
    this.#layers = layers
    this.#provider = provider
    this.#runtime = runtime
    this.#servicePath = servicePath
  }

  #baseImage(runtime) {
    return `lambci/lambda:${runtime}`
  }

  async start(codeDir) {
    await this.#image.pull()

    log.debug('Run Docker container...')

    let permissions = 'ro'

    if (!this.#dockerOptions.readOnly) {
      permissions = 'rw'
    }
    // https://github.com/serverless/serverless/blob/v1.57.0/lib/plugins/aws/invokeLocal/index.js#L291-L293
    const dockerArgs = [
      '-v',
      `${codeDir}:/var/task:${permissions},delegated`,
      '-p',
      9001,
      '-e',
      'DOCKER_LAMBDA_STAY_OPEN=1', // API mode
      '-e',
      'DOCKER_LAMBDA_WATCH=1', // Watch mode
    ]

    if (this.#layers.length > 0) {
      log.verbose(`Found layers, checking provider type`)

      if (this.#provider.name.toLowerCase() !== 'aws') {
        log.warning(
          `Provider ${
            this.#provider.name
          } is Unsupported. Layers are only supported on aws.`,
        )
      } else {
        let layerDir = this.#dockerOptions.layersDir

        if (!layerDir) {
          layerDir = join(this.#servicePath, '.serverless-offline', 'layers')
        }

        layerDir = join(layerDir, this.#getLayersSha256())

        if (await pathExists(layerDir)) {
          log.verbose(
            `Layers already exist for this function. Skipping download.`,
          )
        } else {
          const layers = []

          log.verbose(`Storing layers at ${layerDir}`)

          // Only initialise if we have layers, we're using AWS, and they don't already exist
          this.#lambda = new Lambda({
            apiVersion: '2015-03-31',
            region: this.#provider.region,
          })

          log.verbose(`Getting layers`)

          for (const layerArn of this.#layers) {
            layers.push(this.#downloadLayer(layerArn, layerDir))
          }

          await Promise.all(layers)
        }

        if (
          this.#dockerOptions.hostServicePath &&
          layerDir.startsWith(this.#servicePath)
        ) {
          layerDir = layerDir.replace(
            this.#servicePath,
            this.#dockerOptions.hostServicePath,
          )
        }
        dockerArgs.push('-v', `${layerDir}:/opt:ro,delegated`)
      }
    }

    entries(this.#env).forEach(([key, value]) => {
      dockerArgs.push('-e', `${key}=${value}`)
    })

    if (platform() === 'linux') {
      // Add `host.docker.internal` DNS name to access host from inside the container
      // https://github.com/docker/for-linux/issues/264
      const gatewayIp = await this.#getBridgeGatewayIp()
      if (gatewayIp) {
        dockerArgs.push('--add-host', `host.docker.internal:${gatewayIp}`)
      }
    }

    if (this.#dockerOptions.network) {
      dockerArgs.push('--network', this.#dockerOptions.network)
    }

    const { stdout: containerId } = await execa('docker', [
      'create',
      ...dockerArgs,
      this.#imageNameTag,
      this.#handler,
    ])

    const dockerStart = execa('docker', ['start', '-a', containerId], {
      all: true,
    })

    await new Promise((resolve, reject) => {
      dockerStart.all.on('data', (data) => {
        const str = String(data)
        log.error(str)

        if (str.includes('Lambda API listening on port')) {
          resolve()
        }
      })

      dockerStart.on('error', (err) => {
        reject(err)
      })
    })

    // parse `docker port` output and get the container port
    let containerPort
    const { stdout: dockerPortOutput } = await execa('docker', [
      'port',
      containerId,
    ])
    // NOTE: `docker port` may output multiple lines.
    //
    // e.g.:
    // 9001/tcp -> 0.0.0.0:49153
    // 9001/tcp -> :::49153
    //
    // Parse each line until it finds the mapped port.
    for (const line of dockerPortOutput.split('\n')) {
      const result = line.match(/^9001\/tcp -> (.*):(\d+)$/)
      if (result && result.length > 2) {
        ;[, , containerPort] = result
        break
      }
    }
    if (!containerPort) {
      throw new Error('Failed to get container port')
    }

    this.#containerId = containerId
    this.#port = containerPort

    await pRetry(() => this.#ping(), {
      // default,
      factor: 2,
      // milliseconds
      minTimeout: 10,
      // default
      retries: 10,
    })
  }

  async #downloadLayer(layerArn, layerDir) {
    const layerName = layerArn.split(':layer:')[1]
    const layerZipFile = `${layerDir}/${layerName}.zip`
    const layerProgress = progress.get(`layer-${layerName}`)

    log.verbose(`[${layerName}] ARN: ${layerArn}`)

    const params = {
      Arn: layerArn,
    }

    log.verbose(`[${layerName}] Getting Info`)
    layerProgress.notice(`Retrieving "${layerName}": Getting info`)

    try {
      let layer = null

      try {
        layer = await this.#lambda.getLayerVersionByArn(params).promise()
      } catch (err) {
        log.warning(`[${layerName}] ${err.code}: ${err.message}`)

        return
      }

      if (
        hasOwn(layer, 'CompatibleRuntimes') &&
        !layer.CompatibleRuntimes.includes(this.#runtime)
      ) {
        log.warning(
          `[${layerName}] Layer is not compatible with ${
            this.#runtime
          } runtime`,
        )

        return
      }

      const layerUrl = layer.Content.Location
      // const layerSha = layer.Content.CodeSha256

      const layerSize = layer.Content.CodeSize

      await ensureDir(layerDir)

      log.verbose(
        `Retrieving "${layerName}": Downloading ${this.#formatBytes(
          layerSize,
        )}...`,
      )
      layerProgress.notice(
        `Retrieving "${layerName}": Downloading ${this.#formatBytes(
          layerSize,
        )}`,
      )

      const res = await fetch(layerUrl, {
        method: 'get',
      })

      if (!res.ok) {
        log.warning(
          `[${layerName}] Failed to fetch from ${layerUrl} with ${res.statusText}`,
        )

        return
      }

      const fileStream = createWriteStream(layerZipFile)

      await new Promise((resolve, reject) => {
        res.body.pipe(fileStream)
        res.body.on('error', (err) => {
          reject(err)
        })
        fileStream.on('finish', () => {
          resolve()
        })
      })

      log.verbose(`Retrieving "${layerName}": Unzipping to .layers directory`)
      layerProgress.notice(
        `Retrieving "${layerName}": Unzipping to .layers directory`,
      )

      const data = await readFile(layerZipFile)
      const zip = await jszip.loadAsync(data)
      await Promise.all(
        keys(zip.files).map(async (filename) => {
          const fileData = await zip.files[filename].async('nodebuffer')
          if (filename.endsWith(sep)) {
            return Promise.resolve()
          }
          await ensureDir(join(layerDir, dirname(filename)))
          return writeFile(join(layerDir, filename), fileData, {
            mode: zip.files[filename].unixPermissions,
          })
        }),
      )

      log.verbose(`[${layerName}] Removing zip file`)

      unlinkSync(layerZipFile)
    } finally {
      layerProgress.remove()
    }
  }

  async #getBridgeGatewayIp() {
    let gateway
    try {
      ;({ stdout: gateway } = await execa('docker', [
        'network',
        'inspect',
        'bridge',
        '--format',
        '{{(index .IPAM.Config 0).Gateway}}',
      ]))
    } catch (err) {
      log.error(err.stderr)

      throw err
    }
    return gateway.split('/')[0]
  }

  async #ping() {
    const url = `http://${this.#dockerOptions.host}:${
      this.#port
    }/2018-06-01/ping`
    const res = await fetch(url)

    if (!res.ok) {
      throw new Error(`Failed to fetch from ${url} with ${res.statusText}`)
    }

    return res.text()
  }

  async request(event) {
    const url = `http://${this.#dockerOptions.host}:${
      this.#port
    }/2015-03-31/functions/${this.#functionKey}/invocations`

    const res = await fetch(url, {
      body: stringify(event),
      headers: { 'Content-Type': 'application/json' },
      method: 'post',
    })

    if (!res.ok) {
      throw new Error(`Failed to fetch from ${url} with ${res.statusText}`)
    }

    return res.json()
  }

  async stop() {
    if (this.#containerId) {
      try {
        await execa('docker', ['stop', this.#containerId])
        await execa('docker', ['rm', this.#containerId])
      } catch (err) {
        log.error(err.stderr)

        throw err
      }
    }
  }

  #formatBytes(bytes, decimals = 2) {
    if (bytes === 0) return '0 Bytes'

    const k = 1024
    const dm = decimals < 0 ? 0 : decimals
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']

    const i = Math.floor(Math.log(bytes) / Math.log(k))

    return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`
  }

  #getLayersSha256() {
    return createHash('sha256').update(stringify(this.#layers)).digest('hex')
  }

  get isRunning() {
    return this.#containerId !== null && this.#port !== null
  }
}
<commit_msg>refactor: use Object.entries<commit_after>import { createHash } from 'node:crypto'
import { createWriteStream, unlinkSync } from 'node:fs'
import { readFile, writeFile } from 'node:fs/promises'
import { platform } from 'node:os'
import { dirname, join, sep } from 'node:path'
import { log, progress } from '@serverless/utils/log.js'
import { Lambda } from 'aws-sdk'
import { execa } from 'execa'
import { ensureDir, pathExists } from 'fs-extra'
import jszip from 'jszip'
import pRetry from 'p-retry'
import DockerImage from './DockerImage.js'

const { stringify } = JSON
const { entries, hasOwn } = Object

export default class DockerContainer {
  #containerId = null

  #dockerOptions = null

  #env = null

  #functionKey = null

  #handler = null

  #image = null

  #imageNameTag = null

  #lambda = null

  #layers = null

  #port = null

  #provider = null

  #runtime = null

  #servicePath = null

  constructor(
    env,
    functionKey,
    handler,
    runtime,
    layers,
    provider,
    servicePath,
    dockerOptions,
  ) {
    this.#dockerOptions = dockerOptions
    this.#env = env
    this.#functionKey = functionKey
    this.#handler = handler
    this.#imageNameTag = this.#baseImage(runtime)
    this.#image = new DockerImage(this.#imageNameTag)
    this.#layers = layers
    this.#provider = provider
    this.#runtime = runtime
    this.#servicePath = servicePath
  }

  #baseImage(runtime) {
    return `lambci/lambda:${runtime}`
  }

  async start(codeDir) {
    await this.#image.pull()

    log.debug('Run Docker container...')

    let permissions = 'ro'

    if (!this.#dockerOptions.readOnly) {
      permissions = 'rw'
    }
    // https://github.com/serverless/serverless/blob/v1.57.0/lib/plugins/aws/invokeLocal/index.js#L291-L293
    const dockerArgs = [
      '-v',
      `${codeDir}:/var/task:${permissions},delegated`,
      '-p',
      9001,
      '-e',
      'DOCKER_LAMBDA_STAY_OPEN=1', // API mode
      '-e',
      'DOCKER_LAMBDA_WATCH=1', // Watch mode
    ]

    if (this.#layers.length > 0) {
      log.verbose(`Found layers, checking provider type`)

      if (this.#provider.name.toLowerCase() !== 'aws') {
        log.warning(
          `Provider ${
            this.#provider.name
          } is Unsupported. Layers are only supported on aws.`,
        )
      } else {
        let layerDir = this.#dockerOptions.layersDir

        if (!layerDir) {
          layerDir = join(this.#servicePath, '.serverless-offline', 'layers')
        }

        layerDir = join(layerDir, this.#getLayersSha256())

        if (await pathExists(layerDir)) {
          log.verbose(
            `Layers already exist for this function. Skipping download.`,
          )
        } else {
          const layers = []

          log.verbose(`Storing layers at ${layerDir}`)

          // Only initialise if we have layers, we're using AWS, and they don't already exist
          this.#lambda = new Lambda({
            apiVersion: '2015-03-31',
            region: this.#provider.region,
          })

          log.verbose(`Getting layers`)

          for (const layerArn of this.#layers) {
            layers.push(this.#downloadLayer(layerArn, layerDir))
          }

          await Promise.all(layers)
        }

        if (
          this.#dockerOptions.hostServicePath &&
          layerDir.startsWith(this.#servicePath)
        ) {
          layerDir = layerDir.replace(
            this.#servicePath,
            this.#dockerOptions.hostServicePath,
          )
        }
        dockerArgs.push('-v', `${layerDir}:/opt:ro,delegated`)
      }
    }

    entries(this.#env).forEach(([key, value]) => {
      dockerArgs.push('-e', `${key}=${value}`)
    })

    if (platform() === 'linux') {
      // Add `host.docker.internal` DNS name to access host from inside the container
      // https://github.com/docker/for-linux/issues/264
      const gatewayIp = await this.#getBridgeGatewayIp()
      if (gatewayIp) {
        dockerArgs.push('--add-host', `host.docker.internal:${gatewayIp}`)
      }
    }

    if (this.#dockerOptions.network) {
      dockerArgs.push('--network', this.#dockerOptions.network)
    }

    const { stdout: containerId } = await execa('docker', [
      'create',
      ...dockerArgs,
      this.#imageNameTag,
      this.#handler,
    ])

    const dockerStart = execa('docker', ['start', '-a', containerId], {
      all: true,
    })

    await new Promise((resolve, reject) => {
      dockerStart.all.on('data', (data) => {
        const str = String(data)
        log.error(str)

        if (str.includes('Lambda API listening on port')) {
          resolve()
        }
      })

      dockerStart.on('error', (err) => {
        reject(err)
      })
    })

    // parse `docker port` output and get the container port
    let containerPort
    const { stdout: dockerPortOutput } = await execa('docker', [
      'port',
      containerId,
    ])
    // NOTE: `docker port` may output multiple lines.
    //
    // e.g.:
    // 9001/tcp -> 0.0.0.0:49153
    // 9001/tcp -> :::49153
    //
    // Parse each line until it finds the mapped port.
    for (const line of dockerPortOutput.split('\n')) {
      const result = line.match(/^9001\/tcp -> (.*):(\d+)$/)
      if (result && result.length > 2) {
        ;[, , containerPort] = result
        break
      }
    }
    if (!containerPort) {
      throw new Error('Failed to get container port')
    }

    this.#containerId = containerId
    this.#port = containerPort

    await pRetry(() => this.#ping(), {
      // default,
      factor: 2,
      // milliseconds
      minTimeout: 10,
      // default
      retries: 10,
    })
  }

  async #downloadLayer(layerArn, layerDir) {
    const layerName = layerArn.split(':layer:')[1]
    const layerZipFile = `${layerDir}/${layerName}.zip`
    const layerProgress = progress.get(`layer-${layerName}`)

    log.verbose(`[${layerName}] ARN: ${layerArn}`)

    const params = {
      Arn: layerArn,
    }

    log.verbose(`[${layerName}] Getting Info`)
    layerProgress.notice(`Retrieving "${layerName}": Getting info`)

    try {
      let layer = null

      try {
        layer = await this.#lambda.getLayerVersionByArn(params).promise()
      } catch (err) {
        log.warning(`[${layerName}] ${err.code}: ${err.message}`)

        return
      }

      if (
        hasOwn(layer, 'CompatibleRuntimes') &&
        !layer.CompatibleRuntimes.includes(this.#runtime)
      ) {
        log.warning(
          `[${layerName}] Layer is not compatible with ${
            this.#runtime
          } runtime`,
        )

        return
      }

      const layerUrl = layer.Content.Location
      // const layerSha = layer.Content.CodeSha256

      const layerSize = layer.Content.CodeSize

      await ensureDir(layerDir)

      log.verbose(
        `Retrieving "${layerName}": Downloading ${this.#formatBytes(
          layerSize,
        )}...`,
      )
      layerProgress.notice(
        `Retrieving "${layerName}": Downloading ${this.#formatBytes(
          layerSize,
        )}`,
      )

      const res = await fetch(layerUrl, {
        method: 'get',
      })

      if (!res.ok) {
        log.warning(
          `[${layerName}] Failed to fetch from ${layerUrl} with ${res.statusText}`,
        )

        return
      }

      const fileStream = createWriteStream(layerZipFile)

      await new Promise((resolve, reject) => {
        res.body.pipe(fileStream)
        res.body.on('error', (err) => {
          reject(err)
        })
        fileStream.on('finish', () => {
          resolve()
        })
      })

      log.verbose(`Retrieving "${layerName}": Unzipping to .layers directory`)
      layerProgress.notice(
        `Retrieving "${layerName}": Unzipping to .layers directory`,
      )

      const data = await readFile(layerZipFile)
      const zip = await jszip.loadAsync(data)

      await Promise.all(
        entries(zip.files).map(async ([filename, jsZipObj]) => {
          const fileData = await jsZipObj.async('nodebuffer')
          if (filename.endsWith(sep)) {
            return Promise.resolve()
          }
          await ensureDir(join(layerDir, dirname(filename)))
          return writeFile(join(layerDir, filename), fileData, {
            mode: zip.files[filename].unixPermissions,
          })
        }),
      )

      log.verbose(`[${layerName}] Removing zip file`)

      unlinkSync(layerZipFile)
    } finally {
      layerProgress.remove()
    }
  }

  async #getBridgeGatewayIp() {
    let gateway
    try {
      ;({ stdout: gateway } = await execa('docker', [
        'network',
        'inspect',
        'bridge',
        '--format',
        '{{(index .IPAM.Config 0).Gateway}}',
      ]))
    } catch (err) {
      log.error(err.stderr)

      throw err
    }
    return gateway.split('/')[0]
  }

  async #ping() {
    const url = `http://${this.#dockerOptions.host}:${
      this.#port
    }/2018-06-01/ping`
    const res = await fetch(url)

    if (!res.ok) {
      throw new Error(`Failed to fetch from ${url} with ${res.statusText}`)
    }

    return res.text()
  }

  async request(event) {
    const url = `http://${this.#dockerOptions.host}:${
      this.#port
    }/2015-03-31/functions/${this.#functionKey}/invocations`

    const res = await fetch(url, {
      body: stringify(event),
      headers: { 'Content-Type': 'application/json' },
      method: 'post',
    })

    if (!res.ok) {
      throw new Error(`Failed to fetch from ${url} with ${res.statusText}`)
    }

    return res.json()
  }

  async stop() {
    if (this.#containerId) {
      try {
        await execa('docker', ['stop', this.#containerId])
        await execa('docker', ['rm', this.#containerId])
      } catch (err) {
        log.error(err.stderr)

        throw err
      }
    }
  }

  #formatBytes(bytes, decimals = 2) {
    if (bytes === 0) return '0 Bytes'

    const k = 1024
    const dm = decimals < 0 ? 0 : decimals
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']

    const i = Math.floor(Math.log(bytes) / Math.log(k))

    return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`
  }

  #getLayersSha256() {
    return createHash('sha256').update(stringify(this.#layers)).digest('hex')
  }

  get isRunning() {
    return this.#containerId !== null && this.#port !== null
  }
}
<|endoftext|>