<commit_before>/*
 * Geddy JavaScript Web development framework
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/
var crypto = require('crypto')
  , Cookie;

Cookie = function (callback) {
  this.setup(callback);
};

Cookie.prototype = new (function () {
  this.setup = function (callback) {
    callback();
  };

  this.read = function (session, callback) {
    var controller = session.controller
      , crypted = controller.cookies.get(geddy.config.cookieSessionKey)
      , decipher
      , data = {};

    if (crypted) {
      decipher = crypto.createDecipher('aes-256-cbc', geddy.config.secret +
          this.id);
      data = decipher.update(crypted, 'hex', 'utf8');
      data += decipher.final('utf8');
      try {
        data = JSON.parse(data);
      }
      catch (e) {}
    }

    callback(data);
  };

  this.write = function (session, callback) {
    var controller = session.controller
      , data = JSON.stringify(session.data)
      , cipher
      , crypted
      , opts = {
          expires: (new Date(session.expires)).toGMTString()
        };

    cipher = crypto.createCipher('aes-256-cbc', geddy.config.secret +
        this.id);
    crypted = cipher.update(data, 'utf8', 'hex');
    crypted += cipher.final('hex');

    controller.cookies.set(geddy.config.cookieSessionKey, crypted, opts);
    callback();
  };

})();

exports.Cookie = Cookie;

<commit_msg>Issue #186 Fatol error decrypting encrypted cookie session data<commit_after>/*
 * Geddy JavaScript Web development framework
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/
var crypto = require('crypto')
  , Cookie;

Cookie = function (callback) {
  this.setup(callback);
};

Cookie.prototype = new (function () {
  this.setup = function (callback) {
    callback();
  };

  this.read = function (session, callback) {
    var controller = session.controller
      , crypted = controller.cookies.get(geddy.config.cookieSessionKey)
      , decipher
      , data = {};

    if (crypted) {
      decipher = crypto.createDecipher('aes-256-cbc', geddy.config.secret +
          this.id);
      data = decipher.update(crypted, 'hex', 'utf8');
      try {
        data += decipher.final('utf8');
        try {
          data = JSON.parse(data);
        }
        catch (e) {}
      }
      catch (e) {}
    }

    callback(data);
  };

  this.write = function (session, callback) {
    var controller = session.controller
      , data = JSON.stringify(session.data)
      , cipher
      , crypted
      , opts = {
          expires: (new Date(session.expires)).toGMTString()
        };

    cipher = crypto.createCipher('aes-256-cbc', geddy.config.secret +
        this.id);
    crypted = cipher.update(data, 'utf8', 'hex');
    crypted += cipher.final('hex');

    controller.cookies.set(geddy.config.cookieSessionKey, crypted, opts);
    callback();
  };

})();

exports.Cookie = Cookie;

<|endoftext|>


<commit_before>const fs = require('fs');
const EventEmitter = require('events');
const FormData = require('form-data');
const concat = require('concat-stream');
const shopwareStoreClient = require('./client/shopwareStoreClient');

function getPostDataFromFile(filePath) {
    return new Promise((resolve) => {
        const fd = new FormData();
        fd.append('file', fs.createReadStream(filePath));
        fd.pipe(concat({ encoding: 'buffer' }, data => resolve({ data, headers: fd.getHeaders() })));
    });
}

module.exports = class ShopwareStoreCommander {

    /**
     * @constructor
     * @param {String} username
     * @param {String} password
     * @param {EventEmitter} emitter - optional
     */
    constructor(username, password, emitter = new EventEmitter()) {
        this.client = shopwareStoreClient(username, password, emitter);
        this.logEmitter = emitter;
        this.accountData = null;
        this.statics = {};
    }

    /**
     * @return {Object}
     */
    async getAccountData() {
        if (this.accountData) {
            return this.accountData;
        }
        const producers = await this.client.get('producers');

        // Save producer ID (required to load e.g. plugins)
        this.accountData = {
            producerId: producers.data[0].id,
        };

        const plugins = await this.client.get('plugins', {
            params: {
                offset: 0,
                limit: 1000,
                producerId: this.accountData.producerId,
            },
        });

        // Save plugins
        this.accountData.plugins = {};
        plugins.data.forEach((plugin) => {
            this.accountData.plugins[plugin.name] = plugin;
        });

        // Load static definitions like error codes etc.
        const pluginStatics = await this.client.get('pluginstatics/all');

        this.statics = pluginStatics.data;

        return this.accountData;
    }

    /**
     * @param {String} pluginName
     * @param {Object} extraFields - optional
     * @return {Object}
     */
    async findPlugin(pluginName, extraFields) {
        const accountData = await this.getAccountData();

        if (!accountData.plugins[pluginName]) {
            this.logEmitter('error', `Plugin ${pluginName} not found!`);

            return null;
        }
        if (extraFields && extraFields.length > 0) {
            return this.loadExtraPluginFields(accountData.plugins[pluginName], extraFields);
        }

        return accountData.plugins[pluginName];
    }

    /**
     * @param {Object} plugin
     * @return {Object}
     */
    async savePlugin(plugin) {
        this.logEmitter.emit('info', `Saving changes in plugin ${plugin.name}...`);

        const res = await this.client.put(`plugins/${plugin.id}`, plugin);
        // Save the updated data locally
        const accountData = await this.getAccountData();
        this.updatePluginData(accountData.plugins[plugin.name], res.data);

        return accountData.plugins[plugin.name];
    }

    /**
     * @param {Object} plugin
     * @param {String} filePath
     * @return {Object}
     */
    async uploadPluginBinary(plugin, filePath) {
        const binaryName = filePath.split(/(\\|\/)/g).pop();
        this.logEmitter.emit('info', `Uploading binary ${binaryName} for plugin ${plugin.name}...`);

        const { data, headers } = await getPostDataFromFile(filePath);
        const res = await this.client.post(`plugins/${plugin.id}/binaries`, data, { headers });

        // Add the binary info to the plugin
        plugin.binaries = res.data;
        plugin.latestBinary = plugin.binaries[plugin.binaries.length - 1];

        return plugin;
    }

    /**
     * @param {Object} plugin
     * @param {Object} binary
     * @param {String} filePath
     * @return {Object}
     */
    async updatePluginBinary(plugin, binary, filePath) {
        const binaryName = filePath.split(/(\\|\/)/g).pop();
        this.logEmitter.emit('info', `Uploading updated binary ${binaryName} for plugin ${plugin.name}...`);

        const { data, headers } = await getPostDataFromFile(filePath);
        const res = await this.client.post(`plugins/${plugin.id}/binaries/${binary.id}/file`, data, { headers });

        // Add the binary info to the plugin
        plugin.binaries = res.data;
        plugin.latestBinary = plugin.binaries[plugin.binaries.length - 1];

        return plugin;
    }

    /**
     * @param {Object} plugin
     * @param {Object} binary
     * @return {Object}
     */
    async savePluginBinary(plugin, binary) {
        this.logEmitter.emit('info', `Saving binary version ${binary.version} of plugin ${plugin.name}...`);

        const res = await this.client.put(`plugins/${plugin.id}/binaries/${binary.id}`, binary);
        // Save the updated data locally
        binary.changelogs = res.data.changelogs;
        binary.compatibleSoftwareVersions = res.data.compatibleSoftwareVersions;
        binary.status = res.data.status;

        return plugin;
    }

    /**
     * @param {Object} plugin
     * @return {Object}
     */
    async requestBinaryReview(plugin) {
        const reviewPending = review => review.status.id === 1;
        this.logEmitter.emit('info', `Requesting review of plugin ${plugin.name}...`);

        const res = await this.client.post(`plugins/${plugin.id}/reviews`);
        // Save the review
        const review = res.data;
        plugin.reviews.push(review);

        // Wait for the review to finish
        let pollCount = 0;
        do {
            this.logEmitter.emit('waitingReview');

            // eslint-disable-next-line no-await-in-loop
            await new Promise((resolve) => {
                setTimeout(resolve, 3000);
            });
            pollCount += 1;

            // Get review status
            // eslint-disable-next-line no-await-in-loop
            const reviews = await this.client.get(`plugins/${plugin.id}/reviews`);
            // Update polled review
            const updatedReview = reviews.data.find(rev => rev.id === review.id);
            review.status = updatedReview.status;
            review.comment = updatedReview.comment;
        } while (reviewPending(review) && pollCount < 20);

        if (reviewPending(review)) {
            // Max polls reached
            throw new Error(
                'Reviews is taking longer than expected. Please check the review status online at ' +
                'https://account.shopware.de/'
            );
        }

        return plugin;
    }

    /**
     * @param {Object} plugin
     * @return {Object}
     */
    async enablePartialIonCubeEncryption(plugin) {
        // Check the plugin for the 'encryptionIonCube' addon
        const encryptionAddon = plugin.addons.find(addon => addon.name === 'encryptionIonCube');
        if (!encryptionAddon) {
            // The plugin is not encrpyted, hence don't enable partial encrpytion either
            return plugin;
        }

        // Check the plugin for the 'partialIonCubeEncryptionAllowed' addon
        const partialEncryptionAddonName = 'partialIonCubeEncryptionAllowed';
        let partialEncryptionAddon = plugin.addons.find(addon => addon.name === partialEncryptionAddonName);
        if (partialEncryptionAddon) {
            this.logEmitter.emit('info', `Partial ionCube encryption for plugin ${plugin.name} already enabled`);

            return plugin;
        }

        // Find and add the addon for partial ionCube encryption
        partialEncryptionAddon = this.statics.addons.find(addon => addon.name === partialEncryptionAddonName);
        if (!partialEncryptionAddon) {
            throw new Error(
                `Cannot enable partial ionCube encryption for plugin ${plugin.name} due to missing plugin addon option`
            );
        }
        plugin.addons.push(partialEncryptionAddon);

        this.logEmitter.emit('info', `Enabling partial ionCube encryption for plugin ${plugin.name}...`);

        const res = await this.client.put(`plugins/${plugin.id}`, plugin);
        // Save the updated data locally
        const accountData = await this.getAccountData();
        this.updatePluginData(accountData.plugins[plugin.name], res.data);

        return accountData.plugins[plugin.name];
    }

    /**
     * @param {Object} plugin
     * @param {Array} fields
     * @return {Object}
     */
    async loadExtraPluginFields(plugin, fields) {
        plugin.scsLoadedExtraFields = plugin.scsLoadedExtraFields || [];
        // Load all extra fields
        const extraFieldPromises = fields.map(async (field) => {
            const res = await this.client.get(plugin[field]);
            plugin[field] = res.data;
            // Mark the extra field as loaded
            if (plugin.scsLoadedExtraFields.indexOf(field) === -1) {
                plugin.scsLoadedExtraFields.push(field);
            }
        });
        await Promise.all(extraFieldPromises);

        return plugin;
    }

    /**
     * Apply the given data to the plugin, without overwriting any extra fields that were
     * previously loaded and stored in the plugin using 'loadExtraPluginFields()'.
     *
     * @param {Object} plugin
     * @param {Object} data
     */
    updatePluginData(plugin, data) {
        Object.keys(data).forEach((key) => {
            // Only overwrite the field, if it is not a loaded extra field
            if (plugin[key] && plugin.scsLoadedExtraFields && plugin.scsLoadedExtraFields.indexOf(key) === -1) {
                plugin[key] = data[key];
            }
        });
    }

    /**
     * @param {Object} plugin
     * @param {Object} lastSale to compare against for db sync - optional
     * @return {Array}
     */
    async getSalesForPlugin(plugin, lastSale) {
        const limit = 100;
        const sales = [];
        const accountData = await this.getAccountData();
        let currOffset = 0;
        let tmpArr = [];
        let exit = false;
        do {
            // don't get all sales concurrently as we might skip just after the first request
            // eslint-disable-next-line no-await-in-loop
            tmpArr = await this.client.get('pluginsales/list', {
                params: {
                    offset: currOffset,
                    limit,
                    producerId: accountData.producerId,
                    pluginId: plugin.id,
                },
            });
            tmpArr = tmpArr.data;
            for (let i = 0; i < tmpArr.length; i += 1) {
                const sale = tmpArr[i];
                if (lastSale &&
                    new Date(sale.creationDate).toISOString() === lastSale.bookingDate.toISOString() &&
                    (sale.priceModel.price ? sale.priceModel.price : 0) === lastSale.amount &&
                    sale.id === lastSale.shopwareId &&
                    sale.priceModel.type === lastSale.type) {
                    exit = true;
                    break;
                } else {
                    sales.push(sale);
                }
            }
            currOffset += limit;
        } while (tmpArr.length !== 0 && !exit);

        return sales;
    }

    /**
     * @param {Object} plugin
     * @param {Object} lastCommission to compare against for db sync - optional
     * @return {Array}
     */
    async getCommissionsForPlugin(plugin, lastCommission) {
        const limit = 100;
        const commissions = [];
        const accountData = await this.getAccountData();
        let currOffset = 0;
        let tmpArr = [];
        let exit = false;
        do {
            // don't get all sales concurrently as we might skip just after the first request
            // shopware calls commissions brokerages
            // eslint-disable-next-line no-await-in-loop
            tmpArr = await this.client.get('pluginbrokerages/list', {
                params: {
                    offset: currOffset,
                    limit,
                    producerId: accountData.producerId,
                    pluginId: plugin.id,
                },
            });
            tmpArr = tmpArr.data.list;
            for (let i = 0; i < tmpArr.length; i += 1) {
                const commission = tmpArr[i];
                if (lastCommission &&
                    new Date(commission.bookingDate).toISOString() === lastCommission.bookingDate.toISOString() &&
                    commission.amount === lastCommission.amount &&
                    commission.orderNumber === lastCommission.orderNumber &&
                    commission.text === lastCommission.text &&
                    commission.type.name === lastCommission.type &&
                    commission.domain === lastCommission.domain) {
                    exit = true;
                    break;
                } else {
                    commissions.push(commission);
                }
            }
            currOffset += limit;
        } while (tmpArr.length !== 0 && !exit);

        return commissions;
    }

};
<commit_msg>Include the last sale and commission as well<commit_after>const fs = require('fs');
const EventEmitter = require('events');
const FormData = require('form-data');
const concat = require('concat-stream');
const shopwareStoreClient = require('./client/shopwareStoreClient');

