<commit_before>import m from 'mithril';
import _ from 'underscore';
import I18n from 'i18n-js';
import h from '../h';
import rewardVM from '../vms/reward-vm';
import projectVM from '../vms/project-vm';

const I18nScope = _.partial(h.i18nScope, 'projects.contributions');

const projectRewardCard = {
    controller(args) {
        const storeKey = 'selectedReward',
            reward = args.reward,
            vm = rewardVM,
            descriptionExtended = m.prop(0),
            selectedDestination = m.prop(''),
            toggleDescriptionExtended = (rewardId) => {
                if (descriptionExtended() === rewardId) {
                    descriptionExtended(0);
                } else {
                    descriptionExtended(rewardId);
                }

                return false;
            };

        const setInput = (el, isInitialized) => (!isInitialized ? el.focus() : false);

        const selectDestination = (destination) => {
            selectedDestination(destination);

            const shippingFee = vm.shippingFeeForCurrentReward(selectedDestination)
                ? Number(vm.shippingFeeForCurrentReward(selectedDestination).value)
                : 0;
            const rewardMinValue = Number(vm.selectedReward().minimum_value);
            vm.applyMask(`${h.formatNumber(shippingFee + rewardMinValue, 2, 3)}`);
        };

        // @TODO: move submit, fee & value logic to VM
        const submitContribution = () => {
            const valueFloat = h.monetaryToFloat(vm.contributionValue);
            const shippingFee = rewardVM.hasShippingOptions(vm.selectedReward()) ? vm.shippingFeeForCurrentReward(selectedDestination) : { value: 0 };

            if (!selectedDestination() && rewardVM.hasShippingOptions(vm.selectedReward())) {
                vm.error('Por favor, selecione uma opção de frete válida.');
            } else if (valueFloat < vm.selectedReward().minimum_value + shippingFee.value) {
                vm.error(`O valor de apoio para essa recompensa deve ser de no mínimo R$${vm.selectedReward().minimum_value} ${projectVM.isSubscription(projectVM.currentProject()) ? '' : `+ frete R$${h.formatNumber(shippingFee.value, 2, 3)}`} `);
            } else {
                vm.error('');
                const valueUrl = window.encodeURIComponent(String(valueFloat).replace('.', ','));

                if (projectVM.isSubscription(projectVM.currentProject())) {
                    vm.contributionValue(valueFloat);
                    m.route(`/projects/${projectVM.currentProject().project_id}/subscriptions/checkout`, {contribution_value: valueFloat, reward_id: vm.selectedReward().id});

                    return false;
                }

                h.navigateTo(`/projects/${projectVM.currentProject().project_id}/contributions/fallback_create?contribution%5Breward_id%5D=${vm.selectedReward().id}&contribution%5Bvalue%5D=${valueUrl}&contribution%5Bshipping_fee_id%5D=${shippingFee.id}`);
            }

            return false;
        };
        const isRewardOpened = () => vm.selectedReward().id === reward.id;
        const isRewardDescriptionExtended = () => descriptionExtended() === reward.id;
        const isLongDescription = () => reward.description.length > 110;
        if (h.getStoredObject(storeKey)) {
            const {
                value
            } = h.getStoredObject(storeKey);

            h.removeStoredObject(storeKey);
            vm.selectedReward(reward);
            vm.contributionValue(h.applyMonetaryMask(`${value},00`));
            submitContribution();
        }

        vm.getStates();

        return {
            setInput,
            reward,
            submitContribution,
            toggleDescriptionExtended,
            isRewardOpened,
            isLongDescription,
            isRewardDescriptionExtended,
            selectDestination,
            selectedDestination,
            error: vm.error,
            applyMask: vm.applyMask,
            selectReward: vm.selectReward,
            locationOptions: vm.locationOptions,
            contributionValue: vm.contributionValue
        };
    },
    view(ctrl, args) {
        // FIXME: MISSING ADJUSTS
        // - add draft admin modifications
        const reward = ctrl.reward,
            project = args.project,
            isSub = projectVM.isSubscription(project);
        return m(`div[class="${h.rewardSouldOut(reward) ? 'card-gone' : `card-reward ${project.open_for_contributions ? 'clickable' : ''}`} card card-secondary u-marginbottom-10"]`, {
            onclick: h.analytics.event({
                cat: 'contribution_create',
                act: 'contribution_reward_click',
                lbl: reward.minimum_value,
                project,
                extraData: {
                    reward_id: reward.id,
                    reward_value: reward.minimum_value
                }
            }, ctrl.selectReward(reward)),
            config: ctrl.isRewardOpened(reward) ? h.scrollTo() : Function.prototype
        }, [
            reward.minimum_value >= 100 && !isSub ? m('.tag-circle-installment', [
                m('.fontsize-smallest.fontweight-semibold.lineheight-tightest', '3x'),
                m('.fontsize-mini.lineheight-tightest', 's/ juros')
            ]) : '',
            m('.u-marginbottom-20', [
                m('.fontsize-base.fontweight-semibold', `Para R$ ${h.formatNumber(reward.minimum_value)} ou mais${isSub ? ' por mês' : ''}`)
            ]),
            m('.fontsize-smaller.fontweight-semibold',
                    reward.title
                ),

            m(`.fontsize-smaller.reward-description${h.rewardSouldOut(reward) ? '' : '.fontcolor-secondary'}`, {
                class: ctrl.isLongDescription()
                         ? ctrl.isRewardOpened()
                            ? `opened ${ctrl.isRewardDescriptionExtended() ? 'extended' : ''}`
                            : ''
                         : 'opened extended'
            }, m.trust(h.simpleFormat(h.strip(reward.description)))),
            ctrl.isLongDescription() && ctrl.isRewardOpened() ? m('a[href="javascript:void(0);"].alt-link.fontsize-smallest.gray.link-more.u-marginbottom-20', {
                onclick: () => ctrl.toggleDescriptionExtended(reward.id)
            }, [
                ctrl.isRewardDescriptionExtended() ? 'menos ' : 'mais ',
                m('span.fa.fa-angle-down', {
                    class: ctrl.isRewardDescriptionExtended() ? 'reversed' : ''
                })
            ]) : '',
            isSub ? null : m('.u-marginbottom-20.w-row', [
                m('.w-col.w-col-6', !_.isEmpty(reward.deliver_at) ? [
                    m('.fontcolor-secondary.fontsize-smallest',
                        m('span', 'Entrega prevista:')
                    ),
                    m('.fontsize-smallest',
                        h.momentify(reward.deliver_at, 'MMM/YYYY')
                    )
                ] : ''),
                m('.w-col.w-col-6', rewardVM.hasShippingOptions(reward) || reward.shipping_options === 'presential' ? [
                    m('.fontcolor-secondary.fontsize-smallest',
                        m('span',
                            'Envio:'
                        )
                    ),
                    m('.fontsize-smallest',
                        I18n.t(`shipping_options.${reward.shipping_options}`, I18nScope())
                    )
                ] : '')
            ]),
            reward.maximum_contributions > 0 ? [
                (h.rewardSouldOut(reward) ? m('.u-margintop-10', [
                    m('span.badge.badge-gone.fontsize-smaller', 'Esgotada')
                ]) : m('.u-margintop-10', [
                    m('span.badge.badge-attention.fontsize-smaller', [
                        m('span.fontweight-bold', 'Limitada'),
                        project.open_for_contributions ? ` (${h.rewardRemaning(reward)} de ${reward.maximum_contributions} disponíveis)` : ''
                    ])
                ]))
            ] : '',
            m('.fontcolor-secondary.fontsize-smallest.fontweight-semibold',
                h.pluralize.apply(
                    null,
                    isSub ? [reward.paid_count, ' assinante', ' assinantes'] : [reward.paid_count, ' apoio', ' apoios'])
            ),
            reward.waiting_payment_count > 0 ? m('.maximum_contributions.in_time_to_confirm.clearfix', [
                m('.pending.fontsize-smallest.fontcolor-secondary', h.pluralize(reward.waiting_payment_count, ' apoio em prazo de confirmação', ' apoios em prazo de confirmação.'))
            ]) : '',
            project.open_for_contributions && !h.rewardSouldOut(reward) ? [
                ctrl.isRewardOpened() ? m('.w-form', [
                    m('form.u-margintop-30', {
                        onsubmit: ctrl.submitContribution
                    }, [
                        m('.divider.u-marginbottom-20'),
                        rewardVM.hasShippingOptions(reward) ? m('div', [
                            m('.fontcolor-secondary.u-marginbottom-10',
                                'Local de entrega'
                            ),
                            m('select.positive.text-field.w-select', {
                                onchange: m.withAttr('value', ctrl.selectDestination),
                                value: ctrl.selectedDestination()
                            },
                                _.map(
                                    ctrl.locationOptions(reward, ctrl.selectedDestination),
                                    option => m('option',
                                        { selected: option.value === ctrl.selectedDestination(), value: option.value },
                                        [
                                            `${option.name} `,
                                            option.value != '' ? `+R$${h.formatNumber(option.fee, 2, 3)}` : null
                                        ]
                                    )
                                )
                            )
                        ]) : '',
                        m('.fontcolor-secondary.u-marginbottom-10',
                            `Valor do apoio${isSub ? ' mensal' : ''}`
                        ),
                        m('.w-row.u-marginbottom-20', [
                            m('.w-col.w-col-3.w-col-small-3.w-col-tiny-3',
                                m('.back-reward-input-reward.placeholder', 'R$')
                            ),
                            m('.w-col.w-col-9.w-col-small-9.w-col-tiny-9',
                                m('input.w-input.back-reward-input-reward[type="tel"]', {
                                    config: ctrl.setInput,
                                    onkeyup: m.withAttr('value', ctrl.applyMask),
                                    value: ctrl.contributionValue()
                                })
                            )
                        ]),
                        m('input.w-button.btn.btn-medium[type="submit"][value="Continuar >"]'),
                        ctrl.error().length > 0 ? m('.text-error', [
                            m('br'),
                            m('span.fa.fa-exclamation-triangle'),
                            ` ${ctrl.error()}`
                        ]) : ''
                    ])
                ]) : ''
            ] : ''
        ]);
    }
};

export default projectRewardCard;
<commit_msg>Remove 'Para' from reward value in reward box<commit_after>import m from 'mithril';
import _ from 'underscore';
import I18n from 'i18n-js';
import h from '../h';
import rewardVM from '../vms/reward-vm';
import projectVM from '../vms/project-vm';

const I18nScope = _.partial(h.i18nScope, 'projects.contributions');

const projectRewardCard = {
    controller(args) {
        const storeKey = 'selectedReward',
            reward = args.reward,
            vm = rewardVM,
            descriptionExtended = m.prop(0),
            selectedDestination = m.prop(''),
            toggleDescriptionExtended = (rewardId) => {
                if (descriptionExtended() === rewardId) {
                    descriptionExtended(0);
                } else {
                    descriptionExtended(rewardId);
                }

                return false;
            };

        const setInput = (el, isInitialized) => (!isInitialized ? el.focus() : false);

        const selectDestination = (destination) => {
            selectedDestination(destination);

            const shippingFee = vm.shippingFeeForCurrentReward(selectedDestination)
                ? Number(vm.shippingFeeForCurrentReward(selectedDestination).value)
                : 0;
            const rewardMinValue = Number(vm.selectedReward().minimum_value);
            vm.applyMask(`${h.formatNumber(shippingFee + rewardMinValue, 2, 3)}`);
        };

        // @TODO: move submit, fee & value logic to VM
        const submitContribution = () => {
            const valueFloat = h.monetaryToFloat(vm.contributionValue);
            const shippingFee = rewardVM.hasShippingOptions(vm.selectedReward()) ? vm.shippingFeeForCurrentReward(selectedDestination) : { value: 0 };

            if (!selectedDestination() && rewardVM.hasShippingOptions(vm.selectedReward())) {
                vm.error('Por favor, selecione uma opção de frete válida.');
            } else if (valueFloat < vm.selectedReward().minimum_value + shippingFee.value) {
                vm.error(`O valor de apoio para essa recompensa deve ser de no mínimo R$${vm.selectedReward().minimum_value} ${projectVM.isSubscription(projectVM.currentProject()) ? '' : `+ frete R$${h.formatNumber(shippingFee.value, 2, 3)}`} `);
            } else {
                vm.error('');
                const valueUrl = window.encodeURIComponent(String(valueFloat).replace('.', ','));

                if (projectVM.isSubscription(projectVM.currentProject())) {
                    vm.contributionValue(valueFloat);
                    m.route(`/projects/${projectVM.currentProject().project_id}/subscriptions/checkout`, {contribution_value: valueFloat, reward_id: vm.selectedReward().id});

                    return false;
                }

                h.navigateTo(`/projects/${projectVM.currentProject().project_id}/contributions/fallback_create?contribution%5Breward_id%5D=${vm.selectedReward().id}&contribution%5Bvalue%5D=${valueUrl}&contribution%5Bshipping_fee_id%5D=${shippingFee.id}`);
            }

            return false;
        };
        const isRewardOpened = () => vm.selectedReward().id === reward.id;
        const isRewardDescriptionExtended = () => descriptionExtended() === reward.id;
        const isLongDescription = () => reward.description.length > 110;
        if (h.getStoredObject(storeKey)) {
            const {
                value
            } = h.getStoredObject(storeKey);

            h.removeStoredObject(storeKey);
            vm.selectedReward(reward);
            vm.contributionValue(h.applyMonetaryMask(`${value},00`));
            submitContribution();
        }

        vm.getStates();

        return {
            setInput,
            reward,
            submitContribution,
            toggleDescriptionExtended,
            isRewardOpened,
            isLongDescription,
            isRewardDescriptionExtended,
            selectDestination,
            selectedDestination,
            error: vm.error,
            applyMask: vm.applyMask,
            selectReward: vm.selectReward,
            locationOptions: vm.locationOptions,
            contributionValue: vm.contributionValue
        };
    },
    view(ctrl, args) {
        // FIXME: MISSING ADJUSTS
        // - add draft admin modifications
        const reward = ctrl.reward,
            project = args.project,
            isSub = projectVM.isSubscription(project);
        return m(`div[class="${h.rewardSouldOut(reward) ? 'card-gone' : `card-reward ${project.open_for_contributions ? 'clickable' : ''}`} card card-secondary u-marginbottom-10"]`, {
            onclick: h.analytics.event({
                cat: 'contribution_create',
                act: 'contribution_reward_click',
                lbl: reward.minimum_value,
                project,
                extraData: {
                    reward_id: reward.id,
                    reward_value: reward.minimum_value
                }
            }, ctrl.selectReward(reward)),
            config: ctrl.isRewardOpened(reward) ? h.scrollTo() : Function.prototype
        }, [
            reward.minimum_value >= 100 && !isSub ? m('.tag-circle-installment', [
                m('.fontsize-smallest.fontweight-semibold.lineheight-tightest', '3x'),
                m('.fontsize-mini.lineheight-tightest', 's/ juros')
            ]) : '',
            m('.u-marginbottom-20', [
                m('.fontsize-base.fontweight-semibold', `R$ ${h.formatNumber(reward.minimum_value)} ou mais${isSub ? ' por mês' : ''}`)
            ]),
            m('.fontsize-smaller.fontweight-semibold',
                    reward.title
                ),

            m(`.fontsize-smaller.reward-description${h.rewardSouldOut(reward) ? '' : '.fontcolor-secondary'}`, {
                class: ctrl.isLongDescription()
                         ? ctrl.isRewardOpened()
                            ? `opened ${ctrl.isRewardDescriptionExtended() ? 'extended' : ''}`
                            : ''
                         : 'opened extended'
            }, m.trust(h.simpleFormat(h.strip(reward.description)))),
            ctrl.isLongDescription() && ctrl.isRewardOpened() ? m('a[href="javascript:void(0);"].alt-link.fontsize-smallest.gray.link-more.u-marginbottom-20', {
                onclick: () => ctrl.toggleDescriptionExtended(reward.id)
            }, [
                ctrl.isRewardDescriptionExtended() ? 'menos ' : 'mais ',
                m('span.fa.fa-angle-down', {
                    class: ctrl.isRewardDescriptionExtended() ? 'reversed' : ''
                })
            ]) : '',
            isSub ? null : m('.u-marginbottom-20.w-row', [
                m('.w-col.w-col-6', !_.isEmpty(reward.deliver_at) ? [
                    m('.fontcolor-secondary.fontsize-smallest',
                        m('span', 'Entrega prevista:')
                    ),
                    m('.fontsize-smallest',
                        h.momentify(reward.deliver_at, 'MMM/YYYY')
                    )
                ] : ''),
                m('.w-col.w-col-6', rewardVM.hasShippingOptions(reward) || reward.shipping_options === 'presential' ? [
                    m('.fontcolor-secondary.fontsize-smallest',
                        m('span',
                            'Envio:'
                        )
                    ),
                    m('.fontsize-smallest',
                        I18n.t(`shipping_options.${reward.shipping_options}`, I18nScope())
                    )
                ] : '')
            ]),
            reward.maximum_contributions > 0 ? [
                (h.rewardSouldOut(reward) ? m('.u-margintop-10', [
                    m('span.badge.badge-gone.fontsize-smaller', 'Esgotada')
                ]) : m('.u-margintop-10', [
                    m('span.badge.badge-attention.fontsize-smaller', [
                        m('span.fontweight-bold', 'Limitada'),
                        project.open_for_contributions ? ` (${h.rewardRemaning(reward)} de ${reward.maximum_contributions} disponíveis)` : ''
                    ])
                ]))
            ] : '',
            m('.fontcolor-secondary.fontsize-smallest.fontweight-semibold',
                h.pluralize.apply(
                    null,
                    isSub ? [reward.paid_count, ' assinante', ' assinantes'] : [reward.paid_count, ' apoio', ' apoios'])
            ),
            reward.waiting_payment_count > 0 ? m('.maximum_contributions.in_time_to_confirm.clearfix', [
                m('.pending.fontsize-smallest.fontcolor-secondary', h.pluralize(reward.waiting_payment_count, ' apoio em prazo de confirmação', ' apoios em prazo de confirmação.'))
            ]) : '',
            project.open_for_contributions && !h.rewardSouldOut(reward) ? [
                ctrl.isRewardOpened() ? m('.w-form', [
                    m('form.u-margintop-30', {
                        onsubmit: ctrl.submitContribution
                    }, [
                        m('.divider.u-marginbottom-20'),
                        rewardVM.hasShippingOptions(reward) ? m('div', [
                            m('.fontcolor-secondary.u-marginbottom-10',
                                'Local de entrega'
                            ),
                            m('select.positive.text-field.w-select', {
                                onchange: m.withAttr('value', ctrl.selectDestination),
                                value: ctrl.selectedDestination()
                            },
                                _.map(
                                    ctrl.locationOptions(reward, ctrl.selectedDestination),
                                    option => m('option',
                                        { selected: option.value === ctrl.selectedDestination(), value: option.value },
                                        [
                                            `${option.name} `,
                                            option.value != '' ? `+R$${h.formatNumber(option.fee, 2, 3)}` : null
                                        ]
                                    )
                                )
                            )
                        ]) : '',
                        m('.fontcolor-secondary.u-marginbottom-10',
                            `Valor do apoio${isSub ? ' mensal' : ''}`
                        ),
                        m('.w-row.u-marginbottom-20', [
                            m('.w-col.w-col-3.w-col-small-3.w-col-tiny-3',
                                m('.back-reward-input-reward.placeholder', 'R$')
                            ),
                            m('.w-col.w-col-9.w-col-small-9.w-col-tiny-9',
                                m('input.w-input.back-reward-input-reward[type="tel"]', {
                                    config: ctrl.setInput,
                                    onkeyup: m.withAttr('value', ctrl.applyMask),
                                    value: ctrl.contributionValue()
                                })
                            )
                        ]),
                        m('input.w-button.btn.btn-medium[type="submit"][value="Continuar >"]'),
                        ctrl.error().length > 0 ? m('.text-error', [
                            m('br'),
                            m('span.fa.fa-exclamation-triangle'),
                            ` ${ctrl.error()}`
                        ]) : ''
                    ])
                ]) : ''
            ] : ''
        ]);
    }
};