function getPostDataFromFile(filePath) {
    return new Promise((resolve) => {
        const fd = new FormData();
        fd.append('file', fs.createReadStream(filePath));
        fd.pipe(concat({ encoding: 'buffer' }, data => resolve({ data, headers: fd.getHeaders() })));
    });
}

module.exports = class ShopwareStoreCommander {

    /**
     * @constructor
     * @param {String} username
     * @param {String} password
     * @param {EventEmitter} emitter - optional
     */
    constructor(username, password, emitter = new EventEmitter()) {
        this.client = shopwareStoreClient(username, password, emitter);
        this.logEmitter = emitter;
        this.accountData = null;
        this.statics = {};
    }

    /**
     * @return {Object}
     */
    async getAccountData() {
        if (this.accountData) {
            return this.accountData;
        }
        const producers = await this.client.get('producers');

        // Save producer ID (required to load e.g. plugins)
        this.accountData = {
            producerId: producers.data[0].id,
        };

        const plugins = await this.client.get('plugins', {
            params: {
                offset: 0,
                limit: 1000,
                producerId: this.accountData.producerId,
            },
        });

        // Save plugins
        this.accountData.plugins = {};
        plugins.data.forEach((plugin) => {
            this.accountData.plugins[plugin.name] = plugin;
        });

        // Load static definitions like error codes etc.
        const pluginStatics = await this.client.get('pluginstatics/all');

        this.statics = pluginStatics.data;

        return this.accountData;
    }

    /**
     * @param {String} pluginName
     * @param {Object} extraFields - optional
     * @return {Object}
     */
    async findPlugin(pluginName, extraFields) {
        const accountData = await this.getAccountData();

        if (!accountData.plugins[pluginName]) {
            this.logEmitter('error', `Plugin ${pluginName} not found!`);

            return null;
        }
        if (extraFields && extraFields.length > 0) {
            return this.loadExtraPluginFields(accountData.plugins[pluginName], extraFields);
        }

        return accountData.plugins[pluginName];
    }

    /**
     * @param {Object} plugin
     * @return {Object}
     */
    async savePlugin(plugin) {
        this.logEmitter.emit('info', `Saving changes in plugin ${plugin.name}...`);

        const res = await this.client.put(`plugins/${plugin.id}`, plugin);
        // Save the updated data locally
        const accountData = await this.getAccountData();
        this.updatePluginData(accountData.plugins[plugin.name], res.data);

        return accountData.plugins[plugin.name];
    }

    /**
     * @param {Object} plugin
     * @param {String} filePath
     * @return {Object}
     */
    async uploadPluginBinary(plugin, filePath) {
        const binaryName = filePath.split(/(\\|\/)/g).pop();
        this.logEmitter.emit('info', `Uploading binary ${binaryName} for plugin ${plugin.name}...`);

        const { data, headers } = await getPostDataFromFile(filePath);
        const res = await this.client.post(`plugins/${plugin.id}/binaries`, data, { headers });

        // Add the binary info to the plugin
        plugin.binaries = res.data;
        plugin.latestBinary = plugin.binaries[plugin.binaries.length - 1];

        return plugin;
    }

    /**
     * @param {Object} plugin
     * @param {Object} binary
     * @param {String} filePath
     * @return {Object}
     */
    async updatePluginBinary(plugin, binary, filePath) {
        const binaryName = filePath.split(/(\\|\/)/g).pop();
        this.logEmitter.emit('info', `Uploading updated binary ${binaryName} for plugin ${plugin.name}...`);

        const { data, headers } = await getPostDataFromFile(filePath);
        const res = await this.client.post(`plugins/${plugin.id}/binaries/${binary.id}/file`, data, { headers });

        // Add the binary info to the plugin
        plugin.binaries = res.data;
        plugin.latestBinary = plugin.binaries[plugin.binaries.length - 1];

        return plugin;
    }

    /**
     * @param {Object} plugin
     * @param {Object} binary
     * @return {Object}
     */
    async savePluginBinary(plugin, binary) {
        this.logEmitter.emit('info', `Saving binary version ${binary.version} of plugin ${plugin.name}...`);

        const res = await this.client.put(`plugins/${plugin.id}/binaries/${binary.id}`, binary);
        // Save the updated data locally
        binary.changelogs = res.data.changelogs;
        binary.compatibleSoftwareVersions = res.data.compatibleSoftwareVersions;
        binary.status = res.data.status;

        return plugin;
    }

    /**
     * @param {Object} plugin
     * @return {Object}
     */
    async requestBinaryReview(plugin) {
        const reviewPending = review => review.status.id === 1;
        this.logEmitter.emit('info', `Requesting review of plugin ${plugin.name}...`);

        const res = await this.client.post(`plugins/${plugin.id}/reviews`);
        // Save the review
        const review = res.data;
        plugin.reviews.push(review);

        // Wait for the review to finish
        let pollCount = 0;
        do {
            this.logEmitter.emit('waitingReview');

            // eslint-disable-next-line no-await-in-loop
            await new Promise((resolve) => {
                setTimeout(resolve, 3000);
            });
            pollCount += 1;

            // Get review status
            // eslint-disable-next-line no-await-in-loop
            const reviews = await this.client.get(`plugins/${plugin.id}/reviews`);
            // Update polled review
            const updatedReview = reviews.data.find(rev => rev.id === review.id);
            review.status = updatedReview.status;
            review.comment = updatedReview.comment;
        } while (reviewPending(review) && pollCount < 20);

        if (reviewPending(review)) {
            // Max polls reached
            throw new Error(
                'Reviews is taking longer than expected. Please check the review status online at ' +
                'https://account.shopware.de/'
            );
        }

        return plugin;
    }

    /**
     * @param {Object} plugin
     * @return {Object}
     */
    async enablePartialIonCubeEncryption(plugin) {
        // Check the plugin for the 'encryptionIonCube' addon
        const encryptionAddon = plugin.addons.find(addon => addon.name === 'encryptionIonCube');
        if (!encryptionAddon) {
            // The plugin is not encrpyted, hence don't enable partial encrpytion either
            return plugin;
        }

        // Check the plugin for the 'partialIonCubeEncryptionAllowed' addon
        const partialEncryptionAddonName = 'partialIonCubeEncryptionAllowed';
        let partialEncryptionAddon = plugin.addons.find(addon => addon.name === partialEncryptionAddonName);
        if (partialEncryptionAddon) {
            this.logEmitter.emit('info', `Partial ionCube encryption for plugin ${plugin.name} already enabled`);

            return plugin;
        }

        // Find and add the addon for partial ionCube encryption
        partialEncryptionAddon = this.statics.addons.find(addon => addon.name === partialEncryptionAddonName);
        if (!partialEncryptionAddon) {
            throw new Error(
                `Cannot enable partial ionCube encryption for plugin ${plugin.name} due to missing plugin addon option`
            );
        }
        plugin.addons.push(partialEncryptionAddon);

        this.logEmitter.emit('info', `Enabling partial ionCube encryption for plugin ${plugin.name}...`);

        const res = await this.client.put(`plugins/${plugin.id}`, plugin);
        // Save the updated data locally
        const accountData = await this.getAccountData();
        this.updatePluginData(accountData.plugins[plugin.name], res.data);

        return accountData.plugins[plugin.name];
    }

    /**
     * @param {Object} plugin
     * @param {Array} fields
     * @return {Object}
     */
    async loadExtraPluginFields(plugin, fields) {
        plugin.scsLoadedExtraFields = plugin.scsLoadedExtraFields || [];
        // Load all extra fields
        const extraFieldPromises = fields.map(async (field) => {
            const res = await this.client.get(plugin[field]);
            plugin[field] = res.data;
            // Mark the extra field as loaded
            if (plugin.scsLoadedExtraFields.indexOf(field) === -1) {
                plugin.scsLoadedExtraFields.push(field);
            }
        });
        await Promise.all(extraFieldPromises);

        return plugin;
    }

    /**
     * Apply the given data to the plugin, without overwriting any extra fields that were
     * previously loaded and stored in the plugin using 'loadExtraPluginFields()'.
     *
     * @param {Object} plugin
     * @param {Object} data
     */
    updatePluginData(plugin, data) {
        Object.keys(data).forEach((key) => {
            // Only overwrite the field, if it is not a loaded extra field
            if (plugin[key] && plugin.scsLoadedExtraFields && plugin.scsLoadedExtraFields.indexOf(key) === -1) {
                plugin[key] = data[key];
            }
        });
    }

    /**
     * @param {Object} plugin
     * @param {Object} lastSale to compare against for db sync - optional
     * @return {Array}
     */
    async getSalesForPlugin(plugin, lastSale) {
        const limit = 100;
        const sales = [];
        const accountData = await this.getAccountData();
        let currOffset = 0;
        let tmpArr = [];
        let exit = false;
        do {
            // don't get all sales concurrently as we might skip just after the first request
            // eslint-disable-next-line no-await-in-loop
            tmpArr = await this.client.get('pluginsales/list', {
                params: {
                    offset: currOffset,
                    limit,
                    producerId: accountData.producerId,
                    pluginId: plugin.id,
                },
            });
            tmpArr = tmpArr.data;
            for (let i = 0; i < tmpArr.length; i += 1) {
                const sale = tmpArr[i];
                if (lastSale &&
                    new Date(sale.creationDate).toISOString() === lastSale.bookingDate.toISOString() &&
                    (sale.priceModel.price ? sale.priceModel.price : 0) === lastSale.amount &&
                    sale.id === lastSale.shopwareId &&
                    sale.priceModel.type === lastSale.type) {
                    sales.push(sale);
                    exit = true;
                    break;
                } else {
                    sales.push(sale);
                }
            }
            currOffset += limit;
        } while (tmpArr.length !== 0 && !exit);

        return sales;
    }

    /**
     * @param {Object} plugin
     * @param {Object} lastCommission to compare against for db sync - optional
     * @return {Array}
     */
    async getCommissionsForPlugin(plugin, lastCommission) {
        const limit = 100;
        const commissions = [];
        const accountData = await this.getAccountData();
        let currOffset = 0;
        let tmpArr = [];
        let exit = false;
        do {
            // don't get all sales concurrently as we might skip just after the first request
            // shopware calls commissions brokerages
            // eslint-disable-next-line no-await-in-loop
            tmpArr = await this.client.get('pluginbrokerages/list', {
                params: {
                    offset: currOffset,
                    limit,
                    producerId: accountData.producerId,
                    pluginId: plugin.id,
                },
            });
            tmpArr = tmpArr.data.list;
            for (let i = 0; i < tmpArr.length; i += 1) {
                const commission = tmpArr[i];
                if (lastCommission &&
                    new Date(commission.bookingDate).toISOString() === lastCommission.bookingDate.toISOString() &&
                    commission.amount === lastCommission.amount &&
                    commission.orderNumber === lastCommission.orderNumber &&
                    commission.text === lastCommission.text &&
                    commission.type.name === lastCommission.type &&
                    commission.domain === lastCommission.domain) {
                    commissions.push(commission);
                    exit = true;
                    break;
                } else {
                    commissions.push(commission);
                }
            }
            currOffset += limit;
        } while (tmpArr.length !== 0 && !exit);

        return commissions;
    }

};
<|endoftext|>


<commit_before>const throttle = require('throttle-debounce/throttle');
const shortid = require('shortid');
// const convert = require('convert-range');

(function() {
  const ws = new WebSocket('ws://' + location.hostname + ':' + location.port);
  const id = shortid.generate();

  if (!isController()) {
    ws.addEventListener('open', onOpenSocket);
    ws.addEventListener('message', onSocketMessage);
    document.body.addEventListener('mousemove', throttle(10, onMouseMove));

    document.querySelector('form').addEventListener('submit', onFormSubmit);

    document
      .querySelectorAll('[type=button]')
      .forEach(button => button.addEventListener('click', onButtonClick));

    document.querySelector('#reset').addEventListener('click', resetGame);

    function onFormSubmit(e) {
      e.preventDefault();

      ws.send(
        JSON.stringify({
          device: 'scoreboard',
          action: 'START_GAME',
          maxScore: Number(document.querySelector('input').value)
        })
      );
      return false;
    }

    function onOpenSocket() {
      console.log('Socket connected');
    }

    function onSocketMessage(event) {
      const data = JSON.parse(event.data);
      switch (data.action) {
        case 'SET_MAX_SCORE':
          document.querySelector('input').value = data.maxScore;
          break;
        case 'MOVE_MOUSE':
          if (data.id !== id) {
            const element = document.querySelector(
              `div[data-clientid="${data.id}"]`
            );
            if (!element) {
              document.body.innerHTML += `<div data-clientid="${data.id}"></div>`;
            }

            document.querySelector(
              `div[data-clientid="${data.id}"]`
            ).style.transform = `translate(${data.clientX}px, ${data.clientY}px)`;
            document.querySelector(
              `div[data-clientid="${data.id}"]`
            ).style.animation =
              'none'; // 💩
            setTimeout(() => {
              document.querySelector(
                `div[data-clientid="${data.id}"]`
              ).style.animation =
                'fadeout .5s linear forwards';
            }, 10);
          }
          break;
        case 'NEW_PLAYER':
          const playerList = document.querySelector('#players ul');
          const newPlayer = document.createElement('li');
          const playerType = document.createElement('img');
          const playerName = document.createElement('h2');
          const playerScore = document.createElement('span');
          const colors = data.player.color;

          newPlayer.style.backgroundColor = `rgb(${colors[1]}, ${colors[0]}, ${colors[2]})`;
          newPlayer
            .setAttribute('data-id', data.player.id)
            .setAttribute('data-type', data.player.type);

          playerType.src = `/${data.player.type}.png`;
          playerType.alt = `Player uses ${data.player.type}`;

          playerName.textContent = data.player.id;
          playerScore.textContent = data.player.score;

          newPlayer
            .appendChild(playerType)
            .appendChild(playerName)
            .appendChild(playerScore);
          playerList.appendChild(newPlayer);
          break;
        case 'START_GAME':
          document.querySelector('#players').insertAdjacentHTML('beforebegin', `<section id="score-goal">${data.maxScore}</section>`);

          document.body.classList.add('started');
          break;
        case 'UPDATE_SCORE':
          const player = document.querySelector(`#players li[data-id="${data.id}"]`);
          const score = player.querySelector('span');

          score.textContent = Number(score.textContent) + 1;
          break;
        case 'END_GAME':
          const players = document.querySelectorAll('#players li');

          document.body.classList.remove('started');
          document.body.classList.add('ended');

          players
            .forEach(player => {
              if (player.getAttribute('data-id') === String(data.winner.id)) {
                player.classList.add('won');
              } else {
                player.classList.add('lost');
              }
            });
        default:
          return false;
      }
    }

    function onMouseMove(e) {
      ws.send(
        JSON.stringify({
          device: 'scoreboard',
          id,
          action: 'MOVE_MOUSE',
          clientX: e.clientX,
          clientY: e.clientY,
          innerWidth: window.innerWidth,
          innerHeight: window.innerHeight
        })
      );
    }

    function onButtonClick(e) {
      let value = Number(document.querySelector('input').value);

      if (e.target.id === 'min') {
        value--;
      } else {
        value++;
      }

      document.querySelector('input').value = value;

      ws.send(
        JSON.stringify({
          device: 'scoreboard',
          id,
          action: 'SET_MAX_SCORE',
          maxScore: value
        })
      );
    }

    function resetGame() {
      window.location.replace('/reset');
    }
  } else {
    const btn = document.querySelector('#bigredbutton');
    const btnID = document.querySelector('h1').innerText;

    ws.addEventListener('open', onOpenSocket);
    ws.addEventListener('message', onSocketMessage);

    btn.addEventListener('click', updateScore);

    function onOpenSocket() {
      console.log('Socket connected');
      btn.disabled = true;
    }

    function onSocketMessage(event) {
      const data = JSON.parse(event.data);

      switch (data.action) {
        case 'START_GAME':
          if (document.querySelector('#score-goal')) {
            document.querySelector('#score-goal').textContent = data.maxScore;
          } else {
            document.body.insertAdjacentHTML('afterbegin', `<section id="score-goal">${data.maxScore}</section>`);
          }

          btn.disabled = false;
          document.body.classList.remove('lost');
          break;
        case 'END_GAME':
          btn.disabled = true;
          // btn.removeEventListener('click', updateScore);

          if (btnID !== data.winner.id) {
            document.querySelector('.controller').classList.add('lost');
          }
          break;
        default:
          return false;
      }
    }

    function updateScore() {
      ws.send(
        JSON.stringify({
          device: 'phone',
          action: 'UPDATE_SCORE',
          id: btnID
        })
      )
    }
  }

  function isController() {
    if (document.body.classList.contains('controller')) {
      return true;
    } else {
      return false;
    }
  }
})();
<commit_msg>Add classname for winning player as well<commit_after>const throttle = require('throttle-debounce/throttle');
const shortid = require('shortid');
// const convert = require('convert-range');