export default projectRewardCard;
<|endoftext|>


<commit_before>'use strict'

const events = require('events')
const path = require('path')
const Module = require('module')

// We modified the original process.argv to let node.js load the
// atom-renderer.js, we need to restore it here.
process.argv.splice(1, 1)

// Clear search paths.
require('../common/reset-search-paths')

// Import common settings.
require('../common/init')

var globalPaths = Module.globalPaths

// Expose public APIs.
globalPaths.push(path.join(__dirname, 'api', 'exports'))

// The global variable will be used by ipc for event dispatching
var v8Util = process.atomBinding('v8_util')

v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter())

// Use electron module after everything is ready.
const electron = require('electron')

// Call webFrame method.
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => {
  electron.webFrame[method](...args)
})

electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  const result = electron.webFrame[method](...args)
  event.sender.send(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, result)
})

electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  const responseCallback = function (result) {
    Promise.resolve(result)
      .then((resolvedResult) => {
        event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, null, resolvedResult)
      })
      .catch((resolvedError) => {
        event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedError)
      })
  }
  args.push(responseCallback)
  electron.webFrame[method](...args)
})

// Process command line arguments.
let nodeIntegration = 'false'
let preloadScript = null
let isBackgroundPage = false
for (let arg of process.argv) {
  if (arg.indexOf('--guest-instance-id=') === 0) {
    // This is a guest web view.
    process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1))
  } else if (arg.indexOf('--opener-id=') === 0) {
    // This is a guest BrowserWindow.
    process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1))
  } else if (arg.indexOf('--node-integration=') === 0) {
    nodeIntegration = arg.substr(arg.indexOf('=') + 1)
  } else if (arg.indexOf('--preload=') === 0) {
    preloadScript = arg.substr(arg.indexOf('=') + 1)
  } else if (arg === '--background-page') {
    isBackgroundPage = true
  }
}

if (window.location.protocol === 'chrome-devtools:') {
  // Override some inspector APIs.
  require('./inspector')
  nodeIntegration = 'true'
} else if (window.location.protocol === 'chrome-extension:') {
  // Add implementations of chrome API.
  require('./chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)
  nodeIntegration = 'false'
} else {
  // Override default web functions.
  require('./override')

  // Inject content scripts.
  require('./content-scripts-injector')

  // Load webview tag implementation.
  if (nodeIntegration === 'true' && process.guestInstanceId == null) {
    require('./web-view/web-view')
    require('./web-view/web-view-attributes')
  }
}

if (nodeIntegration === 'true') {
  // Export node bindings to global.
  global.require = require
  global.module = module

  // Set the __filename to the path of html file if it is file: protocol.
  if (window.location.protocol === 'file:') {
    var pathname = process.platform === 'win32' && window.location.pathname[0] === '/' ? window.location.pathname.substr(1) : window.location.pathname
    global.__filename = path.normalize(decodeURIComponent(pathname))
    global.__dirname = path.dirname(global.__filename)

    // Set module's filename so relative require can work as expected.
    module.filename = global.__filename

    // Also search for module under the html file.
    module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
  } else {
    global.__filename = __filename
    global.__dirname = __dirname
  }

  // Redirect window.onerror to uncaughtException.
  window.onerror = function (message, filename, lineno, colno, error) {
    if (global.process.listeners('uncaughtException').length > 0) {
      global.process.emit('uncaughtException', error)
      return true
    } else {
      return false
    }
  }
} else {
  // Delete Node's symbols after the Environment has been loaded.
  process.once('loaded', function () {
    delete global.process
    delete global.setImmediate
    delete global.clearImmediate
    delete global.global
  })
}

// Load the script specfied by the "preload" attribute.
if (preloadScript) {
  try {
    require(preloadScript)
  } catch (error) {
    console.error('Unable to load preload script: ' + preloadScript)
    console.error(error.stack || error.message)
  }
}
<commit_msg>Remove Buffer global on load<commit_after>'use strict'

const events = require('events')
const path = require('path')
const Module = require('module')

// We modified the original process.argv to let node.js load the
// atom-renderer.js, we need to restore it here.
process.argv.splice(1, 1)

// Clear search paths.
require('../common/reset-search-paths')

// Import common settings.
require('../common/init')

var globalPaths = Module.globalPaths

// Expose public APIs.
globalPaths.push(path.join(__dirname, 'api', 'exports'))

// The global variable will be used by ipc for event dispatching
var v8Util = process.atomBinding('v8_util')

v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter())

// Use electron module after everything is ready.
const electron = require('electron')

// Call webFrame method.
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => {
  electron.webFrame[method](...args)
})

electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  const result = electron.webFrame[method](...args)
  event.sender.send(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, result)
})

electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  const responseCallback = function (result) {
    Promise.resolve(result)
      .then((resolvedResult) => {
        event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, null, resolvedResult)
      })
      .catch((resolvedError) => {
        event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedError)
      })
  }
  args.push(responseCallback)
  electron.webFrame[method](...args)
})

// Process command line arguments.
let nodeIntegration = 'false'
let preloadScript = null
let isBackgroundPage = false
for (let arg of process.argv) {
  if (arg.indexOf('--guest-instance-id=') === 0) {
    // This is a guest web view.
    process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1))
  } else if (arg.indexOf('--opener-id=') === 0) {
    // This is a guest BrowserWindow.
    process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1))
  } else if (arg.indexOf('--node-integration=') === 0) {
    nodeIntegration = arg.substr(arg.indexOf('=') + 1)
  } else if (arg.indexOf('--preload=') === 0) {
    preloadScript = arg.substr(arg.indexOf('=') + 1)
  } else if (arg === '--background-page') {
    isBackgroundPage = true
  }
}

if (window.location.protocol === 'chrome-devtools:') {
  // Override some inspector APIs.
  require('./inspector')
  nodeIntegration = 'true'
} else if (window.location.protocol === 'chrome-extension:') {
  // Add implementations of chrome API.
  require('./chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)
  nodeIntegration = 'false'
} else {
  // Override default web functions.
  require('./override')

  // Inject content scripts.
  require('./content-scripts-injector')

  // Load webview tag implementation.
  if (nodeIntegration === 'true' && process.guestInstanceId == null) {
    require('./web-view/web-view')
    require('./web-view/web-view-attributes')
  }
}

if (nodeIntegration === 'true') {
  // Export node bindings to global.
  global.require = require
  global.module = module

  // Set the __filename to the path of html file if it is file: protocol.
  if (window.location.protocol === 'file:') {
    var pathname = process.platform === 'win32' && window.location.pathname[0] === '/' ? window.location.pathname.substr(1) : window.location.pathname
    global.__filename = path.normalize(decodeURIComponent(pathname))
    global.__dirname = path.dirname(global.__filename)

    // Set module's filename so relative require can work as expected.
    module.filename = global.__filename

    // Also search for module under the html file.
    module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
  } else {
    global.__filename = __filename
    global.__dirname = __dirname
  }

  // Redirect window.onerror to uncaughtException.
  window.onerror = function (message, filename, lineno, colno, error) {
    if (global.process.listeners('uncaughtException').length > 0) {
      global.process.emit('uncaughtException', error)
      return true
    } else {
      return false
    }
  }
} else {
  // Delete Node's symbols after the Environment has been loaded.
  process.once('loaded', function () {
    delete global.process
    delete global.Buffer
    delete global.setImmediate
    delete global.clearImmediate
    delete global.global
  })
}

// Load the script specfied by the "preload" attribute.
if (preloadScript) {
  try {
    require(preloadScript)
  } catch (error) {
    console.error('Unable to load preload script: ' + preloadScript)
    console.error(error.stack || error.message)
  }
}
<|endoftext|>


<commit_before>(function(){
  'use strict';

  var utils = require('../e2e/_utils'),
    CONSTANTS = require('../protractor.constants.js'),
    modelsRecipe = require('./_modelsRecipe');

  describe('Specialist Multiple Cases', function() {

    function logout() {
      browser.findElement(by.cssContainingText('a[target="_self"]', 'Sign out')).click();
      protractor.getInstance().manage().deleteAllCookies();
    }

    var case_ref1,
        case_ref2,
        latest_op_case_ref,
        latest_sp_case_ref;

    describe('As Operator', function () {

      beforeEach(utils.setUp);

      it('should create cases as operator and assign then to a provider', function() {
        browser.get(CONSTANTS.callcentreBaseUrl);
        element(by.css('.CaseList tbody td a:first-child')).getText().then(function(case_ref) {
          latest_op_case_ref = case_ref;
        });

        modelsRecipe.Case.createAndAssign(1).then(function(reference) {
          case_ref1 = reference;
        });

        modelsRecipe.Case.createAndAssign(1).then(function(reference) {
          case_ref2 = reference;
        });

      });

      it('should logout', function () {
        this.after(function () {
          logout();
        });
      });
    });

    describe('As Provider', function () {
      beforeEach(utils.setUpAsProvider);

      function fillInSplitForm(splitForm, categoryVal) {
        var categorySelect = splitForm.element(by.css('[name=category]')),
            matterType1Select = splitForm.element(by.css('[name=matter_type1]')),
            matterType2Select = splitForm.element(by.css('[name=matter_type2]'));

        categorySelect.element(by.css('option[value="'+categoryVal+'"]')).click();
        matterType1Select.element(by.css('option:last-child')).click();
        matterType2Select.element(by.css('option:last-child')).click();
      }

      it('shouldnt\'t be able to split if the validation fails', function() {
        browser.get('provider/'+case_ref1+'/');

        var splitButton = element(by.cssContainingText('.Button.Button--secondary', 'Split')),
            splitForm = element(by.css('.modal-content [name=split_case_frm]')),
            assignToProviderButton = splitForm.element(by.cssContainingText('button', 'Assign to'));

        expect(splitButton.isDisplayed()).toBe(true);
        splitButton.click();
        expect(splitForm.isDisplayed()).toBe(true);

        // submit without filling in any data => validation error
        assignToProviderButton.click();
        expect(splitForm.isDisplayed()).toBe(true);

        // choose same category of law and submit => validation error
        fillInSplitForm(splitForm, 'family');
        assignToProviderButton.click();
        expect(splitForm.isDisplayed()).toBe(true);

        // choose a category of law that the provider can't deal with and assign to same provider => validation error
        fillInSplitForm(splitForm, 'benefits');
        assignToProviderButton.click();
        expect(splitForm.isDisplayed()).toBe(true);
      });

      it('should be able to split a case and assign it to same provider if they can deal with that category of law', function() {
        var splitForm = element(by.css('.modal-content [name=split_case_frm]')),
            assignToProviderButton = splitForm.element(by.cssContainingText('button', 'Assign to'));

        // choose housing category
        fillInSplitForm(splitForm, 'housing');
        assignToProviderButton.click();

        // verify modal disappeared
        expect(splitForm.isPresent()).toBe(false);

        // verify outcome code REF-INT created
        expect(element(by.css('.CaseHistory-label:first-child')).getText()).toBe('REF-INT');

        // go to list and verify that new case created
        // browser.get(CONSTANTS.providerBaseUrl);
        // element(by.css('.CaseList tbody td a:first-child')).getText().then(function(new_case_ref1) {
        //   expect(new_case_ref1).not.toBe(case_ref1);
        //   latest_sp_case_ref = new_case_ref1;

        //   // go to that case and verify that outcome code 'created by specialist' exists
        //   browser.get('provider/'+new_case_ref1+'/');
        //   expect(element(by.cssContainingText('.CaseHistory-logItemNotes', 'Case created by Specialist')).isPresent()).toBe(true);
        // });
      });

      it('should be able to split a case and assign it back to an operator', function() {
        browser.get('provider/'+case_ref2+'/');

        var splitButton = element(by.cssContainingText('.Button.Button--secondary', 'Split')),
            splitForm = element(by.css('.modal-content [name=split_case_frm]')),
            assignToOperatorButton = splitForm.element(by.cssContainingText('button', 'Send to operator'));

        expect(splitButton.isDisplayed()).toBe(true);
        splitButton.click();
        expect(splitForm.isDisplayed()).toBe(true);

        // choose benefits category
        fillInSplitForm(splitForm, 'benefits');
        assignToOperatorButton.click();

        // verify modal disappeared
        expect(splitForm.isPresent()).toBe(false);

        // verify outcome code REF-EXT created
        expect(element(by.css('.CaseHistory-label:first-child')).getText()).toBe('REF-EXT');

        // go to list and verify that no new case has been created
        // browser.get(CONSTANTS.providerBaseUrl);
        // element(by.css('.CaseList tbody td a:first-child')).getText().then(function(new_case_ref2) {
        //   expect(new_case_ref2).toBe(latest_sp_case_ref);
        // });

        // // go back to operator case list and verify that new case created
        // logout();
        // utils.setUp();
        // browser.get(CONSTANTS.callcentreBaseUrl);
        // element(by.css('.CaseList tbody tr:not(.is-rejected) a:first-child')).getText().then(function(case_ref) {
        //   expect(case_ref).not.toBe(latest_op_case_ref);
        // });

      });
    });

    it('should logout', function () {
      this.after(function () {
        logout();
      });
    });
  });
})();
<commit_msg>cleanup<commit_after>(function(){
  'use strict';

  var utils = require('../e2e/_utils'),
    CONSTANTS = require('../protractor.constants.js'),
    modelsRecipe = require('./_modelsRecipe');

  describe('Specialist Multiple Cases', function() {

    function logout() {
      browser.findElement(by.cssContainingText('a[target="_self"]', 'Sign out')).click();
      protractor.getInstance().manage().deleteAllCookies();
    }

    var case_ref1,
        case_ref2;

    describe('As Operator', function () {

      beforeEach(utils.setUp);

      it('should create cases as operator and assign then to a provider', function() {
        modelsRecipe.Case.createAndAssign(1).then(function(reference) {
          case_ref1 = reference;
        });

        modelsRecipe.Case.createAndAssign(1).then(function(reference) {
          case_ref2 = reference;
        });

      });

      it('should logout', function () {
        this.after(function () {
          logout();
        });
      });
    });

    describe('As Provider', function () {
      beforeEach(utils.setUpAsProvider);

      function fillInSplitForm(splitForm, categoryVal) {
        var categorySelect = splitForm.element(by.css('[name=category]')),
            matterType1Select = splitForm.element(by.css('[name=matter_type1]')),
            matterType2Select = splitForm.element(by.css('[name=matter_type2]'));

        categorySelect.element(by.css('option[value="'+categoryVal+'"]')).click();
        matterType1Select.element(by.css('option:last-child')).click();
        matterType2Select.element(by.css('option:last-child')).click();
      }

      it('shouldnt\'t be able to split if the validation fails', function() {
        browser.get('provider/'+case_ref1+'/');

        var splitButton = element(by.cssContainingText('.Button.Button--secondary', 'Split')),
            splitForm = element(by.css('.modal-content [name=split_case_frm]')),
            assignToProviderButton = splitForm.element(by.cssContainingText('button', 'Assign to'));

        expect(splitButton.isDisplayed()).toBe(true);
        splitButton.click();
        expect(splitForm.isDisplayed()).toBe(true);

        // submit without filling in any data => validation error
        assignToProviderButton.click();
        expect(splitForm.isDisplayed()).toBe(true);

        // choose same category of law and submit => validation error
        fillInSplitForm(splitForm, 'family');
        assignToProviderButton.click();
        expect(splitForm.isDisplayed()).toBe(true);

        // choose a category of law that the provider can't deal with and assign to same provider => validation error
        fillInSplitForm(splitForm, 'benefits');
        assignToProviderButton.click();
        expect(splitForm.isDisplayed()).toBe(true);
      });

      it('should be able to split a case and assign it to same provider if they can deal with that category of law', function() {
        var splitButton = element(by.cssContainingText('.Button.Button--secondary', 'Split')),
            splitForm = element(by.css('.modal-content [name=split_case_frm]')),
            assignToProviderButton = splitForm.element(by.cssContainingText('button', 'Assign to'));

        // choose housing category
        fillInSplitForm(splitForm, 'housing');
        assignToProviderButton.click();

        // verify modal disappeared
        expect(splitForm.isPresent()).toBe(false);

        // verify outcome code REF-INT created
        expect(element(by.css('.CaseHistory-label:first-child')).getText()).toBe('REF-INT');

        // try to split again => validation error
        expect(splitButton.isDisplayed()).toBe(true);
        splitButton.click();
        fillInSplitForm(splitForm, 'housing');
        assignToProviderButton.click();
        expect(splitForm.isDisplayed()).toBe(true);
        expect(
          splitForm.element(by.cssContainingText('.Error-message', 'This Case has already been split')).isDisplayed()
        ).toBe(true);
      });

      it('should be able to split a case and assign it back to an operator', function() {
        browser.get('provider/'+case_ref2+'/');

        var splitButton = element(by.cssContainingText('.Button.Button--secondary', 'Split')),
            splitForm = element(by.css('.modal-content [name=split_case_frm]')),
            assignToOperatorButton = splitForm.element(by.cssContainingText('button', 'Send to operator'));

        expect(splitButton.isDisplayed()).toBe(true);
        splitButton.click();
        expect(splitForm.isDisplayed()).toBe(true);

        // choose benefits category
        fillInSplitForm(splitForm, 'benefits');
        assignToOperatorButton.click();

        // verify modal disappeared
        expect(splitForm.isPresent()).toBe(false);

        // verify outcome code REF-EXT created
        expect(element(by.css('.CaseHistory-label:first-child')).getText()).toBe('REF-EXT');

        // try to split again => validation error
        expect(splitButton.isDisplayed()).toBe(true);
        splitButton.click();
        fillInSplitForm(splitForm, 'benefits');
        assignToOperatorButton.click();
        expect(splitForm.isDisplayed()).toBe(true);
        expect(
          splitForm.element(by.cssContainingText('.Error-message', 'This Case has already been split')).isDisplayed()
        ).toBe(true);

        // dismiss the modal
        splitForm.element(by.cssContainingText('a', 'Cancel')).click();
      });
    });

    it('should logout', function () {
      this.after(function () {
        logout();
      });
    });
  });
})();
<|endoftext|>


<commit_before>const request = require('request');
const webhook = require('./webhook.js');
const standardUpdates = require('./updates.js');
const standardMethods = require('./methods.js');

const DEFAULT_PLUGIN_FOLDER = '../plugins/';
const DEFAULT_PLUGINS = ['regExpMessage', 'shortReply'];

class TeleBot {

    constructor(cfg) {

        if (typeof cfg != 'object') cfg = {token: cfg};

        if (!cfg.token || cfg.token.split(':').length != 2) {
            throw Error('[bot.error] invalid bot token');
        }

        this.cfg = cfg;
        this.token = cfg.token;
        this.id = this.token.split(':')[0];
        this.api = `https://api.telegram.org/bot${ this.token }`;
        this.fileLink = `https://api.telegram.org/file/bot${ this.token }/`;

        this.pluginFolder = cfg.pluginFolder || DEFAULT_PLUGIN_FOLDER;
        this.usePlugins = Array.isArray(cfg.usePlugins) ? cfg.usePlugins : [];
        this.defaultPlugins = cfg.defaultPlugins !== undefined ? (cfg.defaultPlugins || []) : DEFAULT_PLUGINS;
        this.pluginConfig = cfg.pluginConfig || {};

        const poll = cfg.polling || {};

        this.proxy = poll.proxy;
        this.limit = poll.limit > 0 && poll.limit <= 100 ? poll.limit : 100;
        this.interval = poll.interval >= 0 ? poll.interval : 300;
        this.timeout = poll.timeout >= 0 ? poll.timeout : 0;
        this.retryTimeout = poll.retryTimeout >= 0 ? poll.retryTimeout : 5000;

        this.webhook = cfg.webhook;

        this.allowedUpdates = typeof cfg.allowedUpdates == 'string' || Array.isArray(cfg.allowedUpdates) ? cfg.allowedUpdates : [];
        this.maxConnections = this.webhook && Number.isInteger(this.webhook.maxConnections) ? this.webhook.maxConnections : 40;

        this.updateId = 0;
        this.loopFn = null;

        this.flags = {
            poll: false,
            retry: false,
            looping: false
        };

        this.modList = {};
        this.eventList = new Map();

        this.updateTypes = standardUpdates;

        this.processUpdate = (update, props) => {
            if (update) {
                for (let name in this.updateTypes) {
                    if (name in update) {
                        update = update[name];
                        return this.updateTypes[name].call(this, update, props);
                    }
                }
            }
        };

        // Load build-in plugins
        const userPluginList = this.usePlugins.filter((item) => this.defaultPlugins.indexOf(item) < 0);
        for (let pluginName of this.defaultPlugins.concat(userPluginList)) {
            this.plug(require(this.pluginFolder + pluginName));
        }

    }

    /* Plugins */

    plug(module) {

        const {id, defaultConfig, plugin} = module;

        if (id) {

            const userConfig = this.pluginConfig[id];
            const isConfigObject = Object.prototype.toString.call(defaultConfig) === '[object Object]';

            let config;
            if (isConfigObject) {
                config = Object.assign(defaultConfig, userConfig);
            } else {
                config = userConfig || defaultConfig;
            }

            plugin.call(this, this, config || {});

            console.log(`[bot.plugin] loaded '${id}' plugin`);

        } else {
            console.log('[bot.plugin] skip plugin without id');
        }

    }

    use(module) {
        console.log('[bot.warning] Use .plug() instead of .use()');
        return this.plug(module);
    }

    /* Connection */

    start() {

        const f = this.flags;

        // Set webhook
        if (this.webhook) {

            let {url, cert} = this.webhook;
            if (url) url = `${ url }/${ this.token }`;

            return this.setWebhook(url, cert, this.allowedUpdates, this.maxConnections).then(() => {

                console.log(`[bot.webhook] set to "${url}"`);
                return webhook.call(this, this, this.webhook);

            }).catch((error) => {

                console.error('[bot.error.webhook]', error);
                this.event('error', {error});

            });

        }

        // Delete webhook
        this.setWebhook().then((response) => {
            f.poll = true;

            if (response.description == 'Webhook was deleted') {
                console.log('[bot.webhook] webhook was deleted');
            }

            console.log('[bot.info] bot started');

        }).catch((error) => {

            console.error('[bot.error.webhook]', error);
            this.event('error', {error});

        });

        f.looping = true;

        this.event('start');

        // Global loop function
        this.loopFn = setInterval(() => {

            // Stop on false looping flag
            if (!f.looping) clearInterval(this.loopFn);

            // Skip processing on false poll flag
            if (!f.poll) return;

            f.poll = false;

            // Get updates
            this.getUpdates().then(() => {

                // Retry connecting
                if (f.retry) {

                    const now = Date.now();
                    const diff = (now - f.retry) / 1000;

                    console.log(`[bot.info.update] reconnected after ${ diff } seconds`);
                    this.event('reconnected', {
                        startTime: f.retry, endTime: now, diffTime: diff
                    });

                    f.retry = false;

                }

                // Tick
                return this.event('tick');

            }).then(() => {

                // Seems okay for the next poll
                f.poll = true;

            }).catch(error => {

                // Set retry flag as current date (for timeout calculations)
                if (f.retry === false) f.retry = Date.now();

                console.error(`[bot.error.update]`, error.stack || error);
                this.event(['error', 'error.update'], {error});

                return Promise.reject();

            }).catch(() => {

                const seconds = this.retryTimeout / 1000;
                console.log(`[bot.info.update] reconnecting in ${ seconds } seconds...`);
                this.event('reconnecting');

                // Set reconnecting timeout
                setTimeout(() => (f.poll = true), this.retryTimeout);

            });

        }, this.interval);

    }

    connect(...args) {
        return this.start(...args);
    }

    /* Stop looping */

    stop(message) {
        this.flags.looping = false;
        console.log(`[bot.info] bot stopped ${ message ? ': ' + message : '' }`);
        this.event('stop', message);
    }

    /* Fetch updates */

    getUpdates(offset = this.updateId, limit = this.limit, timeout = this.timeout, allowed_updates = this.allowedUpdates) {

        // Request updates from Telegram server
        return this.request('/getUpdates', {
            offset, limit, timeout, allowed_updates
        }).then(body =>
            this.receiveUpdates(body.result)
        );

    }

    /* Recive updates */

    receiveUpdates(updateList) {

        // Globals
        var mod, props = {};
        var promise = Promise.resolve();

        // No updates
        if (!updateList.length) return promise;

        // We have updates
        return this.event('update', updateList).then(eventProps => {

            // Run update list modifiers
            mod = this.modRun('updateList', {
                updateList, props: extendProps(props, eventProps)
            });

            updateList = mod.updateList;
            props = mod.props;

            // Every Telegram update
            for (let update of updateList) {

                // Update ID
                const nextId = ++update.update_id;
                if (this.updateId < nextId) this.updateId = nextId;

                // Run update modifiers
                mod = this.modRun('update', {update, props});

                update = mod.update;
                props = mod.props;

                // Process update
                promise = promise.then(() => this.processUpdate(update, props));

            }

            return promise;

        }).catch(error => {

            console.log('[bot.error]', error.stack || error);
            this.event('error', {error});

            // Don't trigger server reconnect
            return Promise.resolve();

        });

    }

    /* Send request to server */

    request(url, form, data) {

        const options = {
            url: this.api + url,
            json: true
        };

        if (this.proxy) options.proxy = this.proxy;

        if (form) {
            options.form = form;
        } else {
            for (let item in data) {
                const type = typeof data[item];
                if (type == 'string' || type == 'object') continue;
                data[item] = JSON.stringify(data[item]);
            }
            options.formData = data;
        }

        return new Promise((resolve, reject) => {
            request.post(options, (error, response, body) => {
                if (error || !body || !body.ok || response.statusCode == 404) {
                    return reject(error || body || 404);
                }
                return resolve(body);
            });
        });

    }

    /* Modifications */

    mod(names, fn) {
        if (typeof names == 'string') names = [names];
        const mods = this.modList;
        for (let name of names) {
            if (!mods[name]) mods[name] = [];
            if (mods[name].includes(fn)) return;
            mods[name].push(fn);
        }
        return fn;
    }

    modRun(name, data) {
        const list = this.modList[name];
        if (!list || !list.length) return data;
        for (let fn of list) data = fn.call(this, data);
        return data;
    }

    removeMod(name, fn) {
        let list = this.modList[name];
        if (!list) return false;
        let index = list.indexOf(fn);
        if (index == -1) return false;
        list.splice(index, 1);
        return true;
    }

    /* Events */

    on(types, fn, opt) {

        if (!opt) opt = {};
        if (!Array.isArray(types)) types = [types];

        const eventList = this.eventList;

        for (let type of types) {

            if (!eventList.has(type)) {

                eventList.set(type, {fired: null, list: [fn]});

            } else {

                const event = eventList.get(type);

                if (event.list.includes(fn)) continue;
                event.list.push(fn);

                if (opt.fired && event.fired) {

                    let fired = event.fired;

                    new Promise((resolve, reject) => {

                        let output = fn.call(fired.self, fired.data, fired.self, fired.details);

                        if (output instanceof Promise) {
                            output.then(resolve).catch(reject);
                        } else {
                            resolve(output);
                        }

                    }).catch(error => {
                        eventPromiseError.call(this, type, fired, error);
                    });

                    if (opt.cleanFired) {
                        eventList.set(type, event.fired = null);
                    }

                }

            }

        }
    }

    event(types, data, self) {

        let promises = [];

        if (!Array.isArray(types)) types = [types];

        for (let type of types) {

            let event = this.eventList.get(type);
            let details = {type, time: Date.now()};
            let fired = {self, data, details};

            if (!event) {
                this.eventList.set(type, {fired, list: []});
                continue;
            }

            event.fired = fired;
            event = event.list;

            for (let fn of event) {

                promises.push((new Promise((resolve, reject) => {
                    let that = this;

                    details.remove = (function (fn) {
                        return () => that.removeEvent(type, fn);
                    }(fn));

                    fn = fn.call(self, data, self, details);

                    if (fn instanceof Promise) {
                        fn.then(resolve).catch(reject);
                    } else {
                        resolve(fn);
                    }

                })).catch(error => {
                    eventPromiseError.call(this, type, fired, error);
                }));

            }

        }

        return Promise.all(promises);
    }

    cleanEvent(type) {
        const eventList = this.eventList;
        if (!eventList.has(type)) return false;
        eventList.set(type, eventList.get(type).fired = null);
        return true;
    }

    removeEvent(type, fn) {
        const eventList = this.eventList;
        if (!eventList.has(type)) return false;
        let event = eventList.get(type).list;
        let index = event.indexOf(fn);
        if (index == -1) return false;
        event.splice(index, 1);
        return true;
    }

    destroyEvent(type) {
        let eventList = this.eventList;
        if (!eventList.has(type)) return false;
        eventList.delete(type);
        return true;
    }

    /* Process global properties */

    properties(form = {}, opt = {}) {

        const parseMode = opt.parseMode || opt.parse;
        const replyToMessage = opt.replyToMessage || opt.reply;
        const replyMarkup = opt.replyMarkup || opt.markup;
        const notification = opt.notification === false || opt.notify === false;
        const webPreview = opt.webPreview === false || opt.preview === false;

        if (replyToMessage) form.reply_to_message_id = replyToMessage;
        if (parseMode) form.parse_mode = parseMode;
        if (notification) form.disable_notification = true;
        if (webPreview) form.disable_web_page_preview = true;

        // Markup object
        if (replyMarkup !== undefined) {
            if (replyMarkup == 'hide' || replyMarkup === false) {
                // Hide keyboard
                form.reply_markup = JSON.stringify({hide_keyboard: true});
            } else if (replyMarkup == 'reply') {
                // Fore reply
                form.reply_markup = JSON.stringify({force_reply: true});
            } else {
                // JSON keyboard
                form.reply_markup = JSON.stringify(replyMarkup);
            }
        }

        return (this.modRun('property', {form, options: opt})).form;

    }

    /* Method adder */

    static addMethods(methods) {

        for (let id in methods) {

            const method = methods[id];

            // If method is a function
            if (typeof method == 'function') {
                this.prototype[id] = method;
                continue;
            }

            // Set method name
            const name = method.short || id;

            // Argument function
            let argFn = method.arguments;
            if (argFn && typeof argFn != 'function') {
                if (typeof argFn == 'string') argFn = [argFn];
                let args = argFn;
                argFn = function () {
                    const form = {};
                    args.forEach((v, i) => form[v] = arguments[i]);
                    return form;
                };
            }

            // Options function
            let optFn = method.options;

            // Create method
            this.prototype[id] = this.prototype[name] = function () {
                this.event([id, name], arguments);
                let form = {}, args = [].slice.call(arguments);
                let options = args[args.length - 1], fnOptions = {};
                if (typeof options != 'object') options = {};
                if (argFn) form = argFn.apply(this, args);
                if (optFn) fnOptions = optFn.apply(this, [].concat(form, options));
                form = this.properties(form, Object.assign(options, fnOptions));
                let request = this.request(`/${id}`, form);
                if (method.then) request = request.then(method.then);
                return request;
            };

        }
    }
}

/* Add standard methods */

TeleBot.addMethods(standardMethods);

/* Functions */

function eventPromiseError(type, fired, error) {
    return new Promise((resolve, reject) => {
        console.error('[bot.error.event]', error.stack || error);
        if (type != 'error' && type != 'error.event') {
            this.event(['error', 'error.event'], {error, data: fired.data})
                .then(resolve).catch(reject);
        } else {
            resolve();
        }
    });
}

function extendProps(props, input) {
    for (let obj of input) {
        for (let naprops in obj) {
            const key = props[naprops], value = obj[naprops];
            if (key !== undefined) {
                if (!Array.isArray(key)) props[naprops] = [key];
                props[naprops].push(value);
                continue;
            }
            props[naprops] = value;
        }
    }
    return props;
}

/* Exports */

module.exports = TeleBot;
<commit_msg>this.logging<commit_after>const request = require('request');
const webhook = require('./webhook.js');
const standardUpdates = require('./updates.js');
const standardMethods = require('./methods.js');

const DEFAULT_PLUGIN_FOLDER = '../plugins/';
const DEFAULT_PLUGINS = ['regExpMessage', 'shortReply'];

class TeleBot {

    constructor(cfg) {

        if (typeof cfg != 'object') cfg = {token: cfg};

        if (!cfg.token || cfg.token.split(':').length != 2) {
            throw Error('[bot.error] invalid bot token');
        }

        this.cfg = cfg;
        this.token = cfg.token;
        this.id = this.token.split(':')[0];
        this.api = `https://api.telegram.org/bot${ this.token }`;
        this.fileLink = `https://api.telegram.org/file/bot${ this.token }/`;

        this.pluginFolder = cfg.pluginFolder || DEFAULT_PLUGIN_FOLDER;
        this.usePlugins = Array.isArray(cfg.usePlugins) ? cfg.usePlugins : [];
        this.defaultPlugins = cfg.defaultPlugins !== undefined ? (cfg.defaultPlugins || []) : DEFAULT_PLUGINS;
        this.pluginConfig = cfg.pluginConfig || {};
        this.logging = cfg.logging || true;

        const poll = cfg.polling || {};

        this.proxy = poll.proxy;
        this.limit = poll.limit > 0 && poll.limit <= 100 ? poll.limit : 100;
        this.interval = poll.interval >= 0 ? poll.interval : 300;
        this.timeout = poll.timeout >= 0 ? poll.timeout : 0;
        this.retryTimeout = poll.retryTimeout >= 0 ? poll.retryTimeout : 5000;

        this.webhook = cfg.webhook;

        this.allowedUpdates = typeof cfg.allowedUpdates == 'string' || Array.isArray(cfg.allowedUpdates) ? cfg.allowedUpdates : [];
        this.maxConnections = this.webhook && Number.isInteger(this.webhook.maxConnections) ? this.webhook.maxConnections : 40;

        this.updateId = 0;
        this.loopFn = null;

        this.flags = {
            poll: false,
            retry: false,
            looping: false
        };

        this.modList = {};
        this.eventList = new Map();

        this.updateTypes = standardUpdates;

        this.processUpdate = (update, props) => {
            if (update) {
                for (let name in this.updateTypes) {
                    if (name in update) {
                        update = update[name];
                        return this.updateTypes[name].call(this, update, props);
                    }
                }
            }
        };

        // Load build-in plugins
        const userPluginList = this.usePlugins.filter((item) => this.defaultPlugins.indexOf(item) < 0);
        for (let pluginName of this.defaultPlugins.concat(userPluginList)) {
            this.plug(require(this.pluginFolder + pluginName));
        }

    }

    /* Plugins */

    plug(module) {

        const {id, defaultConfig, plugin} = module;

        if (id) {

            const userConfig = this.pluginConfig[id];
            const isConfigObject = Object.prototype.toString.call(defaultConfig) === '[object Object]';

            let config;
            if (isConfigObject) {
                config = Object.assign(defaultConfig, userConfig);
            } else {
                config = userConfig || defaultConfig;
            }

            plugin.call(this, this, config || {});

            if(this.logging) console.log(`[bot.plugin] loaded '${id}' plugin`);

        } else {
            if(this.logging) console.log('[bot.plugin] skip plugin without id');
        }

    }

    use(module) {
        console.log('[bot.warning] Use .plug() instead of .use()');
        return this.plug(module);
    }

    /* Connection */

    start() {

        const f = this.flags;

        // Set webhook
        if (this.webhook) {

            let {url, cert} = this.webhook;
            if (url) url = `${ url }/${ this.token }`;

            return this.setWebhook(url, cert, this.allowedUpdates, this.maxConnections).then(() => {

                if(this.logging) console.log(`[bot.webhook] set to "${url}"`);
                return webhook.call(this, this, this.webhook);

            }).catch((error) => {

                if(this.logging) console.error('[bot.error.webhook]', error);
                this.event('error', {error});

            });

        }

        // Delete webhook
        this.setWebhook().then((response) => {
            f.poll = true;

            if (response.description == 'Webhook was deleted') {
                if(this.logging) console.log('[bot.webhook] webhook was deleted');
            }

            if(this.logging) console.log('[bot.info] bot started');

        }).catch((error) => {

            if(this.logging) console.error('[bot.error.webhook]', error);
            this.event('error', {error});

        });

        f.looping = true;

        this.event('start');

        // Global loop function
        this.loopFn = setInterval(() => {

            // Stop on false looping flag
            if (!f.looping) clearInterval(this.loopFn);

            // Skip processing on false poll flag
            if (!f.poll) return;

            f.poll = false;

            // Get updates
            this.getUpdates().then(() => {

                // Retry connecting
                if (f.retry) {

                    const now = Date.now();
                    const diff = (now - f.retry) / 1000;

                    if(this.logging) console.log(`[bot.info.update] reconnected after ${ diff } seconds`);
                    this.event('reconnected', {
                        startTime: f.retry, endTime: now, diffTime: diff
                    });

                    f.retry = false;

                }

                // Tick
                return this.event('tick');

            }).then(() => {

                // Seems okay for the next poll
                f.poll = true;

            }).catch(error => {

                // Set retry flag as current date (for timeout calculations)
                if (f.retry === false) f.retry = Date.now();

                if(this.logging) console.error(`[bot.error.update]`, error.stack || error);
                this.event(['error', 'error.update'], {error});

                return Promise.reject();

            }).catch(() => {

                const seconds = this.retryTimeout / 1000;
                if(this.logging) console.log(`[bot.info.update] reconnecting in ${ seconds } seconds...`);
                this.event('reconnecting');

                // Set reconnecting timeout
                setTimeout(() => (f.poll = true), this.retryTimeout);

            });

        }, this.interval);

    }

    connect(...args) {
        return this.start(...args);
    }

    /* Stop looping */

    stop(message) {
        this.flags.looping = false;
        if(this.logging) console.log(`[bot.info] bot stopped ${ message ? ': ' + message : '' }`);
        this.event('stop', message);
    }

    /* Fetch updates */

    getUpdates(offset = this.updateId, limit = this.limit, timeout = this.timeout, allowed_updates = this.allowedUpdates) {

        // Request updates from Telegram server
        return this.request('/getUpdates', {
            offset, limit, timeout, allowed_updates
        }).then(body =>
            this.receiveUpdates(body.result)
        );

    }

    /* Recive updates */

    receiveUpdates(updateList) {

        // Globals
        var mod, props = {};
        var promise = Promise.resolve();

        // No updates
        if (!updateList.length) return promise;

        // We have updates
        return this.event('update', updateList).then(eventProps => {

            // Run update list modifiers
            mod = this.modRun('updateList', {
                updateList, props: extendProps(props, eventProps)
            });

            updateList = mod.updateList;
            props = mod.props;

            // Every Telegram update
            for (let update of updateList) {

                // Update ID
                const nextId = ++update.update_id;
                if (this.updateId < nextId) this.updateId = nextId;

                // Run update modifiers
                mod = this.modRun('update', {update, props});

                update = mod.update;
                props = mod.props;

                // Process update
                promise = promise.then(() => this.processUpdate(update, props));

            }

            return promise;

        }).catch(error => {

            if(this.logging) console.log('[bot.error]', error.stack || error);
            this.event('error', {error});

            // Don't trigger server reconnect
            return Promise.resolve();

        });

    }

    /* Send request to server */

    request(url, form, data) {

        const options = {
            url: this.api + url,
            json: true
        };

        if (this.proxy) options.proxy = this.proxy;

        if (form) {
            options.form = form;
        } else {
            for (let item in data) {
                const type = typeof data[item];
                if (type == 'string' || type == 'object') continue;
                data[item] = JSON.stringify(data[item]);
            }
            options.formData = data;
        }

        return new Promise((resolve, reject) => {
            request.post(options, (error, response, body) => {
                if (error || !body || !body.ok || response.statusCode == 404) {
                    return reject(error || body || 404);
                }
                return resolve(body);
            });
        });

    }

    /* Modifications */

    mod(names, fn) {
        if (typeof names == 'string') names = [names];
        const mods = this.modList;
        for (let name of names) {
            if (!mods[name]) mods[name] = [];
            if (mods[name].includes(fn)) return;
            mods[name].push(fn);
        }
        return fn;
    }

    modRun(name, data) {
        const list = this.modList[name];
        if (!list || !list.length) return data;
        for (let fn of list) data = fn.call(this, data);
        return data;
    }

    removeMod(name, fn) {
        let list = this.modList[name];
        if (!list) return false;
        let index = list.indexOf(fn);
        if (index == -1) return false;
        list.splice(index, 1);
        return true;
    }

    /* Events */

    on(types, fn, opt) {

        if (!opt) opt = {};
        if (!Array.isArray(types)) types = [types];

        const eventList = this.eventList;

        for (let type of types) {

            if (!eventList.has(type)) {

                eventList.set(type, {fired: null, list: [fn]});

            } else {

                const event = eventList.get(type);

                if (event.list.includes(fn)) continue;
                event.list.push(fn);

                if (opt.fired && event.fired) {

                    let fired = event.fired;

                    new Promise((resolve, reject) => {

                        let output = fn.call(fired.self, fired.data, fired.self, fired.details);

                        if (output instanceof Promise) {
                            output.then(resolve).catch(reject);
                        } else {
                            resolve(output);
                        }

                    }).catch(error => {
                        eventPromiseError.call(this, type, fired, error);
                    });

                    if (opt.cleanFired) {
                        eventList.set(type, event.fired = null);
                    }

                }

            }

        }
    }

    event(types, data, self) {

        let promises = [];

        if (!Array.isArray(types)) types = [types];

        for (let type of types) {

            let event = this.eventList.get(type);
            let details = {type, time: Date.now()};
            let fired = {self, data, details};

            if (!event) {
                this.eventList.set(type, {fired, list: []});
                continue;
            }

            event.fired = fired;
            event = event.list;

            for (let fn of event) {

                promises.push((new Promise((resolve, reject) => {
                    let that = this;

                    details.remove = (function (fn) {
                        return () => that.removeEvent(type, fn);
                    }(fn));

                    fn = fn.call(self, data, self, details);

                    if (fn instanceof Promise) {
                        fn.then(resolve).catch(reject);
                    } else {
                        resolve(fn);
                    }

                })).catch(error => {
                    eventPromiseError.call(this, type, fired, error);
                }));

            }

        }

        return Promise.all(promises);
    }

    cleanEvent(type) {
        const eventList = this.eventList;
        if (!eventList.has(type)) return false;
        eventList.set(type, eventList.get(type).fired = null);
        return true;
    }

    removeEvent(type, fn) {
        const eventList = this.eventList;
        if (!eventList.has(type)) return false;
        let event = eventList.get(type).list;
        let index = event.indexOf(fn);
        if (index == -1) return false;
        event.splice(index, 1);
        return true;
    }

    destroyEvent(type) {
        let eventList = this.eventList;
        if (!eventList.has(type)) return false;
        eventList.delete(type);
        return true;
    }

    /* Process global properties */

    properties(form = {}, opt = {}) {

        const parseMode = opt.parseMode || opt.parse;
        const replyToMessage = opt.replyToMessage || opt.reply;
        const replyMarkup = opt.replyMarkup || opt.markup;
        const notification = opt.notification === false || opt.notify === false;
        const webPreview = opt.webPreview === false || opt.preview === false;

        if (replyToMessage) form.reply_to_message_id = replyToMessage;
        if (parseMode) form.parse_mode = parseMode;
        if (notification) form.disable_notification = true;
        if (webPreview) form.disable_web_page_preview = true;

        // Markup object
        if (replyMarkup !== undefined) {
            if (replyMarkup == 'hide' || replyMarkup === false) {
                // Hide keyboard
                form.reply_markup = JSON.stringify({hide_keyboard: true});
            } else if (replyMarkup == 'reply') {
                // Fore reply
                form.reply_markup = JSON.stringify({force_reply: true});
            } else {
                // JSON keyboard
                form.reply_markup = JSON.stringify(replyMarkup);
            }
        }

        return (this.modRun('property', {form, options: opt})).form;

    }

    /* Method adder */

    static addMethods(methods) {

        for (let id in methods) {

            const method = methods[id];

            // If method is a function
            if (typeof method == 'function') {
                this.prototype[id] = method;
                continue;
            }

            // Set method name
            const name = method.short || id;

            // Argument function
            let argFn = method.arguments;
            if (argFn && typeof argFn != 'function') {
                if (typeof argFn == 'string') argFn = [argFn];
                let args = argFn;
                argFn = function () {
                    const form = {};
                    args.forEach((v, i) => form[v] = arguments[i]);
                    return form;
                };
            }

            // Options function
            let optFn = method.options;

            // Create method
            this.prototype[id] = this.prototype[name] = function () {
                this.event([id, name], arguments);
                let form = {}, args = [].slice.call(arguments);
                let options = args[args.length - 1], fnOptions = {};
                if (typeof options != 'object') options = {};
                if (argFn) form = argFn.apply(this, args);
                if (optFn) fnOptions = optFn.apply(this, [].concat(form, options));
                form = this.properties(form, Object.assign(options, fnOptions));
                let request = this.request(`/${id}`, form);
                if (method.then) request = request.then(method.then);
                return request;
            };

        }
    }
}

/* Add standard methods */

TeleBot.addMethods(standardMethods);

/* Functions */

function eventPromiseError(type, fired, error) {
    return new Promise((resolve, reject) => {
        if(this.logging) console.error('[bot.error.event]', error.stack || error);
        if (type != 'error' && type != 'error.event') {
            this.event(['error', 'error.event'], {error, data: fired.data})
                .then(resolve).catch(reject);
        } else {
            resolve();
        }
    });
}

function extendProps(props, input) {
    for (let obj of input) {
        for (let naprops in obj) {
            const key = props[naprops], value = obj[naprops];
            if (key !== undefined) {
                if (!Array.isArray(key)) props[naprops] = [key];
                props[naprops].push(value);
                continue;
            }
            props[naprops] = value;
        }
    }
    return props;
}

/* Exports */

module.exports = TeleBot;
<|endoftext|>