(function() {
  const ws = new WebSocket('ws://' + location.hostname + ':' + location.port);
  const id = shortid.generate();

  if (!isController()) {
    ws.addEventListener('open', onOpenSocket);
    ws.addEventListener('message', onSocketMessage);
    document.body.addEventListener('mousemove', throttle(10, onMouseMove));

    document.querySelector('form').addEventListener('submit', onFormSubmit);

    document
      .querySelectorAll('[type=button]')
      .forEach(button => button.addEventListener('click', onButtonClick));

    document.querySelector('#reset').addEventListener('click', resetGame);

    function onFormSubmit(e) {
      e.preventDefault();

      ws.send(
        JSON.stringify({
          device: 'scoreboard',
          action: 'START_GAME',
          maxScore: Number(document.querySelector('input').value)
        })
      );
      return false;
    }

    function onOpenSocket() {
      console.log('Socket connected');
    }

    function onSocketMessage(event) {
      const data = JSON.parse(event.data);
      switch (data.action) {
        case 'SET_MAX_SCORE':
          document.querySelector('input').value = data.maxScore;
          break;
        case 'MOVE_MOUSE':
          if (data.id !== id) {
            const element = document.querySelector(
              `div[data-clientid="${data.id}"]`
            );
            if (!element) {
              document.body.innerHTML += `<div data-clientid="${data.id}"></div>`;
            }

            document.querySelector(
              `div[data-clientid="${data.id}"]`
            ).style.transform = `translate(${data.clientX}px, ${data.clientY}px)`;
            document.querySelector(
              `div[data-clientid="${data.id}"]`
            ).style.animation =
              'none'; // 💩
            setTimeout(() => {
              document.querySelector(
                `div[data-clientid="${data.id}"]`
              ).style.animation =
                'fadeout .5s linear forwards';
            }, 10);
          }
          break;
        case 'NEW_PLAYER':
          const playerList = document.querySelector('#players ul');
          const newPlayer = document.createElement('li');
          const playerType = document.createElement('img');
          const playerName = document.createElement('h2');
          const playerScore = document.createElement('span');
          const colors = data.player.color;

          newPlayer.style.backgroundColor = `rgb(${colors[1]}, ${colors[0]}, ${colors[2]})`;
          newPlayer
            .setAttribute('data-id', data.player.id)
            .setAttribute('data-type', data.player.type);

          playerType.src = `/${data.player.type}.png`;
          playerType.alt = `Player uses ${data.player.type}`;

          playerName.textContent = data.player.id;
          playerScore.textContent = data.player.score;

          newPlayer
            .appendChild(playerType)
            .appendChild(playerName)
            .appendChild(playerScore);
          playerList.appendChild(newPlayer);
          break;
        case 'START_GAME':
          document.querySelector('#players').insertAdjacentHTML('beforebegin', `<section id="score-goal">${data.maxScore}</section>`);

          document.body.classList.add('started');
          break;
        case 'UPDATE_SCORE':
          const player = document.querySelector(`#players li[data-id="${data.id}"]`);
          const score = player.querySelector('span');

          score.textContent = Number(score.textContent) + 1;
          break;
        case 'END_GAME':
          const players = document.querySelectorAll('#players li');

          document.body.classList.remove('started');
          document.body.classList.add('ended');

          players
            .forEach(player => {
              if (player.getAttribute('data-id') === String(data.winner.id)) {
                player.classList.add('won');
              } else {
                player.classList.add('lost');
              }
            });
        default:
          return false;
      }
    }

    function onMouseMove(e) {
      ws.send(
        JSON.stringify({
          device: 'scoreboard',
          id,
          action: 'MOVE_MOUSE',
          clientX: e.clientX,
          clientY: e.clientY,
          innerWidth: window.innerWidth,
          innerHeight: window.innerHeight
        })
      );
    }

    function onButtonClick(e) {
      let value = Number(document.querySelector('input').value);

      if (e.target.id === 'min') {
        value--;
      } else {
        value++;
      }

      document.querySelector('input').value = value;

      ws.send(
        JSON.stringify({
          device: 'scoreboard',
          id,
          action: 'SET_MAX_SCORE',
          maxScore: value
        })
      );
    }

    function resetGame() {
      window.location.replace('/reset');
    }
  } else {
    const btn = document.querySelector('#bigredbutton');
    const btnID = document.querySelector('h1').innerText;

    ws.addEventListener('open', onOpenSocket);
    ws.addEventListener('message', onSocketMessage);

    btn.addEventListener('click', updateScore);

    function onOpenSocket() {
      console.log('Socket connected');
      btn.disabled = true;
    }

    function onSocketMessage(event) {
      const data = JSON.parse(event.data);

      switch (data.action) {
        case 'START_GAME':
          if (document.querySelector('#score-goal')) {
            document.querySelector('#score-goal').textContent = data.maxScore;
          } else {
            document.body.insertAdjacentHTML('afterbegin', `<section id="score-goal">${data.maxScore}</section>`);
          }

          btn.disabled = false;
          document.body.classList.remove('lost');
          break;
        case 'END_GAME':
          btn.disabled = true;

          if (btnID === data.winner.id) {
            document.querySelector('.controller').classList.add('won');
          } else {
            document.querySelector('.controller').classList.add('lost');
          }
          break;
        default:
          return false;
      }
    }

    function updateScore() {
      ws.send(
        JSON.stringify({
          device: 'phone',
          action: 'UPDATE_SCORE',
          id: btnID
        })
      )
    }
  }

  function isController() {
    if (document.body.classList.contains('controller')) {
      return true;
    } else {
      return false;
    }
  }
})();
<|endoftext|>


<commit_before>import angular from 'angular';
import ngRoute from 'angular-route';

import auth from './js/controllers/auth';
import routeProvider from './js/providers/route-provider';

'use strict';
angular.module( 'app', [ 'ngRoute' ] );

angular.module( 'app' )
  .config( routeProvider );
<commit_msg>[REMOVED]<commit_after><|endoftext|>


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

'strict mode'

const assert = require('assert')
const request = require('request')
const autoUpdater = require('auto-updater')
const appConfig = require('../js/constants/appConfig')
const messages = require('../js/constants/messages')
const UpdateStatus = require('../js/constants/updateStatus')
const querystring = require('querystring')
const AppStore = require('../js/stores/appStore')
const appActions = require('../js/actions/appActions')
const Immutable = require('immutable')
const dates = require('./dates')
const Channel = require('./channel')

const fs = require('fs')
const path = require('path')
const app = require('app')
const os = require('os')
const updateLogPath = path.join(app.getPath('userData'), 'updateLog.log')

// in built mode console.log output is not emitted to the terminal
// in prod mode we pipe to a file
var debug = function (contents) {
  fs.appendFile(updateLogPath, new Date().toISOString() + ' - ' + contents + os.EOL)
}

// this maps the result of a call to process.platform to an update API identifier
var platforms = {
  'darwin': 'osx',
  'win32x64': 'winx64',
  'win32ia32': 'winia32'
}

// We are storing this as a package variable because a number of functions need access
// It is set in the init function
var platformBaseUrl = null
var version = null

// build the complete update url from the base, platform and version
exports.updateUrl = function (updates, platform, arch) {
  // For windows we need to separate x64 and ia32 for update purposes
  if (platform === 'win32') {
    platform = platform + arch
  }
  platformBaseUrl = `${updates.baseUrl}/${Channel.channel()}/${version}/${platforms[platform]}`
  debug(`platformBaseUrl = ${platformBaseUrl}`)
  if (platform === 'darwin') {
    return platformBaseUrl
  } else {
    if (platform.match(/^win32/)) {
      var windowsUpdateUrlWithArch = updates.winBaseUrl.replace('CHANNEL', Channel.channel()) + platforms[platform]
      return windowsUpdateUrlWithArch
    } else {
      // Unsupport platform for automatic updates
      return null
    }
  }
}

// Setup schedule for periodic and startup update checks
var scheduleUpdates = () => {
  // Periodic check
  if (appConfig.updates.appUpdateCheckFrequency) {
    setInterval(() => {
      exports.checkForUpdate()
    }, appConfig.updates.appUpdateCheckFrequency)
  }

  // Startup check
  if (appConfig.updates.runtimeUpdateCheckDelay) {
    setTimeout(() => {
      exports.checkForUpdate()
    }, appConfig.updates.runtimeUpdateCheckDelay)
  }
}

// set the feed url for the auto-update system
exports.init = (platform, arch, ver) => {
  // When starting up we should not expect an update to be available
  appActions.setUpdateStatus(UpdateStatus.UPDATE_NONE)

  // Browser version X.X.X
  version = ver

  var baseUrl = exports.updateUrl(appConfig.updates, platform, arch)
  debug(`updateUrl = ${baseUrl}`)

  scheduleUpdates()

  // This will fail if we are in dev
  try {
    autoUpdater.setFeedURL(baseUrl)
  } catch (err) {
    console.log(err)
  }
}

// Build a set of three params providing flags determining when the last update occurred
// This is a privacy preserving policy. Instead of passing personally identifying
// information, the browser will pass thefour boolean values indicating when the last
// update check occurred.
var paramsFromLastCheckDelta = (lastCheckYMD, lastCheckWOY, lastCheckMonth, firstCheckMade) => {
  return {
    daily: !lastCheckYMD || (dates.todayYMD() > lastCheckYMD),
    weekly: !lastCheckWOY || (dates.todayWOY() !== lastCheckWOY),
    monthly: !lastCheckMonth || (dates.todayMonth() !== lastCheckMonth),
    first: !firstCheckMade
  }
}

// Make a request to the update server to retrieve meta data
var requestVersionInfo = (done) => {
  if (!platformBaseUrl) throw new Error('platformBaseUrl not set')

  // Get the daily, week of year and month update checks
  var lastCheckYMD = AppStore.getState().toJS().updates['lastCheckYMD'] || null
  debug(`lastCheckYMD = ${lastCheckYMD}`)

  var lastCheckWOY = AppStore.getState().toJS().updates['lastCheckWOY'] || null
  debug(`lastCheckWOY = ${lastCheckWOY}`)

  var lastCheckMonth = AppStore.getState().toJS().updates['lastCheckMonth'] || null
  debug(`lastCheckMonth = ${lastCheckMonth}`)

  // Has the browser ever asked for an update
  var firstCheckMade = AppStore.getState().toJS().updates['firstCheckMade'] || false
  debug(`firstCheckMade = ${firstCheckMade}`)

  // Build query string based on the last date an update request was made
  var query = paramsFromLastCheckDelta(
    lastCheckYMD,
    lastCheckWOY,
    lastCheckMonth,
    firstCheckMade
  )
  var queryString = `${platformBaseUrl}?${querystring.stringify(query)}`
  debug(queryString)

  request(queryString, (err, response, body) => {
    appActions.setUpdateLastCheck()
    if (!err && response.statusCode === 204) {
      autoUpdater.emit(messages.UPDATE_NOT_AVAILABLE)
    } else if (!err && (response.statusCode === 200)) {
      if (body) {
        try {
          body = JSON.parse(body)
        } catch (error) {
          autoUpdater.emit('error', error)
        }
      }
      // This should be handled by a UI component for the update toolbar
      process.emit(messages.UPDATE_META_DATA_RETRIEVED, body)
      done(null, body)
    } else {
      // Network error or mis-configuration
      autoUpdater.emit('error', err)
    }
  })
}

var downloadHandler = (err, metadata) => {
  assert.equal(err, null)
  debug(`Metadata: ${JSON.stringify(metadata)}`)
  appActions.setUpdateStatus(undefined, undefined, Immutable.fromJS(metadata))
  if (process.platform === 'win32') {
    // check versions to see if an update is required
    if (metadata) {
      autoUpdater.checkForUpdates()
    } else {
      autoUpdater.emit(messages.UPDATE_NOT_AVAILABLE)
    }
  } else {
    autoUpdater.checkForUpdates()
  }
}

// Make network request to check for an available update
exports.checkForUpdate = (verbose) => {
  const updateStatus = AppStore.getState().getIn(['updates', 'status'])
  if (updateStatus !== UpdateStatus.UPDATE_ERROR &&
      updateStatus !== UpdateStatus.UPDATE_NOT_AVAILABLE &&
      updateStatus !== UpdateStatus.UPDATE_NONE) {
    debug('Already checking for updates...')
    appActions.setUpdateStatus(undefined, verbose)
    return
  }
  // Force falsy or truthy here so session store will write out a value
  // and it won't auto make updater UI appear periodically.
  appActions.setUpdateStatus(UpdateStatus.UPDATE_CHECKING, !!verbose)
  debug('checkForUpdates')
  try {
    requestVersionInfo(downloadHandler)
  } catch (err) {
    debug(err)
  }
}

// The UI indicates that we should update the software
exports.updateNowRequested = () => {
  debug('update requested in updater')
  // App shutdown process will save state and then call autoUpdater.quitAndInstall
  appActions.setUpdateStatus(UpdateStatus.UPDATE_APPLYING_RESTART)
}

exports.quitAndInstall = () => {
  autoUpdater.quitAndInstall()
}

// The download is complete, we send a signal and await UI
autoUpdater.on('update-downloaded', (evt, releaseNotes, releaseDate, updateURL) => {
  debug('update downloaded')
  if (releaseNotes) {
    debug(`Release notes : ${releaseNotes}`)
  }
  if (releaseDate) {
    debug(`Release date: ${releaseDate}`)
  }
  if (updateURL) {
    debug(`Update URL: ${updateURL}`)
  }
  appActions.setUpdateStatus(UpdateStatus.UPDATE_AVAILABLE)
})

// Download has started
autoUpdater.on(messages.UPDATE_AVAILABLE, (evt) => {
  debug('update downloading')
  appActions.setUpdateStatus(UpdateStatus.UPDATE_DOWNLOADING)
})

// The current version of the software is up to date
autoUpdater.on(messages.UPDATE_NOT_AVAILABLE, (evt) => {
  debug('update not available')
  appActions.setUpdateStatus(UpdateStatus.UPDATE_NOT_AVAILABLE)
})

// Handle autoUpdater errors (Network, permissions etc...)
autoUpdater.on('error', (err) => {
  appActions.setUpdateStatus(UpdateStatus.UPDATE_ERROR)
  debug(err)
})
<commit_msg>Continue to send update requests when a staged<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

'strict mode'

const assert = require('assert')
const request = require('request')
const autoUpdater = require('auto-updater')
const appConfig = require('../js/constants/appConfig')
const messages = require('../js/constants/messages')
const UpdateStatus = require('../js/constants/updateStatus')
const querystring = require('querystring')
const AppStore = require('../js/stores/appStore')
const appActions = require('../js/actions/appActions')
const Immutable = require('immutable')
const dates = require('./dates')
const Channel = require('./channel')

const fs = require('fs')
const path = require('path')
const app = require('app')
const os = require('os')
const updateLogPath = path.join(app.getPath('userData'), 'updateLog.log')

// in built mode console.log output is not emitted to the terminal
// in prod mode we pipe to a file
var debug = function (contents) {
  fs.appendFile(updateLogPath, new Date().toISOString() + ' - ' + contents + os.EOL)
}

// this maps the result of a call to process.platform to an update API identifier
var platforms = {
  'darwin': 'osx',
  'win32x64': 'winx64',
  'win32ia32': 'winia32'
}

// We are storing this as a package variable because a number of functions need access
// It is set in the init function
var platformBaseUrl = null
var version = null

// build the complete update url from the base, platform and version
exports.updateUrl = function (updates, platform, arch) {
  // For windows we need to separate x64 and ia32 for update purposes
  if (platform === 'win32') {
    platform = platform + arch
  }
  platformBaseUrl = `${updates.baseUrl}/${Channel.channel()}/${version}/${platforms[platform]}`
  debug(`platformBaseUrl = ${platformBaseUrl}`)
  if (platform === 'darwin') {
    return platformBaseUrl
  } else {
    if (platform.match(/^win32/)) {
      var windowsUpdateUrlWithArch = updates.winBaseUrl.replace('CHANNEL', Channel.channel()) + platforms[platform]
      return windowsUpdateUrlWithArch
    } else {
      // Unsupport platform for automatic updates
      return null
    }
  }
}

// Setup schedule for periodic and startup update checks
var scheduleUpdates = () => {
  // Periodic check
  if (appConfig.updates.appUpdateCheckFrequency) {
    setInterval(() => {
      exports.checkForUpdate()
    }, appConfig.updates.appUpdateCheckFrequency)
  }

  // Startup check
  if (appConfig.updates.runtimeUpdateCheckDelay) {
    setTimeout(() => {
      exports.checkForUpdate()
    }, appConfig.updates.runtimeUpdateCheckDelay)
  }
}

// set the feed url for the auto-update system
exports.init = (platform, arch, ver) => {
  // When starting up we should not expect an update to be available
  appActions.setUpdateStatus(UpdateStatus.UPDATE_NONE)

  // Browser version X.X.X
  version = ver

  var baseUrl = exports.updateUrl(appConfig.updates, platform, arch)
  debug(`updateUrl = ${baseUrl}`)

  scheduleUpdates()

  // This will fail if we are in dev
  try {
    autoUpdater.setFeedURL(baseUrl)
  } catch (err) {
    console.log(err)
  }
}

// Build a set of three params providing flags determining when the last update occurred
// This is a privacy preserving policy. Instead of passing personally identifying
// information, the browser will pass thefour boolean values indicating when the last
// update check occurred.
var paramsFromLastCheckDelta = (lastCheckYMD, lastCheckWOY, lastCheckMonth, firstCheckMade) => {
  return {
    daily: !lastCheckYMD || (dates.todayYMD() > lastCheckYMD),
    weekly: !lastCheckWOY || (dates.todayWOY() !== lastCheckWOY),
    monthly: !lastCheckMonth || (dates.todayMonth() !== lastCheckMonth),
    first: !firstCheckMade
  }
}

// Make a request to the update server to retrieve meta data
var requestVersionInfo = (done, pingOnly) => {
  if (!platformBaseUrl) throw new Error('platformBaseUrl not set')

  // Get the daily, week of year and month update checks
  var lastCheckYMD = AppStore.getState().toJS().updates['lastCheckYMD'] || null
  debug(`lastCheckYMD = ${lastCheckYMD}`)

  var lastCheckWOY = AppStore.getState().toJS().updates['lastCheckWOY'] || null
  debug(`lastCheckWOY = ${lastCheckWOY}`)

  var lastCheckMonth = AppStore.getState().toJS().updates['lastCheckMonth'] || null
  debug(`lastCheckMonth = ${lastCheckMonth}`)

  // Has the browser ever asked for an update
  var firstCheckMade = AppStore.getState().toJS().updates['firstCheckMade'] || false
  debug(`firstCheckMade = ${firstCheckMade}`)

  // Build query string based on the last date an update request was made
  var query = paramsFromLastCheckDelta(
    lastCheckYMD,
    lastCheckWOY,
    lastCheckMonth,
    firstCheckMade
  )
  var queryString = `${platformBaseUrl}?${querystring.stringify(query)}`
  debug(queryString)

  request(queryString, (err, response, body) => {
    if (pingOnly) {
      return
    }
    appActions.setUpdateLastCheck()
    if (!err && response.statusCode === 204) {
      autoUpdater.emit(messages.UPDATE_NOT_AVAILABLE)
    } else if (!err && (response.statusCode === 200)) {
      if (body) {
        try {
          body = JSON.parse(body)
        } catch (error) {
          autoUpdater.emit('error', error)
        }
      }
      // This should be handled by a UI component for the update toolbar
      process.emit(messages.UPDATE_META_DATA_RETRIEVED, body)
      done(null, body)
    } else {
      // Network error or mis-configuration
      autoUpdater.emit('error', err)
    }
  })
}

var downloadHandler = (err, metadata) => {
  assert.equal(err, null)
  debug(`Metadata: ${JSON.stringify(metadata)}`)
  appActions.setUpdateStatus(undefined, undefined, Immutable.fromJS(metadata))
  if (process.platform === 'win32') {
    // check versions to see if an update is required
    if (metadata) {
      autoUpdater.checkForUpdates()
    } else {
      autoUpdater.emit(messages.UPDATE_NOT_AVAILABLE)
    }
  } else {
    autoUpdater.checkForUpdates()
  }
}

// Make network request to check for an available update
exports.checkForUpdate = (verbose) => {
  const updateStatus = AppStore.getState().getIn(['updates', 'status'])
  if (updateStatus !== UpdateStatus.UPDATE_ERROR &&
      updateStatus !== UpdateStatus.UPDATE_NOT_AVAILABLE &&
      updateStatus !== UpdateStatus.UPDATE_NONE) {
    debug('Already checking for updates...')
    requestVersionInfo(undefined, true)
    appActions.setUpdateStatus(undefined, verbose)
    return
  }
  // Force falsy or truthy here so session store will write out a value
  // and it won't auto make updater UI appear periodically.
  appActions.setUpdateStatus(UpdateStatus.UPDATE_CHECKING, !!verbose)
  debug('checkForUpdates')
  try {
    requestVersionInfo(downloadHandler, false)
  } catch (err) {
    debug(err)
  }
}

// The UI indicates that we should update the software
exports.updateNowRequested = () => {
  debug('update requested in updater')
  // App shutdown process will save state and then call autoUpdater.quitAndInstall
  appActions.setUpdateStatus(UpdateStatus.UPDATE_APPLYING_RESTART)
}

exports.quitAndInstall = () => {
  autoUpdater.quitAndInstall()
}

// The download is complete, we send a signal and await UI
autoUpdater.on('update-downloaded', (evt, releaseNotes, releaseDate, updateURL) => {
  debug('update downloaded')
  if (releaseNotes) {
    debug(`Release notes : ${releaseNotes}`)
  }
  if (releaseDate) {
    debug(`Release date: ${releaseDate}`)
  }
  if (updateURL) {
    debug(`Update URL: ${updateURL}`)
  }
  appActions.setUpdateStatus(UpdateStatus.UPDATE_AVAILABLE)
})

// Download has started
autoUpdater.on(messages.UPDATE_AVAILABLE, (evt) => {
  debug('update downloading')
  appActions.setUpdateStatus(UpdateStatus.UPDATE_DOWNLOADING)
})

// The current version of the software is up to date
autoUpdater.on(messages.UPDATE_NOT_AVAILABLE, (evt) => {
  debug('update not available')
  appActions.setUpdateStatus(UpdateStatus.UPDATE_NOT_AVAILABLE)
})

// Handle autoUpdater errors (Network, permissions etc...)
autoUpdater.on('error', (err) => {
  appActions.setUpdateStatus(UpdateStatus.UPDATE_ERROR)
  debug(err)
})
<|endoftext|>


<commit_before>// @flow
/*jshint esversion: 6 */
/*jslint node: true */
"use strict";

const {DateTime} = require("luxon");

function assert(condition, message) {
    if (!condition)
        throw new Error(message || "Assertion failed");
}

/**
 * Like `document.querySelectorAll()`, but queries shadow roots and template
 * contents too and returns an `Array` of nodes instead of a `NodeList`.
 *
 * @param {string} selector - selector string
 * @returns {Array} array of matched nodes
 */
function querySelectorAllDeep(selector, startRoot = document) {
    const roots = [startRoot];

    const nodeQueue = [... startRoot.children];
    while (nodeQueue.length) {
        const node = nodeQueue.shift();
        if (node.shadowRoot)
            roots.push(node.shadowRoot);
        if (node.tagName === "TEMPLATE" && node.content)
            roots.push(node.content);
        nodeQueue.push(... node.children);
    }

    const matches = [];
    for (const r of roots)
        matches.push(... r.querySelectorAll(selector));
    return matches;
}

function deepClone(obj) {
    // feel free to make a better implementation
    if (!obj)
        return null;
    return JSON.parse(JSON.stringify(obj));
}

/**
 * Shows a OK/Cancel message box with `msg`. Executes `ifOk` lambda if user
 * presses OK or executes `onCancel` if it is defined and user presses Cancel.
 *
 * @param {string} msg - message for the user
 * @param {function} onOk - lambda executed if OK is pressed
 * @param {function=} onOk - lambda executed if Cancel is pressed
 */
function warnUser(msg, onOk, onCancel) {
    if (confirm(msg))
        onOk();
    else if (onCancel)
        onCancel();
}

function logout() {
    ipcRenderer.send("do-logout");
    location.href = "./login.html";
}

function exitApp() {
    ipcRenderer.send("exit-from-menu");
}

function openUrl(url) {
    const {shell} = require("electron");
    shell.openExternal(url);
}

function fixLinks(parent = document) {
    querySelectorAllDeep("a[href^='http']", parent).forEach(link =>
        link.addEventListener("click", event => {
            event.preventDefault();
            openUrl(link.href);
        }));
}

function fixAmountInputs(parent = document) {
    querySelectorAllDeep(".amountInput", parent).forEach(node => {
        function updateBalanceText() { node.value = formatBalance(node.value) }
        updateBalanceText();
        node.addEventListener("change", () => updateBalanceText());
    });
}

function fixPage(parent = document) {
    fixLinks(parent);
    // fixAmountInputs(parent);
}

/**
 * Sets `<input>` node's value and also fires the change event. Needed for
 * amount nodes which are currently hacked by reformatting their contents on
 * change.
 *
 * @param {Node} node
 * @param {string} value
 */
function setInputNodeValue(node, value) {
    node.value = value;
    node.dispatchEvent(new Event("change"));
}

function formatBalance(balance) {
    return parseFloat(balance).toLocaleString(undefined, {minimumFractionDigits: 8, maximumFractionDigits: 8});
}

function formatFiatBalance(balance) {
    return parseFloat(balance).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}

function formatEpochTime(epochSeconds) {
    return DateTime.fromMillis(epochSeconds).toLocaleString(DateTime.DATETIME_MED);
}

function hideElement(node, yes) {
    if (yes) {
        node.classList.add("hidden");
    } else {
        node.classList.remove("hidden");
    }
}

function clearChildNodes(parent) {
    parent.childNodes.forEach(p => parent.removeChild(p));
}

function cloneTemplate(id) {
    const templateNode = document.getElementById(id);
    if (!templateNode)
        throw new Error(`No template with ID "${id}"`);
    const node = templateNode.content.cloneNode(true).firstElementChild;
    if (!node)
        throw new Error(`Template is empty (ID "${id}")`);
    fixPage(node);
    return node;
}

/**
 * Creates dialog from a template and adds it to the DOM.
 *
 * WARNING! You have to call close() on the retunred dialog otherwise it'll stay
 * in the DOM. The close event handler will automatically remove it from DOM. If
 * you use `id` attributes on slotted contents and forget to remove old dialog
 * before creating a new one from the same template, this will result in
 * duplicate IDs in the DOM. Maybe. To be honest, I (woky) don't know how IDs on
 * slotted contents in shadow DOM work. Feel free to experiment and update this
 * comment.
 *
 * @param templateId id of the template
 * @returns the `<arizen-dialog>` node of the created dialog
 */
function createDialogFromTemplate(templateId) {
    const dialog = cloneTemplate(templateId);
    if (dialog.tagName !== "ARIZEN-DIALOG")
        throw new Error("No dialog in the template");
    document.body.appendChild(dialog);
    dialog.addEventListener("close", () => dialog.remove());
    return dialog;
}

function showDialogFromTemplate(templateId, dialogInit, onClose = null) {
    const dialog = createDialogFromTemplate(templateId);
    dialogInit(dialog);
    if (onClose)
        dialog.addEventListener("close", () => onClose());
    dialog.showModal();
}

function scrollIntoViewIfNeeded(parent, child) {
    const parentRect = parent.getBoundingClientRect();
    const childRect = child.getBoundingClientRect();
    if (childRect.top < parentRect.top ||
        childRect.right > parentRect.right ||
        childRect.bottom > parentRect.bottom ||
        childRect.left < parentRect.left)
        child.scrollIntoView();
}

function createLink(url, text) {
    const link = document.createElement("a");
    link.href = url;
    link.textContent = text;
    return link;
}

// TODO this doesn't belong here
function showAboutDialog() {
    const pkg = require("../package.json");
    showDialogFromTemplate("aboutDialogTemplate", dialog => {
        dialog.querySelector(".aboutHomepage").appendChild(createLink(pkg.homepage, pkg.homepage));
        dialog.querySelector(".aboutVersion").textContent = pkg.version;
        dialog.querySelector(".aboutLicense").textContent = pkg.license;
        const authorsNode = dialog.querySelector(".aboutAuthors");
        pkg.contributors.forEach(function (person) {
            const row = document.createElement("div");
            row.textContent = person.name;
            if (/@/.test(person.email)) {
                row.textContent += " ";
                row.appendChild(createLink("mailto: " + person.email, person.email));
            }
            authorsNode.appendChild(row);
        });
    });
}

// TODO this doesn't belong here
let settings;
let langDict;
(() => {
    const {ipcRenderer} = require("electron");
    ipcRenderer.on("settings", (sender, settingsStr) => {
        settings = JSON.parse(settingsStr);
        loadLang();
        translateCurrentPage();
        setMenuLang();
    });
})();

function saveModifiedSettings() {
    ipcRenderer.send("save-settings", JSON.stringify(settings));
}

function showSettingsDialog() {
    showDialogFromTemplate("settingsDialogTemplate", dialog => {
        const inputTxHistory = dialog.querySelector(".settingsTxHistory");
        const inputExplorerUrl = dialog.querySelector(".settingsExplorerUrl");
        const inputApiUrls = dialog.querySelector(".settingsApiUrls");
        const inputFiatCurrency = dialog.querySelector(".settingsFiatCurrency");
        const inputLanguages = dialog.querySelector(".settingsLanguage");

        inputTxHistory.value = settings.txHistory;
        inputExplorerUrl.value = settings.explorerUrl;
        loadAvailableLangs(inputLanguages, settings.lang);
        inputApiUrls.value = settings.apiUrls.join("\n");
        inputFiatCurrency.value = settings.fiatCurrency;

        // An existing user has empty value settings.fiatCurrency
        if (settings.fiatCurrency === "" || settings.fiatCurrency === undefined || settings.fiatCurrency === null) {
            inputFiatCurrency.value = "USD";
        }
        console.log(settings);

        dialog.querySelector(".settingsSave").addEventListener("click", () => {

            Object.assign(settings, {
                txHistory: parseInt(inputTxHistory.value),
                explorerUrl: inputExplorerUrl.value.trim().replace(/\/?$/, ""),
                apiUrls: inputApiUrls.value.split(/\s+/).filter(s => !/^\s*$/.test(s)).map(s => s.replace(/\/?$/, "")),
                fiatCurrency: inputFiatCurrency.value,
                lang: inputLanguages[inputLanguages.selectedIndex].value
            });
            saveModifiedSettings();
            let zenBalance = getZenBalance();
            setFiatBalanceText(zenBalance, inputFiatCurrency.value);

            dialog.close();
        });
    });
}

function setMenuLang() {
    if (!langDict)
        return;
    if (!langDict.menu)
        return;
    console.log(langDict.menu);
    ipcRenderer.send("set-menu",JSON.stringify(langDict.menu));
}

function openZenExplorer(path) {
    openUrl(settings.explorerUrl + "/" + path);
}

function getZenBalance(){
    const totalBalanceAmountNode = document.getElementById("totalBalanceAmount");
    return formatBalance(parseFloat(totalBalanceAmountNode.innerHTML));
}

function loadAvailableLangs(select, selected) {
    const fs = require("fs");
    fs.readdir(__dirname + "/lang", (err, files) => {
        if (err) {
            console.log(err);
            return;
        }
        files.forEach(file => {
            let tempLangData = require("./lang/" + file);
            let opt = document.createElement("option");
            opt.value = tempLangData.languageValue;
            opt.innerHTML = tempLangData.languageName;
            if (tempLangData.languageValue === selected) {
                opt.selected = true;
            }
            select.appendChild(opt);
        });
    });
}

function loadLang() {
    if (!settings.lang)
        return;
    // TODO: there can be invalid language in DB, fail gracefully
    langDict = require("./lang/lang_" + settings.lang + ".json");
}

function tr(key, defaultVal) {
    if (!langDict)
        return defaultVal;

    function iter(dict, trPath) {
        switch (typeof(dict)) {
            case "object":
                if (trPath.length)
                    return iter(dict[trPath[0]], trPath.slice(1));
                break;
            case "string":
                if (!trPath.length)
                    return dict;
                break;
        }
        console.warn("Untranslated key: " + key);
        return defaultVal;
    }

    return iter(langDict, key.split("."));
}

/**
 * Sets `node`'s `textContent` to a translated text based on the translation
 * key `key` and current language setting or to the `defaultVal` if the `key`
 * is not translated. Also sets node's `data-tr` attribute to the `key`.
 * If the `key` is null, only `defaultVal` is used and `data-tr` attribute is
 * removed.
 *
 * @param {Node} node node to which set the translated text (`<span>`/`<div>`)
 * @param {string} key translation key
 * @param {string} defaultVal default text
 */
function setNodeTrText(node, key, defaultVal) {
    if (key) {
        node.dataset.tr = key;
        node.textContent = tr(key, defaultVal);
    } else {
        delete node.dataset.tr;
        node.textContent = defaultVal;
    }
}

function translateCurrentPage() {
    if (!langDict)
        return;
    querySelectorAllDeep("[data-tr]").forEach(node =>
        node.textContent = tr(node.dataset.tr, node.textContent));
}
<commit_msg>Read double value (valueAsNumber) from number inputs<commit_after>// @flow
/*jshint esversion: 6 */
/*jslint node: true */
"use strict";

const {DateTime} = require("luxon");

function assert(condition, message) {
    if (!condition)
        throw new Error(message || "Assertion failed");
}

/**
 * Like `document.querySelectorAll()`, but queries shadow roots and template
 * contents too and returns an `Array` of nodes instead of a `NodeList`.
 *
 * @param {string} selector - selector string
 * @returns {Array} array of matched nodes
 */
function querySelectorAllDeep(selector, startRoot = document) {
    const roots = [startRoot];

    const nodeQueue = [... startRoot.children];
    while (nodeQueue.length) {
        const node = nodeQueue.shift();
        if (node.shadowRoot)
            roots.push(node.shadowRoot);
        if (node.tagName === "TEMPLATE" && node.content)
            roots.push(node.content);
        nodeQueue.push(... node.children);
    }

    const matches = [];
    for (const r of roots)
        matches.push(... r.querySelectorAll(selector));
    return matches;
}

function deepClone(obj) {
    // feel free to make a better implementation
    if (!obj)
        return null;
    return JSON.parse(JSON.stringify(obj));
}

/**
 * Shows a OK/Cancel message box with `msg`. Executes `ifOk` lambda if user
 * presses OK or executes `onCancel` if it is defined and user presses Cancel.
 *
 * @param {string} msg - message for the user
 * @param {function} onOk - lambda executed if OK is pressed
 * @param {function=} onOk - lambda executed if Cancel is pressed
 */
function warnUser(msg, onOk, onCancel) {
    if (confirm(msg))
        onOk();
    else if (onCancel)
        onCancel();
}

function logout() {
    ipcRenderer.send("do-logout");
    location.href = "./login.html";
}

function exitApp() {
    ipcRenderer.send("exit-from-menu");
}

function openUrl(url) {
    const {shell} = require("electron");
    shell.openExternal(url);
}

function fixLinks(parent = document) {
    querySelectorAllDeep("a[href^='http']", parent).forEach(link =>
        link.addEventListener("click", event => {
            event.preventDefault();
            openUrl(link.href);
        }));
}

function fixAmountInputs(parent = document) {
    querySelectorAllDeep(".amountInput", parent).forEach(node => {
        function updateBalanceText() { node.value = formatBalance(node.valueAsNumber) }
        updateBalanceText();
        node.addEventListener("change", () => updateBalanceText());
    });
}

function fixPage(parent = document) {
    fixLinks(parent);
    // fixAmountInputs(parent);
}

/**
 * Sets `<input>` node's value and also fires the change event. Needed for
 * amount nodes which are currently hacked by reformatting their contents on
 * change.
 *
 * @param {Node} node
 * @param {string} value
 */
function setInputNodeValue(node, value) {
    node.value = value;
    node.dispatchEvent(new Event("change"));
}

function formatBalance(balance) {
    return parseFloat(balance).toLocaleString(undefined, {minimumFractionDigits: 8, maximumFractionDigits: 8});
}

function formatFiatBalance(balance) {
    return parseFloat(balance).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2});
}

function formatEpochTime(epochSeconds) {
    return DateTime.fromMillis(epochSeconds).toLocaleString(DateTime.DATETIME_MED);
}

function hideElement(node, yes) {
    if (yes) {
        node.classList.add("hidden");
    } else {
        node.classList.remove("hidden");
    }
}

function clearChildNodes(parent) {
    parent.childNodes.forEach(p => parent.removeChild(p));
}

function cloneTemplate(id) {
    const templateNode = document.getElementById(id);
    if (!templateNode)
        throw new Error(`No template with ID "${id}"`);
    const node = templateNode.content.cloneNode(true).firstElementChild;
    if (!node)
        throw new Error(`Template is empty (ID "${id}")`);
    fixPage(node);
    return node;
}

/**
 * Creates dialog from a template and adds it to the DOM.
 *
 * WARNING! You have to call close() on the retunred dialog otherwise it'll stay
 * in the DOM. The close event handler will automatically remove it from DOM. If
 * you use `id` attributes on slotted contents and forget to remove old dialog
 * before creating a new one from the same template, this will result in
 * duplicate IDs in the DOM. Maybe. To be honest, I (woky) don't know how IDs on
 * slotted contents in shadow DOM work. Feel free to experiment and update this
 * comment.
 *
 * @param templateId id of the template
 * @returns the `<arizen-dialog>` node of the created dialog
 */
function createDialogFromTemplate(templateId) {
    const dialog = cloneTemplate(templateId);
    if (dialog.tagName !== "ARIZEN-DIALOG")
        throw new Error("No dialog in the template");
    document.body.appendChild(dialog);
    dialog.addEventListener("close", () => dialog.remove());
    return dialog;
}

function showDialogFromTemplate(templateId, dialogInit, onClose = null) {
    const dialog = createDialogFromTemplate(templateId);
    dialogInit(dialog);
    if (onClose)
        dialog.addEventListener("close", () => onClose());
    dialog.showModal();
}

function scrollIntoViewIfNeeded(parent, child) {
    const parentRect = parent.getBoundingClientRect();
    const childRect = child.getBoundingClientRect();
    if (childRect.top < parentRect.top ||
        childRect.right > parentRect.right ||
        childRect.bottom > parentRect.bottom ||
        childRect.left < parentRect.left)
        child.scrollIntoView();
}

function createLink(url, text) {
    const link = document.createElement("a");
    link.href = url;
    link.textContent = text;
    return link;
}

// TODO this doesn't belong here
function showAboutDialog() {
    const pkg = require("../package.json");
    showDialogFromTemplate("aboutDialogTemplate", dialog => {
        dialog.querySelector(".aboutHomepage").appendChild(createLink(pkg.homepage, pkg.homepage));
        dialog.querySelector(".aboutVersion").textContent = pkg.version;
        dialog.querySelector(".aboutLicense").textContent = pkg.license;
        const authorsNode = dialog.querySelector(".aboutAuthors");
        pkg.contributors.forEach(function (person) {
            const row = document.createElement("div");
            row.textContent = person.name;
            if (/@/.test(person.email)) {
                row.textContent += " ";
                row.appendChild(createLink("mailto: " + person.email, person.email));
            }
            authorsNode.appendChild(row);
        });
    });
}

// TODO this doesn't belong here
let settings;
let langDict;
(() => {
    const {ipcRenderer} = require("electron");
    ipcRenderer.on("settings", (sender, settingsStr) => {
        settings = JSON.parse(settingsStr);
        loadLang();
        translateCurrentPage();
        setMenuLang();
    });
})();

function saveModifiedSettings() {
    ipcRenderer.send("save-settings", JSON.stringify(settings));
}

function showSettingsDialog() {
    showDialogFromTemplate("settingsDialogTemplate", dialog => {
        const inputTxHistory = dialog.querySelector(".settingsTxHistory");
        const inputExplorerUrl = dialog.querySelector(".settingsExplorerUrl");
        const inputApiUrls = dialog.querySelector(".settingsApiUrls");
        const inputFiatCurrency = dialog.querySelector(".settingsFiatCurrency");
        const inputLanguages = dialog.querySelector(".settingsLanguage");

        inputTxHistory.value = settings.txHistory;
        inputExplorerUrl.value = settings.explorerUrl;
        loadAvailableLangs(inputLanguages, settings.lang);
        inputApiUrls.value = settings.apiUrls.join("\n");
        inputFiatCurrency.value = settings.fiatCurrency;

        // An existing user has empty value settings.fiatCurrency
        if (settings.fiatCurrency === "" || settings.fiatCurrency === undefined || settings.fiatCurrency === null) {
            inputFiatCurrency.value = "USD";
        }
        console.log(settings);

        dialog.querySelector(".settingsSave").addEventListener("click", () => {

            Object.assign(settings, {
                txHistory: parseInt(inputTxHistory.value),
                explorerUrl: inputExplorerUrl.value.trim().replace(/\/?$/, ""),
                apiUrls: inputApiUrls.value.split(/\s+/).filter(s => !/^\s*$/.test(s)).map(s => s.replace(/\/?$/, "")),
                fiatCurrency: inputFiatCurrency.value,
                lang: inputLanguages[inputLanguages.selectedIndex].value
            });
            saveModifiedSettings();
            let zenBalance = getZenBalance();
            setFiatBalanceText(zenBalance, inputFiatCurrency.value);

            dialog.close();
        });
    });
}

function setMenuLang() {
    if (!langDict)
        return;
    if (!langDict.menu)
        return;
    console.log(langDict.menu);
    ipcRenderer.send("set-menu",JSON.stringify(langDict.menu));
}

function openZenExplorer(path) {
    openUrl(settings.explorerUrl + "/" + path);
}

function getZenBalance(){
    const totalBalanceAmountNode = document.getElementById("totalBalanceAmount");
    return formatBalance(parseFloat(totalBalanceAmountNode.innerHTML));
}

function loadAvailableLangs(select, selected) {
    const fs = require("fs");
    fs.readdir(__dirname + "/lang", (err, files) => {
        if (err) {
            console.log(err);
            return;
        }
        files.forEach(file => {
            let tempLangData = require("./lang/" + file);
            let opt = document.createElement("option");
            opt.value = tempLangData.languageValue;
            opt.innerHTML = tempLangData.languageName;
            if (tempLangData.languageValue === selected) {
                opt.selected = true;
            }
            select.appendChild(opt);
        });
    });
}

function loadLang() {
    if (!settings.lang)
        return;
    // TODO: there can be invalid language in DB, fail gracefully
    langDict = require("./lang/lang_" + settings.lang + ".json");
}

function tr(key, defaultVal) {
    if (!langDict)
        return defaultVal;

    function iter(dict, trPath) {
        switch (typeof(dict)) {
            case "object":
                if (trPath.length)
                    return iter(dict[trPath[0]], trPath.slice(1));
                break;
            case "string":
                if (!trPath.length)
                    return dict;
                break;
        }
        console.warn("Untranslated key: " + key);
        return defaultVal;
    }

    return iter(langDict, key.split("."));
}

/**
 * Sets `node`'s `textContent` to a translated text based on the translation
 * key `key` and current language setting or to the `defaultVal` if the `key`
 * is not translated. Also sets node's `data-tr` attribute to the `key`.
 * If the `key` is null, only `defaultVal` is used and `data-tr` attribute is
 * removed.
 *
 * @param {Node} node node to which set the translated text (`<span>`/`<div>`)
 * @param {string} key translation key
 * @param {string} defaultVal default text
 */
function setNodeTrText(node, key, defaultVal) {
    if (key) {
        node.dataset.tr = key;
        node.textContent = tr(key, defaultVal);
    } else {
        delete node.dataset.tr;
        node.textContent = defaultVal;
    }
}

function translateCurrentPage() {
    if (!langDict)
        return;
    querySelectorAllDeep("[data-tr]").forEach(node =>
        node.textContent = tr(node.dataset.tr, node.textContent));
}
<|endoftext|>


<commit_before>/* SQB
 ------------------------
 (c) 2017-present Panates
 SQB may be freely distributed under the MIT license.
 For details and documentation:
 https://panates.github.io/sqb/
 */
'use strict';

/**
 * Module dependencies.
 * @private
 */
const {ArgumentError} = require('errorex');
const Serializable = require('../Serializable');

const TABLE_COLUMN_PATTERN = /^((?:[a-zA-Z_][\w$_]*\.){0,2}) *([a-zA-Z_][\w$_]*|\*) *(?:as)? *([a-zA-Z_][\w$_]*)?$/;

/**
 *
 * @class
 */
class SelectColumn extends Serializable {
  /**
   * @param {String} value
   * @constructor
   * @public
   */
  constructor(value) {
    super();
    this.type = 'column';
    const m = value.match(TABLE_COLUMN_PATTERN);
    if (!m)
      throw new ArgumentError('(%s) does not match table column format', value);
    this.field = m[2];
    if (m[1]) {
      const a = m[1].split(/\./g);
      a.pop();
      this.table = a.pop();
      this.schema = a.pop();
    }
    this.alias = this.field !== '*' ? m[3] : '';
  }

  /**
   * Performs serialization
   *
   * @param {Object} ctx
   * @return {string}
   * @override
   */
  _serialize(ctx) {
    const o = {
      schema: this.schema,
      table: this.table,
      field: this.field,
      alias: this.alias,
      isReservedWord: Serializable.isReserved(ctx, this.field)
    };
    return Serializable.serializeFallback(ctx, 'select_column', o, () => {
      return (this.schema ? this.schema + '.' : '') +
          (this.table ? this.table + '.' : '') +
          (o.isReservedWord ? '"' + this.field + '"' : this.field) +
          (this.alias ? ' ' + this.alias : '');
    });
  }

}

/**
 * Expose `SelectColumn`.
 */
module.exports = SelectColumn;
<commit_msg>Fixed: "select" query does not accept numbers as field<commit_after>/* SQB
 ------------------------
 (c) 2017-present Panates
 SQB may be freely distributed under the MIT license.
 For details and documentation:
 https://panates.github.io/sqb/
 */
'use strict';

/**
 * Module dependencies.
 * @private
 */
const {ArgumentError} = require('errorex');
const Serializable = require('../Serializable');

const TABLE_COLUMN_PATTERN = /^((?:[a-zA-Z_][\w$_]*\.){0,2}) *([0-9a-zA-Z_][\w$_]*|\*) *(?:as)? *([a-zA-Z_][\w$_]*)?$/;

/**
 *
 * @class
 */
class SelectColumn extends Serializable {
  /**
   * @param {String} value
   * @constructor
   * @public
   */
  constructor(value) {
    super();
    this.type = 'column';
    const m = value.match(TABLE_COLUMN_PATTERN);
    if (!m)
      throw new ArgumentError('(%s) does not match table column format', value);
    this.field = m[2];
    if (m[1]) {
      const a = m[1].split(/\./g);
      a.pop();
      this.table = a.pop();
      this.schema = a.pop();
    }
    this.alias = this.field !== '*' ? m[3] : '';
  }

  /**
   * Performs serialization
   *
   * @param {Object} ctx
   * @return {string}
   * @override
   */
  _serialize(ctx) {
    const o = {
      schema: this.schema,
      table: this.table,
      field: this.field,
      alias: this.alias,
      isReservedWord: Serializable.isReserved(ctx, this.field)
    };
    return Serializable.serializeFallback(ctx, 'select_column', o, () => {
      return (this.schema ? this.schema + '.' : '') +
          (this.table ? this.table + '.' : '') +
          (o.isReservedWord ? '"' + this.field + '"' : this.field) +
          (this.alias ? ' ' + this.alias : '');
    });
  }

}

/**
 * Expose `SelectColumn`.
 */
module.exports = SelectColumn;
<|endoftext|>


<commit_before>// require //
  //example:
  // customJsFile = require('./lib/customJsFile.js');

// require custom js files
var credentials = require('./credentials.js');

// require dependencies
var   express = require('express'),
    http = require('http'),
    https = require('https'),
    formidable = require('formidable'),
    Q = require('q'),
    mongoose = require('mongoose');

var app = express();

var handlebars = require('express-handlebars').create({
  defaultLayout:'main',
  helpers: {
    section: function(name, options){
      if(!this._sections) this._sections = {};
      this._sections[name] = options.fn(this);
      return null;
    },
    static: function(name) {
      return require('./lib/static.js').map(name);
    }
  }
});

// engines //
app.engine('handlebars', handlebars.engine);
app.set('view engine', 'handlebars');

// set port //
app.set('port', process.env.PORT || 3000);

// set location of assets //
app.use(express.static('public'));

// mount middleware //

// tests
app.use(function(req, res, next){
  res.locals.showTests = app.get('env') !== 'production' && req.query.test === '1';
  next();
});

// connect to database
var opts = {
  server: {
    socketOptions: { keepAlive: 1 }
  }
};
switch(app.get('env')){
  case 'development':
    mongoose.connect(credentials.mongo.development.connectionString, opts);
    break;
  case 'production':
    mongoose.connect(credentials.mongo.production.connectionString, opts);
    break;
  default:
    throw new Error('Unknown execution environment: ' + app.get('env'));
}
// error handling //
app.use(function(req, res, next){
  res.status(404).render('404');
});

app.use(function(err, req, res, next){
  console.error(err.stack);
  res.status(500).render('500');
});

// listen //
app.listen(app.get('port'), function(){
  console.log('express started on andre:' + app.get('port') + '; Ctrl+C to terminate');
});
<commit_msg>move requiring the routes further down the page. Debugging grunt throwing an 'assert not defined' error.<commit_after>// require //
  //example:
  // customJsFile = require('./lib/customJsFile.js');

// require custom js files
var credentials = require('./credentials.js');

// require dependencies
var   express = require('express'),
    http = require('http'),
    https = require('https'),
    formidable = require('formidable'),
    Q = require('q'),
    mongoose = require('mongoose');

var app = express();

var handlebars = require('express-handlebars').create({
  defaultLayout:'main',
  helpers: {
    section: function(name, options){
      if(!this._sections) this._sections = {};
      this._sections[name] = options.fn(this);
      return null;
    },
    static: function(name) {
      return require('./lib/static.js').map(name);
    }
  }
});

// engines //
app.engine('handlebars', handlebars.engine);
app.set('view engine', 'handlebars');

// set port //
app.set('port', process.env.PORT || 3000);

// set location of assets //
app.use(express.static('public'));

// mount middleware //

// tests
app.use(function(req, res, next){
  res.locals.showTests = app.get('env') !== 'production' && req.query.test === '1';
  next();
});

// connect to database
var opts = {
  server: {
    socketOptions: { keepAlive: 1 }
  }
};
switch(app.get('env')){
  case 'development':
    mongoose.connect(credentials.mongo.development.connectionString, opts);
    break;
  case 'production':
    mongoose.connect(credentials.mongo.production.connectionString, opts);
    break;
  default:
    throw new Error('Unknown execution environment: ' + app.get('env'));
}

//require routes
var routes = require('./routes.js')(app);

// error handling //
app.use(function(req, res, next){
  res.status(404).render('404');
});

app.use(function(err, req, res, next){
  console.error(err.stack);
  res.status(500).render('500');
});

// listen //
app.listen(app.get('port'), function(){
  console.log('express started on andre:' + app.get('port') + '; Ctrl+C to terminate');
});
<|endoftext|>