<commit_before>'use babel'

import {React} from 'react-for-atom'

export default React.createClass({

  propTypes: {
    readyCount: React.PropTypes.number,
    totalCount: React.PropTypes.number
  },

  render () {
    if (isNaN(this.props.readyCount) || isNaN(this.props.totalCount)) {
      return
    } else {
      return (

            Reading {this.props.readyCount} of {this.props.totalCount} notes

          <progress className='inline-block' max={this.props.totalCount} value={this.props.readyCount} />

)
    }
  }

})
<commit_msg>:art: Add info dialog for more re: file reads<commit_after>'use babel'

import {React} from 'react-for-atom'

export default React.createClass({

  propTypes: {
    readyCount: React.PropTypes.number,
    totalCount: React.PropTypes.number
  },

  render () {
    if (isNaN(this.props.readyCount) || isNaN(this.props.totalCount)) {
      return
    } else {
      return (

            Reading {this.props.readyCount} of {this.props.totalCount} notes

          <progress className='inline-block' max={this.props.totalCount} value={this.props.readyCount} />

)
    }
  },

  _onHover () {
    atom.notifications.addInfo('Reading files', {
      description: 'This is necesary to populate the search index. It is only necessary the first time, the notes will be cached for next session.',
      dismissible: true
    })
  }
})
<|endoftext|>


<commit_before>
var passport = require('passport');
var express = require('express');

function RouterPassport(conf, app){

  var failUrl = '/static/error/error.html';
  var router = express.Router();

    //local strategy (REST API JSON)
  router.route('/local').post(
    passport.authenticate('local',{ failureRedirect: failUrl,
        usernameField: 'username',
        passwordField: 'password',
        passReqToCallback: true

      }),
      function(req, res) {
        console.log("session id"+req.session.id);
        console.log("session cookie"+JSON.stringify(req.session.cookie));
        res.redirect('/static/idm/authenticateUser.html');
      }

  );

  //Github
  router.route('/github').get(
    passport.authenticate('github'),
    function(req, res){});
  router.route('/callback_github').get(
    passport.authenticate('github', { failureRedirect: failUrl }),
    function(req, res) {
      console.log("session id"+req.session.id);
      console.log("session cookie"+JSON.stringify(req.session.cookie));
      res.redirect('/static/index.html');
    });

  //Google
  router.route('/google').get(
    passport.authenticate('google', {  }
  ));
  //TODO place into router later! Fix README first!
  app.get/*router.route*/('/callback_google'/*).get(*/,
    passport.authenticate('google', { failureRedirect: failUrl }),
    function(req, res) {
      console.log("session id"+req.session.id);
      console.log("session cookie"+JSON.stringify(req.session.cookie));
      res.redirect('/static/index.html');
  });

  router.route('/web_id').get(
     //TODO adjust to show errors with express properly. check example in the webid passport (Agile-IoT) it handles errors with ejs and failureFlash true
     passport.authenticate('webid', { failureRedirect: failUrl,  passReqToCallback: true, failureFlash: false   }),
         function(req, res) {
               res.redirect('/static/idm/authenticateUser.html');
  });

  return router;

}
module.exports = RouterPassport;
<commit_msg>fix redirection web id<commit_after>
var passport = require('passport');
var express = require('express');

function RouterPassport(conf, app){

  var failUrl = '/static/error/error.html';
  var router = express.Router();

    //local strategy (REST API JSON)
  router.route('/local').post(
    passport.authenticate('local',{ failureRedirect: failUrl,
        usernameField: 'username',
        passwordField: 'password',
        passReqToCallback: true

      }),
      function(req, res) {
        console.log("session id"+req.session.id);
        console.log("session cookie"+JSON.stringify(req.session.cookie));
        res.redirect('/static/idm/authenticateUser.html');
      }

  );

  //Github
  router.route('/github').get(
    passport.authenticate('github'),
    function(req, res){});
  router.route('/callback_github').get(
    passport.authenticate('github', { failureRedirect: failUrl }),
    function(req, res) {
      console.log("session id"+req.session.id);
      console.log("session cookie"+JSON.stringify(req.session.cookie));
      res.redirect('/static/index.html');
    });

  //Google
  router.route('/google').get(
    passport.authenticate('google', {  }
  ));
  //TODO place into router later! Fix README first!
  app.get/*router.route*/('/callback_google'/*).get(*/,
    passport.authenticate('google', { failureRedirect: failUrl }),
    function(req, res) {
      console.log("session id"+req.session.id);
      console.log("session cookie"+JSON.stringify(req.session.cookie));
      res.redirect('/static/index.html');
  });

  router.route('/web_id').get(
     //TODO adjust to show errors with express properly. check example in the webid passport (Agile-IoT) it handles errors with ejs and failureFlash true
     passport.authenticate('webid', { failureRedirect: failUrl,  passReqToCallback: true, failureFlash: false   }),
         function(req, res) {
               res.redirect('/static/index.html');
  });

  return router;

}
module.exports = RouterPassport;
<|endoftext|>


<commit_before>'use strict';

const EntryPoint = require('./entry-point');
const Funnel = require('broccoli-funnel');
const Rollup = require('./rollup-with-dependencies');
const fs = require('fs');
const mergeTrees = require('broccoli-merge-trees');
const path = require('path');
const rollupReplace = require('rollup-plugin-replace');
const uglify = require('broccoli-uglify-sourcemap');

const TREE_FOR_METHODS = {
  'service-worker': 'treeForServiceWorker',
  'service-worker-registration': 'treeForServiceWorkerRegistration'
};

const ENTRY_POINT_FILENAMES = {
  'service-worker': 'sw.js',
  'service-worker-registration': 'sw-registration.js'
};

module.exports = class ServiceWorkerBuilder {
  constructor(options) {
    this.options = options || {};

    this.plugins = options.plugins;
    this.appTree = options.appTree;
    this.app = options.app;
  }

  build(type) {
    let trees = this._treesForPlugins(type);
    return this._compileTrees(trees, ENTRY_POINT_FILENAMES[type]);
  }

  _treesForPlugins(type) {
    return this.plugins.reduce((trees, plugin) => {
      let pluginTree = this._treeForPlugin(plugin, type);

      if (pluginTree) {
        return trees.concat(pluginTree);
      }

      return trees
    }, []);
  }

  _treeForPlugin(plugin, type) {
    let pluginPath = path.resolve(plugin.root, type);
    let treeForMethod = TREE_FOR_METHODS[type];

    let tree;

    if (fs.existsSync(pluginPath)) {
      tree = this.app.treeGenerator(pluginPath);
    }

    if (plugin[treeForMethod]) {
      tree = plugin[treeForMethod](tree, this.appTree);
    }

    if (tree) {
      return new Funnel(tree, {
        destDir: plugin.pkg.name + '/' + type
      });
    }
  }

  _compileTrees(trees, treeName) {
    let entryPoint = new EntryPoint(trees, { entryPoint: `${treeName}` });
    let tree = mergeTrees(trees.concat(entryPoint), { overwrite: true });

    let registrationDistPath = this.options.registrationDistPath;
    let treeDistPath = treeName;
    if (treeName === 'sw-registration.js' && registrationDistPath) {
      treeDistPath = path.join(registrationDistPath, treeName);
    }

    tree = this._babelTranspile(tree);
    tree = this._rollupTree(tree, `${treeName}`, treeDistPath);
    tree = this._uglifyTree(tree);

    return tree;
  }

  _rollupTree(tree, entryFile, destFile) {
    let rollupReplaceConfig = {
      include: [
        '/**/ember-service-worker/**/*.js',
        /^[a-z]:\/(?:[^\/:*?"<>|\r\n]+\/)*ember-service-worker\/(?:[^\/:*?"<>|\r\n]+\/)*[^\/:*?"<>|\r\n]*\.js$/i
      ],
      delimiters: ['{{', '}}'],
      ROOT_URL: this.options.rootURL
    };

    return new Rollup(tree, {
      inputFiles: '**/*.js',
      rollup: {
        input: entryFile,
        output: {
          file: destFile || entryFile,
          format: 'iife',
        },
        exports: 'none',
        plugins: [
          rollupReplace(rollupReplaceConfig)
        ]
      }
    });
  }

  _uglifyTree(tree) {
    if (this.options.minifyJS && this.options.minifyJS.enabled) {
      let options = this.options.minifyJS.options || {};
      options.sourceMapConfig = this.options.sourcemaps;
      return uglify(tree,  options);
    }

    return tree;
  }

  _babelTranspile(tree) {
    let emberCliBabel = this.app.project.addons.filter((a) => a.name === 'ember-cli-babel')[0];
    return emberCliBabel.transpileTree(tree, { 'ember-cli-babel': { compileModules: false } });
  }
}
<commit_msg>comment for the regex<commit_after>'use strict';

const EntryPoint = require('./entry-point');
const Funnel = require('broccoli-funnel');
const Rollup = require('./rollup-with-dependencies');
const fs = require('fs');
const mergeTrees = require('broccoli-merge-trees');
const path = require('path');
const rollupReplace = require('rollup-plugin-replace');
const uglify = require('broccoli-uglify-sourcemap');

const TREE_FOR_METHODS = {
  'service-worker': 'treeForServiceWorker',
  'service-worker-registration': 'treeForServiceWorkerRegistration'
};

const ENTRY_POINT_FILENAMES = {
  'service-worker': 'sw.js',
  'service-worker-registration': 'sw-registration.js'
};

module.exports = class ServiceWorkerBuilder {
  constructor(options) {
    this.options = options || {};

    this.plugins = options.plugins;
    this.appTree = options.appTree;
    this.app = options.app;
  }

  build(type) {
    let trees = this._treesForPlugins(type);
    return this._compileTrees(trees, ENTRY_POINT_FILENAMES[type]);
  }

  _treesForPlugins(type) {
    return this.plugins.reduce((trees, plugin) => {
      let pluginTree = this._treeForPlugin(plugin, type);

      if (pluginTree) {
        return trees.concat(pluginTree);
      }

      return trees
    }, []);
  }

  _treeForPlugin(plugin, type) {
    let pluginPath = path.resolve(plugin.root, type);
    let treeForMethod = TREE_FOR_METHODS[type];

    let tree;

    if (fs.existsSync(pluginPath)) {
      tree = this.app.treeGenerator(pluginPath);
    }

    if (plugin[treeForMethod]) {
      tree = plugin[treeForMethod](tree, this.appTree);
    }

    if (tree) {
      return new Funnel(tree, {
        destDir: plugin.pkg.name + '/' + type
      });
    }
  }

  _compileTrees(trees, treeName) {
    let entryPoint = new EntryPoint(trees, { entryPoint: `${treeName}` });
    let tree = mergeTrees(trees.concat(entryPoint), { overwrite: true });

    let registrationDistPath = this.options.registrationDistPath;
    let treeDistPath = treeName;
    if (treeName === 'sw-registration.js' && registrationDistPath) {
      treeDistPath = path.join(registrationDistPath, treeName);
    }

    tree = this._babelTranspile(tree);
    tree = this._rollupTree(tree, `${treeName}`, treeDistPath);
    tree = this._uglifyTree(tree);

    return tree;
  }

  _rollupTree(tree, entryFile, destFile) {
    let rollupReplaceConfig = {
      include: [
        '/**/ember-service-worker/**/*.js',
        // same as above but for windows paths like D:\my-ember\ember-service-worker\whatever\sw.js;
        // the original one doesn't work when the source code and the %TMP% folder, which ember started to use after v3.5, are located on different logical drives
        // which is quite common in Windows.
        /^[a-z]:\/(?:[^\/:*?"<>|\r\n]+\/)*ember-service-worker\/(?:[^\/:*?"<>|\r\n]+\/)*[^\/:*?"<>|\r\n]*\.js$/i
      ],
      delimiters: ['{{', '}}'],
      ROOT_URL: this.options.rootURL
    };

    return new Rollup(tree, {
      inputFiles: '**/*.js',
      rollup: {
        input: entryFile,
        output: {
          file: destFile || entryFile,
          format: 'iife',
        },
        exports: 'none',
        plugins: [
          rollupReplace(rollupReplaceConfig)
        ]
      }
    });
  }

  _uglifyTree(tree) {
    if (this.options.minifyJS && this.options.minifyJS.enabled) {
      let options = this.options.minifyJS.options || {};
      options.sourceMapConfig = this.options.sourcemaps;
      return uglify(tree,  options);
    }

    return tree;
  }

  _babelTranspile(tree) {
    let emberCliBabel = this.app.project.addons.filter((a) => a.name === 'ember-cli-babel')[0];
    return emberCliBabel.transpileTree(tree, { 'ember-cli-babel': { compileModules: false } });
  }
}
<|endoftext|>


<commit_before>'use strict'

const logger = require('cooking/util/logger');
const path = require('path')
const webpack = require('webpack')
const APICloud = require('apicloud-tools-core');

module.exports = function(program, configs) {
    let compiler;
    configs.devServer = {
        port: 8080,
        hot: true,
        log: true,
        enable: false,
        historyApiFallback: true,
        lazy: false,
        stats: 'errors-only',
        host: '0.0.0.0',
        __host__: 'http://0.0.0.0:8080'
    };
    compiler = webpack(configs)

    logger.log('Loading...')
    let count = 0;
    compiler.watch({}, (err, stats) => {
        console.log(stats.toString(configs.devServer.stats));
        if (err) {
            return logger.error(err)
        }
        APICloud.syncWifi({
            syncAll: true,
            projectPath: compiler.options.output.path
        })
        logger.log('webpack info \n' + stats.toString(configs.devServer.stats))
    })
    APICloud.startWifi({
        port: program.progress || 8090
    })
};<commit_msg>watch<commit_after>'use strict'

const logger = require('cooking/util/logger');
const path = require('path')
const webpack = require('webpack')
const APICloud = require('apicloud-tools-core');

module.exports = function(program, configs) {
    let compiler;
    configs.devServer = {
        port: 8080,
        hot: true,
        log: true,
        enable: false,
        historyApiFallback: true,
        lazy: false,
        stats: 'errors-only',
        host: '0.0.0.0',
        __host__: 'http://0.0.0.0:8080'
    };
    compiler = webpack(configs)

    logger.log('Loading...')
    let count = 0;
    compiler.watch({}, (err, stats) => {
        console.log(stats.toString(configs.devServer.stats));
        if (err) {
            return logger.error(err)
        }
        APICloud.syncWifi({
            updateAll: !(count++),
            projectPath: compiler.options.output.path
        })
        logger.log('webpack info \n' + stats.toString(configs.devServer.stats))
    })
    APICloud.startWifi({
        port: program.progress || 8090
    })
    APICloud.socketServer.on('connection', () => {
        if(count)
            APICloud.syncWifi({
                updateAll: true,
                projectPath: compiler.options.output.path
            })
    });
};<|endoftext|>


<commit_before>const fs = require('fs');
const EventEmitter = require('events');
const { backOff } = require('exponential-backoff');
const FormData = require('form-data');
const concat = require('concat-stream');
const { promisify } = require('util');

const shopwareStoreClient = require('./client/shopwareStoreClient');

const sleep = promisify(setTimeout);

function getPostDataFromFile(filePath) {
    // eslint-disable-next-line promise/avoid-new
    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 {
    /**
     * Tries to retrieve the correct domain for a shopware-ag commission and set it as domain.
     *
     * @param {Object} commission
     */
    static updateDomainForShopwareCommission(commission) {
        if (commission.domain !== 'www.shopware-ag.de') {
            return;
        }
        let matches = commission.text.match(/\([^()]*\)/g);
        if (!matches) {
            return;
        }
        matches = matches
            .map(match => match.slice(1, -1))
            .filter(match => match !== 'alt');
        if (matches.length === 0) {
            return;
        }

        commission.domain = matches[0];
    }

    /**
     * Tries to cleanup the domain string so it does not contain any non domain strings
     *
     * @param {string} domain
     */
    static cleanupDomainString(domain) {
        return domain
            .replace('OLD-OLD-', '')
            .replace('-OLD-OLD', '')
            .replace('OLD-', '')
            .replace('-OLD', '');
    }

    /**
     * @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
     * @param {Object} [options]
     * @param {number} [options.retryCount] how often the save operation should be retried in case of failure
     * @return {Object}
     */
    async savePlugin(plugin, options) {
        this.logEmitter.emit('info', `Saving changes in plugin ${plugin.name}...`);

        const numOfAttempts = ((options && options.retryCount) || 0) + 1;
        const res = await backOff(
            () => this.client.put(`plugins/${plugin.id}`, plugin),
            { numOfAttempts },
        );
        // 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 sleep(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 encrypted, hence don't enable partial encryption 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];
                sale.licenseShop.domain = ShopwareStoreCommander.cleanupDomainString(sale.licenseShop.domain);
                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];
                ShopwareStoreCommander.updateDomainForShopwareCommission(commission);
                commission.domain = ShopwareStoreCommander.cleanupDomainString(commission.domain);
                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;
    }
};
<commit_msg>Fix code style<commit_after>const fs = require('fs');
const EventEmitter = require('events');
const { backOff } = require('exponential-backoff');
const FormData = require('form-data');
const concat = require('concat-stream');
const { promisify } = require('util');

const shopwareStoreClient = require('./client/shopwareStoreClient');

const sleep = promisify(setTimeout);

function getPostDataFromFile(filePath) {
    // eslint-disable-next-line promise/avoid-new
    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 {
    /**
     * Tries to retrieve the correct domain for a shopware-ag commission and set it as domain.
     *
     * @param {Object} commission
     */
    static updateDomainForShopwareCommission(commission) {
        if (commission.domain !== 'www.shopware-ag.de') {
            return;
        }
        let matches = commission.text.match(/\([^()]*\)/g);
        if (!matches) {
            return;
        }
        matches = matches
            .map(match => match.slice(1, -1))
            .filter(match => match !== 'alt');
        if (matches.length === 0) {
            return;
        }

        commission.domain = matches[0];
    }

    /**
     * Tries to cleanup the domain string so it does not contain any non domain strings
     *
     * @param {string} domain
     */
    static cleanupDomainString(domain) {
        return domain
            .replace('OLD-OLD-', '')
            .replace('-OLD-OLD', '')
            .replace('OLD-', '')
            .replace('-OLD', '');
    }

    /**
     * @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
     * @param {Object} [options]
     * @param {number} [options.retryCount] how often the save operation should be retried in case of failure
     * @return {Object}
     */
    async savePlugin(plugin, options) {
        this.logEmitter.emit('info', `Saving changes in plugin ${plugin.name}...`);

        const numOfAttempts = ((options && options.retryCount) || 0) + 1;
        const res = await backOff(
            () => this.client.put(`plugins/${plugin.id}`, plugin),
            { numOfAttempts },
        );
        // 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 sleep(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 encrypted, hence don't enable partial encryption 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];
                sale.licenseShop.domain = ShopwareStoreCommander.cleanupDomainString(sale.licenseShop.domain);
                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];
                ShopwareStoreCommander.updateDomainForShopwareCommission(commission);
                commission.domain = ShopwareStoreCommander.cleanupDomainString(commission.domain);
                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>/* Upload pattern.
 *
 * Options:
 *    showTitle(boolean): show/hide the h1 title (true)
 *    url(string): If not used with a form, this option must provide the URL to submit to or baseUrl with relativePath needs to be used (null)
 *    baseUrl(string): to be used in conjunction with relativePath to generate submission urls based on related items (null)
 *    relativePath(string): again, to be used with baseUrl to create upload url (null)
 *    initialFolder(string): UID of initial folder related items widget should have selected (null)
 *    currentPath(string): Current path related items is starting with (null)
 *    clickable(boolean): If you can click on container to also upload (false)
 *    className(string): value for class attribute in the form element ('upload')
 *    paramName(string): value for name attribute in the file input element ('file')
 *    ajaxUpload(boolean): true or false for letting the widget upload the files via ajax. If false the form will act like a normal form. (true)
 *    wrap(boolean): true or false for wrapping this element using the value of wrapperTemplate. (false)
 *    wrapperTemplate(string): HTML template for wrapping around with this element. ('
')
 *    resultTemplate(string): HTML template for the element that will contain file information. ('

Drop files here...

')
 *    autoCleanResults(boolean): condition value for the file preview in div element to fadeout after file upload is completed. (true)
 *    previewsContainer(selector): JavaScript selector for file preview in div element. (.upload-previews)
 *    container(selector): JavaScript selector for where to put upload stuff into in case of form. If not provided it will be place before the first submit button. ('')
 *    relatedItems(object): Related items pattern options. Will only use only if relativePath is used to use correct upload destination ({ attributes: ["UID", "Title", "Description", "getURL", "Type", "path", "ModificationDate"], batchSize: 20, basePath: "/", vocabularyUrl: null, width: 500, maximumSelectionSize: 1, placeholder: "Search for item on site..." })
 *
 * Documentation:
 *
 *    # On a div element
 *
 *    {{ example-1 }}
 *
 * Example: example-1
 *

 *

 *
Something here that is useful

 *
Something else here that is useful

 *
Another thing here that is useful

 *

 *

 *
 *
 * License:
 *    Copyright (C) 2010 Plone Foundation
 *
 *    This program is free software; you can redistribute it and/or modify it
 *    under the terms of the GNU General Public License as published by the
 *    Free Software Foundation; either version 2 of the License.
 *
 *    This program is distributed in the hope that it will be useful, but
 *    WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 *    Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

define([
  'jquery',
  'underscore',
  'mockup-patterns-base',
  'mockup-patterns-relateditems',
  'dropzone',
  'text!mockup-patterns-upload-url/templates/upload.xml',
  'text!mockup-patterns-upload-url/templates/preview.xml',
  'mockup-i18n'
], function($, _, Base, RelatedItems, Dropzone,
            UploadTemplate, PreviewTemplate, i18n) {
  'use strict';

  /* we do not want this plugin to auto discover */
  Dropzone.autoDiscover = false;

  i18n.loadCatalog('widgets');
  var _t = i18n.MessageFactory('widgets');

  var UploadPattern = Base.extend({
    name: 'upload',
    defaults: {
      showTitle: true,
      url: null, // XXX MUST provide url to submit to OR be in a form
      className: 'upload',
      wrap: false,
      wrapperTemplate: '
',
      fileaddedClassName: 'dropping',
      useTus: false,
      container: '',
      ajaxUpload: true,

      paramName: 'file',
      clickable: true,
      addRemoveLinks: false,
      autoCleanResults: true,
      previewsContainer: '.previews',
      previewTemplate: null,
      maxFiles: null,
      maxFilesize: 99999999, // let's not have a max by default...

      relatedItems: {
        // UID attribute is required here since we're working with related items
        attributes: ['UID', 'Title', 'Description', 'getURL', 'Type', 'path', 'ModificationDate'],
        batchSize: 20,
        basePath: '/',
        vocabularyUrl: null,
        width: 500,
        maximumSelectionSize: 1,
        placeholder: _t('Search for item on site...')
      }
    },

    //placeholder: 'Search for item on site...'
    init: function() {
      var self = this,
          template = UploadTemplate;

      // values that will change current processing
      self.currentPath = self.options.currentPath;
      self.numFiles = 0;
      self.currentFile = 0;

      template = _.template(template, {_t: _t});
      self.$el.addClass(self.options.className);
      self.$el.append(template);

      self.$progress = $('.progress-bar-success', self.$el);

      if (!self.options.showTitle) {
        self.$el.find('h2.title').hide();
      }

      if (!self.options.ajaxUpload) {
        // no ajax upload, drop the fallback
        $('.fallback', this.$el).remove();
        if (this.$el.hasClass('.upload-container')) {
          this.$el.addClass('no-ajax-upload');
        } else {
          this.$el.closest('.upload-container').addClass('no-ajax-upload');
        }
      }

      if (self.options.wrap) {
        self.$el.wrap(self.options.wrapperTemplate);
        self.$el = self.$el.parent();
      }

      if (self.options.baseUrl && self.options.relativePath){
        // only use related items if we can generate paths based urls
        self.$pathInput = $('input[name="location"]', self.$el);
        self.relatedItems = self.setupRelatedItems(self.$pathInput);
      } else {
        $('input[name="location"]', self.$el).parent().remove();
        self.relatedItems = null;
      }

      var $uploadArea = $('.upload-area', self.$el);

      $('button.browse', self.$el).click(function(e) {
        e.preventDefault();
        e.stopPropagation();
        // we trigger the dropzone dialog!
        self.dropzone.hiddenFileInput.click();
      });

      var dzoneOptions = this.getDzoneOptions();

      try {
        // if init of Dropzone fails it says nothing and
        // it fails silently. Using this block we make sure
        // that if you break it w/ some weird or missing option
        // you can get a proper log of it
        //
        self.dropzone = new Dropzone($uploadArea[0], dzoneOptions);
      } catch (e) {
        if (window.DEBUG) {
          // log it!
          console.log(e);
        }
        throw e;
      }

      self.dropzone.on('addedfile', function(file) {
        //console.log('file added!!');
        // show upload controls
        self.showControls();
        // self.$el.addClass(fileaddedClassName);
        // setTimeout(function() {
        //   if (!processing) {
        //     process();
        //   }
        // }, 100);
      });

      self.dropzone.on('removedfile', function() {
        if (self.dropzone.files.length < 1) {
          self.hideControls();
        }
      });

      if (self.options.autoCleanResults) {
        self.dropzone.on('complete', function(file) {
          setTimeout(function() {
            $(file.previewElement).fadeOut();
          }, 3000);
        });
      }

      self.dropzone.on('complete', function(file) {
        if (self.dropzone.files.length < 1) {
          self.hideControls();
        }
      });

      self.dropzone.on('totaluploadprogress', function(pct) {
        // need to caclulate total pct here in reality since we're manually
        // processing each file one at a time.
        pct = ((((self.currentFile - 1) * 100) + pct) / (self.numFiles * 100)) * 100;
        self.$progress.attr('aria-valuenow', pct).css('width', pct + '%');
      });

      $('.upload-all', self.$el).click(function (e) {
        e.preventDefault();
        e.stopPropagation();
        self.processUpload({
          finished: function() {
            self.$progress.attr('aria-valuenow', 0).css('width', '0%');
          }
        });
      });
    },

    showControls: function() {
      var self = this;
      $('.controls', self.$el).fadeIn('slow');
    },

    hideControls: function() {
      var self = this;
      $('.controls', self.$el).fadeOut('slow');
    },

    pathJoin: function() {
      var parts = [];
      _.each(arguments, function(part) {
        if (!part){
          return;
        }
        if (part[0] === '/'){
          part = part.substring(1);
        }
        if (part[part.length - 1] === '/'){
          part = part.substring(0, part.length - 1);
        }
        parts.push(part);
      });
      return parts.join('/');
    },

    getUrl: function() {

      var self = this;
      var url = self.options.url;
      if (!url) {
        if (self.options.baseUrl && self.options.relativePath){
          url = self.options.baseUrl;
          if (url[url.length - 1] !== '/'){
            url = url + '/';
          }
          url = url + self.pathJoin(self.currentPath, self.options.relativePath);
        } else {
          var $form = self.$el.parents('form');
          if ($form.length > 0){
            url = $form.attr('action');
          } else {
            url = window.location.href;
          }
        }
      }
      return url;
    },

    getDzoneOptions: function() {
      var self = this;

      // clickable option
      if (typeof(self.options.clickable) === 'string') {
        if (self.options.clickable === 'true') {
          self.options.clickable = true;
        } else {
          self.options.clickable = false;
        }
      }

      var options = $.extend({}, self.options);
      options.url = self.getUrl();
      // XXX force to only upload one at a time,
      // right now we don't support multiple for backends
      options.uploadMultiple = false;

      delete options.wrap;
      delete options.wrapperTemplate;
      delete options.resultTemplate;
      delete options.autoCleanResults;
      delete options.fileaddedClassName;
      delete options.useTus;

      if (self.options.previewsContainer) {
        /*
         * if they have a select but it's not an id, let's make an id selector
         * so we can target the correct container. dropzone is weird here...
         */
        var $preview = self.$el.find(self.options.previewsContainer);
        if ($preview.length > 0) {
          options.previewsContainer = $preview[0];
        }
      }

      // XXX: do we need to allow this?
      options.autoProcessQueue = false;
      // options.addRemoveLinks = true;  // we show them in the template
      options.previewTemplate = PreviewTemplate;

      // if our element is a form we should force some values
      // https://github.com/enyo/dropzone/wiki/Combine-normal-form-with-Dropzone
      return options;
    },

    processUpload: function(options) {
      if (!options){
        options = {};
      }

      var self = this,
          processing = false,
          useTus = self.options.useTus,
          fileaddedClassName = self.options.fileaddedClassName,
          finished = options.finished;

      self.numFiles = self.dropzone.files.length;
      self.currentFile = 0;

      function process() {
        processing = true;
        if (self.dropzone.files.length === 0) {
          processing = false;
          self.$el.removeClass(fileaddedClassName);
          if (finished !== undefined && typeof(finished) === 'function'){
            finished();
          }
          return;
        }
        var file = self.dropzone.files[0];
        if ([Dropzone.SUCCESS, Dropzone.ERROR, Dropzone.CANCELED]
            .indexOf(file.status) !== -1) {
          // remove it
          self.dropzone.removeFile(file);
          process();
        } else if (file.status !== Dropzone.UPLOADING) {
          // start processing file
          if (useTus && window.tus) {
            // use tus upload if installed
            self.handleTusUpload(file);
          } else {
            // otherwise, just use dropzone to process
            self.currentFile += 1;
            self.dropzone.processFile(file);
          }
          setTimeout(process, 100);
        } else {
          // currently processing
          setTimeout(process, 100);
        }
      }
      process();
    },

    handleTusUpload: function(file) {
      /* this needs fixing... */
      var self = this,
          $preview = $(file.previewElement),
          chunkSize = 1024 * 1024 * 5; // 5mb chunk size

      file.status = Dropzone.UPLOADING;

      window.tus.upload(file, {
        endpoint: self.dropzone.options.url,
        headers: {
          'FILENAME': file.name
        },
        chunkSize: chunkSize
      }).fail(function() {
        console.alert('Error uploading with TUS resumable uploads');
        file.status = Dropzone.ERROR;
      }).progress(function(e, bytesUploaded, bytesTotal) {
        var percentage = (bytesUploaded / bytesTotal * 100);
        self.$progress.attr('aria-valuenow', percentage).css('width', percentage + '%');
        self.$progress.html(_t('uploading...
') +
                            self.formatBytes(bytesUploaded) +
                            ' / ' + self.formatBytes(bytesTotal));
      }).done(function(url, file) {
        file.status = Dropzone.SUCCESS;
        self.dropzone.emit('success', file);
        self.dropzone.emit('complete', file);
      });
    },

    formatBytes: function(bytes) {
      var kb = Math.round(bytes / 1024);
      if (kb < 1024) {
        return kb + ' KiB';
      }
      var mb = Math.round(kb / 1024);
      if (mb < 1024) {
        return mb + ' MB';
      }
      return Math.round(mb / 1024) + ' GB';
    },

    setupRelatedItems: function($input) {
      var self = this;
      var options = self.options.relatedItems;
      if (self.options.initialFolder){
        $input.attr('value', self.options.initialFolder);
      }
      var ri = new RelatedItems($input, options);
      ri.$el.on('change', function() {
        var result = $(this).select2('data');
        if (result.length > 0){
          self.currentPath = result[0].path;
        } else {
          self.currentPath = null;
        }
        self.options.url = self.dropzone.options.url = self.getUrl();
      });
      return ri;
    }

  });

  return UploadPattern;

});
<commit_msg>ie9 fixes<commit_after>/* Upload pattern.
 *
 * Options:
 *    showTitle(boolean): show/hide the h1 title (true)
 *    url(string): If not used with a form, this option must provide the URL to submit to or baseUrl with relativePath needs to be used (null)
 *    baseUrl(string): to be used in conjunction with relativePath to generate submission urls based on related items (null)
 *    relativePath(string): again, to be used with baseUrl to create upload url (null)
 *    initialFolder(string): UID of initial folder related items widget should have selected (null)
 *    currentPath(string): Current path related items is starting with (null)
 *    clickable(boolean): If you can click on container to also upload (false)
 *    className(string): value for class attribute in the form element ('upload')
 *    paramName(string): value for name attribute in the file input element ('file')
 *    ajaxUpload(boolean): true or false for letting the widget upload the files via ajax. If false the form will act like a normal form. (true)
 *    wrap(boolean): true or false for wrapping this element using the value of wrapperTemplate. (false)
 *    wrapperTemplate(string): HTML template for wrapping around with this element. ('
')
 *    resultTemplate(string): HTML template for the element that will contain file information. ('

Drop files here...

')
 *    autoCleanResults(boolean): condition value for the file preview in div element to fadeout after file upload is completed. (true)
 *    previewsContainer(selector): JavaScript selector for file preview in div element. (.upload-previews)
 *    container(selector): JavaScript selector for where to put upload stuff into in case of form. If not provided it will be place before the first submit button. ('')
 *    relatedItems(object): Related items pattern options. Will only use only if relativePath is used to use correct upload destination ({ attributes: ["UID", "Title", "Description", "getURL", "Type", "path", "ModificationDate"], batchSize: 20, basePath: "/", vocabularyUrl: null, width: 500, maximumSelectionSize: 1, placeholder: "Search for item on site..." })
 *
 * Documentation:
 *
 *    # On a div element
 *
 *    {{ example-1 }}
 *
 * Example: example-1
 *

 *

 *
Something here that is useful

 *
Something else here that is useful

 *
Another thing here that is useful

 *

 *

 *
 *
 * License:
 *    Copyright (C) 2010 Plone Foundation
 *
 *    This program is free software; you can redistribute it and/or modify it
 *    under the terms of the GNU General Public License as published by the
 *    Free Software Foundation; either version 2 of the License.
 *
 *    This program is distributed in the hope that it will be useful, but
 *    WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 *    Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

define([
  'jquery',
  'underscore',
  'mockup-patterns-base',
  'mockup-patterns-relateditems',
  'dropzone',
  'text!mockup-patterns-upload-url/templates/upload.xml',
  'text!mockup-patterns-upload-url/templates/preview.xml',
  'mockup-i18n'
], function($, _, Base, RelatedItems, Dropzone,
            UploadTemplate, PreviewTemplate, i18n) {
  'use strict';

  /* we do not want this plugin to auto discover */
  Dropzone.autoDiscover = false;

  i18n.loadCatalog('widgets');
  var _t = i18n.MessageFactory('widgets');

  var UploadPattern = Base.extend({
    name: 'upload',
    defaults: {
      showTitle: true,
      url: null, // XXX MUST provide url to submit to OR be in a form
      className: 'upload',
      wrap: false,
      wrapperTemplate: '
',
      fileaddedClassName: 'dropping',
      useTus: false,
      container: '',
      ajaxUpload: true,

      paramName: 'file',
      clickable: true,
      addRemoveLinks: false,
      autoCleanResults: true,
      previewsContainer: '.previews',
      previewTemplate: null,
      maxFiles: null,
      maxFilesize: 99999999, // let's not have a max by default...

      relatedItems: {
        // UID attribute is required here since we're working with related items
        attributes: ['UID', 'Title', 'Description', 'getURL', 'Type', 'path', 'ModificationDate'],
        batchSize: 20,
        basePath: '/',
        vocabularyUrl: null,
        width: 500,
        maximumSelectionSize: 1,
        placeholder: _t('Search for item on site...')
      }
    },

    //placeholder: 'Search for item on site...'
    init: function() {
      var self = this,
          template = UploadTemplate;

      // values that will change current processing
      self.currentPath = self.options.currentPath;
      self.numFiles = 0;
      self.currentFile = 0;

      template = _.template(template, {_t: _t});
      self.$el.addClass(self.options.className);
      self.$el.append(template);

      self.$progress = $('.progress-bar-success', self.$el);

      if (!self.options.showTitle) {
        self.$el.find('h2.title').hide();
      }

      if (!self.options.ajaxUpload) {
        // no ajax upload, drop the fallback
        $('.fallback', this.$el).remove();
        if (this.$el.hasClass('.upload-container')) {
          this.$el.addClass('no-ajax-upload');
        } else {
          this.$el.closest('.upload-container').addClass('no-ajax-upload');
        }
      }

      if (self.options.wrap) {
        self.$el.wrap(self.options.wrapperTemplate);
        self.$el = self.$el.parent();
      }

      if (self.options.baseUrl && self.options.relativePath){
        // only use related items if we can generate paths based urls
        self.$pathInput = $('input[name="location"]', self.$el);
        self.relatedItems = self.setupRelatedItems(self.$pathInput);
      } else {
        $('input[name="location"]', self.$el).parent().remove();
        self.relatedItems = null;
      }

      self.$dropzone = $('.upload-area', self.$el);

      $('button.browse', self.$el).click(function(e) {
        e.preventDefault();
        e.stopPropagation();
        // we trigger the dropzone dialog!
        self.dropzone.hiddenFileInput.click();
      });

      var dzoneOptions = this.getDzoneOptions();

      try {
        // if init of Dropzone fails it says nothing and
        // it fails silently. Using this block we make sure
        // that if you break it w/ some weird or missing option
        // you can get a proper log of it
        //
        self.dropzone = new Dropzone(self.$dropzone[0], dzoneOptions);
      } catch (e) {
        if (window.DEBUG) {
          // log it!
          console.log(e);
        }
        throw e;
      }

      self.$dropzone.on('addedfile', function(file) {
        self.showControls();
      });

      self.$dropzone.on('removedfile', function() {
        if (self.dropzone.files.length < 1) {
          self.hideControls();
        }
      });

      if (self.options.autoCleanResults) {
        self.$dropzone.on('complete', function(file) {
          setTimeout(function() {
            $(file.previewElement).fadeOut();
          }, 3000);
        });
      }

      self.$dropzone.on('complete', function(file) {
        if (self.dropzone.files.length < 1) {
          self.hideControls();
        }
      });

      self.$dropzone.on('totaluploadprogress', function(pct) {
        // need to caclulate total pct here in reality since we're manually
        // processing each file one at a time.
        pct = ((((self.currentFile - 1) * 100) + pct) / (self.numFiles * 100)) * 100;
        self.$progress.attr('aria-valuenow', pct).css('width', pct + '%');
      });

      $('.upload-all', self.$el).click(function (e) {
        e.preventDefault();
        e.stopPropagation();
        self.processUpload({
          finished: function() {
            self.$progress.attr('aria-valuenow', 0).css('width', '0%');
          }
        });
      });
    },

    showControls: function() {
      var self = this;
      $('.controls', self.$el).fadeIn('slow');
    },

    hideControls: function() {
      var self = this;
      $('.controls', self.$el).fadeOut('slow');
    },

    pathJoin: function() {
      var parts = [];
      _.each(arguments, function(part) {
        if (!part){
          return;
        }
        if (part[0] === '/'){
          part = part.substring(1);
        }
        if (part[part.length - 1] === '/'){
          part = part.substring(0, part.length - 1);
        }
        parts.push(part);
      });
      return parts.join('/');
    },

    getUrl: function() {

      var self = this;
      var url = self.options.url;
      if (!url) {
        if (self.options.baseUrl && self.options.relativePath){
          url = self.options.baseUrl;
          if (url[url.length - 1] !== '/'){
            url = url + '/';
          }
          url = url + self.pathJoin(self.currentPath, self.options.relativePath);
        } else {
          var $form = self.$el.parents('form');
          if ($form.length > 0){
            url = $form.attr('action');
          } else {
            url = window.location.href;
          }
        }
      }
      return url;
    },

    getDzoneOptions: function() {
      var self = this;

      // clickable option
      if (typeof(self.options.clickable) === 'string') {
        if (self.options.clickable === 'true') {
          self.options.clickable = true;
        } else {
          self.options.clickable = false;
        }
      }

      var options = $.extend({}, self.options);
      options.url = self.getUrl();
      // XXX force to only upload one at a time,
      // right now we don't support multiple for backends
      options.uploadMultiple = false;

      delete options.wrap;
      delete options.wrapperTemplate;
      delete options.resultTemplate;
      delete options.autoCleanResults;
      delete options.fileaddedClassName;
      delete options.useTus;

      if (self.options.previewsContainer) {
        /*
         * if they have a select but it's not an id, let's make an id selector
         * so we can target the correct container. dropzone is weird here...
         */
        var $preview = self.$el.find(self.options.previewsContainer);
        if ($preview.length > 0) {
          options.previewsContainer = $preview[0];
        }
      }

      // XXX: do we need to allow this?
      options.autoProcessQueue = false;
      // options.addRemoveLinks = true;  // we show them in the template
      options.previewTemplate = PreviewTemplate;

      // if our element is a form we should force some values
      // https://github.com/enyo/dropzone/wiki/Combine-normal-form-with-Dropzone
      return options;
    },

    processUpload: function(options) {
      if (!options){
        options = {};
      }

      var self = this,
          processing = false,
          useTus = self.options.useTus,
          fileaddedClassName = self.options.fileaddedClassName,
          finished = options.finished;

      self.numFiles = self.dropzone.files.length;
      self.currentFile = 0;

      function process() {
        processing = true;
        if (self.dropzone.files.length === 0) {
          processing = false;
          self.$el.removeClass(fileaddedClassName);
          if (finished !== undefined && typeof(finished) === 'function'){
            finished();
          }
          return;
        }
        var file = self.dropzone.files[0];
        if ([Dropzone.SUCCESS, Dropzone.ERROR, Dropzone.CANCELED]
            .indexOf(file.status) !== -1) {
          // remove it
          self.dropzone.removeFile(file);
          process();
        } else if (file.status !== Dropzone.UPLOADING) {
          // start processing file
          if (useTus && window.tus) {
            // use tus upload if installed
            self.handleTusUpload(file);
          } else {
            // otherwise, just use dropzone to process
            self.currentFile += 1;
            self.dropzone.processFile(file);
          }
          setTimeout(process, 100);
        } else {
          // currently processing
          setTimeout(process, 100);
        }
      }
      process();
    },

    handleTusUpload: function(file) {
      /* this needs fixing... */
      var self = this,
          $preview = $(file.previewElement),
          chunkSize = 1024 * 1024 * 5; // 5mb chunk size

      file.status = Dropzone.UPLOADING;

      window.tus.upload(file, {
        endpoint: self.dropzone.options.url,
        headers: {
          'FILENAME': file.name
        },
        chunkSize: chunkSize
      }).fail(function() {
        if(window.DEBUG){
          console.alert('Error uploading with TUS resumable uploads');
        }
        file.status = Dropzone.ERROR;
      }).progress(function(e, bytesUploaded, bytesTotal) {
        var percentage = (bytesUploaded / bytesTotal * 100);
        self.$progress.attr('aria-valuenow', percentage).css('width', percentage + '%');
        self.$progress.html(_t('uploading...
') +
                            self.formatBytes(bytesUploaded) +
                            ' / ' + self.formatBytes(bytesTotal));
      }).done(function(url, file) {
        file.status = Dropzone.SUCCESS;
        self.dropzone.emit('success', file);
        self.dropzone.emit('complete', file);
      });
    },

    formatBytes: function(bytes) {
      var kb = Math.round(bytes / 1024);
      if (kb < 1024) {
        return kb + ' KiB';
      }
      var mb = Math.round(kb / 1024);
      if (mb < 1024) {
        return mb + ' MB';
      }
      return Math.round(mb / 1024) + ' GB';
    },

    setupRelatedItems: function($input) {
      var self = this;
      var options = self.options.relatedItems;
      if (self.options.initialFolder){
        $input.attr('value', self.options.initialFolder);
      }
      var ri = new RelatedItems($input, options);
      ri.$el.on('change', function() {
        var result = $(this).select2('data');
        if (result.length > 0){
          self.currentPath = result[0].path;
        } else {
          self.currentPath = null;
        }
        self.options.url = self.dropzone.options.url = self.getUrl();
      });
      return ri;
    }

  });

  return UploadPattern;

});
<|endoftext|>


<commit_before>// @flow

import onLoadEvent from '../events/onLoad';
import {transitionTo} from '../fsm';
import type {State} from '../types';
import {on} from '../eventBus';
import vars from '../vars';

const state: State = {
  onEnter() {
    if (!vars.manualPageLoadEvent) {
      on(onLoadEvent.name, onLoad);
      onLoadEvent.initialize();
    }
  },

  getActiveTraceId() {
    return vars.pageLoadTraceId;
  },

  triggerManualPageLoad: onLoad
};
export default state;

function onLoad() {
  transitionTo('pageLoaded');
}
<commit_msg>Support cases where the manual page load was triggered before the real page load<commit_after>// @flow

import onLoadEvent from '../events/onLoad';
import {transitionTo} from '../fsm';
import type {State} from '../types';
import {on} from '../eventBus';
import vars from '../vars';

const state: State = {
  onEnter() {
    if (!vars.manualPageLoadEvent || vars.manualPageLoadTriggered) {
      on(onLoadEvent.name, onLoad);
      onLoadEvent.initialize();
    }
  },

  getActiveTraceId() {
    return vars.pageLoadTraceId;
  },

  triggerManualPageLoad: onLoad
};
export default state;

function onLoad() {
  transitionTo('pageLoaded');
}
<|endoftext|>


<commit_before>var smocks = require('smocks');

/**
 * Create a route definition that will keep track of (in the state) the requests that are called for all variants
 * http://localhost:8000/api/history
 */
smocks.route('/api/history')
  // not necessary since default is GET but just to be explicit
  .method('GET')

  // add a route config (admin page input field) that we will use to indicate an attribute name in the response payload
  // this is really just a silly example used to demonstrate the usage of config values
  .config({
    // "attributeName" is arbitrary - it just represents the config key that the user input will be saved with
    attributeName: {
      label: 'What attribute name should I use to demonstrate state?',
      type: 'text',
      defaultValue: 'history'
    }
  })

  // add 3 different variants which will push a token to a "history" array that we store in state.
  // take a look at http://localhost:8000/api/foo multiple times to see the history grow
  // the reset the state in the admin panel (top button) and see the history go away
  .variant('scenario1').onRequest(historyScenario('scenario1'))
  .variant('scenario2').onRequest(historyScenario('scenario2'))
  .variant('scenario3').onRequest(historyScenario('scenario3'))

/**
 * Create a route definition that will return a hello world-ish payload.  This demonstrates variables in a route definition.
 * http://localhost:8000/hello/whatever
 */
.route('/api/hello/{message}')
  .onRequest(function(request, reply) {
    // this is the simplest route handler we can have because we are using the default method (GET)
    // and the default variant id ("default") for this route
    reply({ hello: request.params.message });
  })

/**
 * You can also easily respond with files - and even use dynamic route variables using "withFile".
 * Remember that ./ will point to the root of your project rather than the current directory (which in this case will be the same).
 * To test this route, uncomment the code below and add a file called "bar.json" in the root directory what some content.
 * Then browse to http://localhost:8000/foo/bar
 */
// .route('/foo/{something}').withFile('./{something}.json')

/**
 * Global variants (can be associated with any route definitions)
 */
.global()

  // simulate a back-end server error
  .variant('500').onRequest(function(request, reply) {
    reply( {code: 'BAD_NEWS', message: 'Something bad happened'} ).code(500);
  })

  // wait 3 seconds and then simulate a back-end timeout with a 504
  .variant('timeout').onRequest(function(request, reply) {
    setTimeout(function() {
      reply( {code: 'TIMEOUT', message: 'Gateway timeout'} ).code(504);
    }, 3000);
  })

.start({
    host: 'localhost',
    port: 8000
});

/**
 * Return a request handler scoped to a specific scenario name
 *
 * We will simply rstore the user's scenario request history (can be changed by altering the variants in the admin panel).
 */
function historyScenario(scenarioName) {

  // return the actual request handler (variant)
  return function(request, reply) {
    // add a state entry to indicate that we've seen "scenario1"
    // state values are meaningful because the user can reset the state to go back to a fresh start
    var history = this.state('history') || [];
    this.state('history', history);
    history.push(scenarioName);

    // get the user config attribute which we'll use to construct the payload
    // config values are meaningful because the user can control them using the admin panel
    var attributeName = this.config('attributeName');

    // not return the view history
    var payload = {};
    payload[attributeName] = history;
    reply(payload);
  };
}
<commit_msg>update<commit_after>var smocks = require('smocks');

/**
 * Create a route definition that will keep track of (in the state) the requests that are called for all variants
 * http://localhost:8000/api/history
 */
smocks.route('/api/history')
  // not necessary since default is GET but just to be explicit
  .method('GET')

  // add a route config (admin page input field) that we will use to indicate an attribute name in the response payload
  // this is really just a silly example used to demonstrate the usage of config values
  .config({
    // "attributeName" is arbitrary - it just represents the config key that the user input will be saved with
    attributeName: {
      label: 'What attribute name should I use to demonstrate state?',
      type: 'text',
      defaultValue: 'history'
    }
  })

  // add 3 different variants which will push a token to a "history" array that we store in state.
  // take a look at http://localhost:8000/api/foo multiple times to see the history grow
  // the reset the state in the admin panel (top button) and see the history go away
  .variant('scenario1').onRequest(historyScenario('scenario1'))
  .variant('scenario2').onRequest(historyScenario('scenario2'))
  .variant('scenario3').onRequest(historyScenario('scenario3'))

/**
 * Create a route definition that will return a hello world-ish payload.  This demonstrates variables in a route definition.
 * http://localhost:8000/hello/whatever
 */
.route('/api/hello/{message}')
  .onRequest(function(request, reply) {
    // this is the simplest route handler we can have because we are using the default method (GET)
    // and the default variant id ("default") for this route
    reply({ hello: request.params.message });
  })

/**
 * You can also easily respond with files - and even use dynamic route variables using "withFile".
 * Remember that ./ will point to the root of your project rather than the current directory (which in this case will be the same).
 * To test this route, uncomment the code below and add a file called "bar.json" in the root directory what some content.
 * Then browse to http://localhost:8000/foo/bar
 */
// .route('/foo/{something}').withFile('./{something}.json')

/**
 * Global variants (can be associated with any route definitions)
 */
.global()

  // simulate a back-end server error
  .variant('500').onRequest(function(request, reply) {
    reply( {code: 'BAD_NEWS', message: 'Something bad happened'} ).code(500);
  })

  // wait 3 seconds and then simulate a back-end timeout with a 504
  .variant('timeout').onRequest(function(request, reply) {
    setTimeout(function() {
      reply( {code: 'TIMEOUT', message: 'Gateway timeout'} ).code(504);
    }, 3000);
  })

.start({
    host: 'localhost',
    port: 8000
});

/**
 * Return a request handler scoped to a specific scenario name
 *
 * We will simply rstore the user's scenario request history (can be changed by altering the variants in the admin panel).
 */
function historyScenario(scenarioName) {

  // return the actual request handler (variant)
  return function(request, reply) {
    // add a state entry to indicate that we've seen "scenario1"
    // state values are meaningful because the user can reset the state to go back to a fresh start
    var history = this.state('history') || [];
    // save the var in state (in case we just created it)
    this.state('history', history);
    history.push(scenarioName);

    // get the user config attribute which we'll use to construct the payload
    // config values are meaningful because the user can control them using the admin panel
    var attributeName = this.config('attributeName');

    // not return the view history
    var payload = {};
    payload[attributeName] = history;
    reply(payload);
  };
}
<|endoftext|>


<commit_before>import test from 'tape-catch';

import candela from '..';

const componentList = [
  'BarChart',
  'BoxPlot',
  'BulletChart',
  'GanttChart',
  'Geo',
  'GeoDots',
  'Heatmap',
  'Histogram',
  'LineChart',
  'LineUp',
  'OnSet',
  'ScatterPlot',
  'ScatterPlotMatrix',
  'SurvivalPlot',
  'TrackerDash',
  'UpSet'
];

function componentExists (t, cd, name) {
  const component = cd.components[name];

  t.ok(component, `candela.components.${name} exists`);
  t.equal(typeof component, 'function', `candela.components.${name} is a function`);
}

function structureTests (t, cd) {
  t.ok(cd, 'candela exists');

  t.ok(cd.VisComponent, 'candela.VisComponent exists');

  t.ok(cd.VisComponent.VisComponent, 'candela.VisComponent.VisComponent exists');
  t.equal(typeof cd.VisComponent.VisComponent, 'function', 'candela.VisComponent.VisComponent is a function');

  t.ok(cd.components, 'candela.components exists');

  t.equal(Object.keys(cd.components).length, componentList.length, `candela.components contains ${componentList.length} components`);

  componentList.forEach(name => {
    componentExists(t, cd, name);
  });
}

test('Structure and content of exported Candela library object', t => {
  structureTests(t, candela);
  t.end();
});

test('Structure and content of unminified Candela library file', t => {
  try {
    let candelaBuilt = require('../../../build/candela/candela.js');
    t.ok(candelaBuilt, 'The built candela.js file exists');

    if (candelaBuilt) {
      structureTests(t, candelaBuilt);
    }
  } catch (e) {
    t.skip('Built candela.js file does not exist');
  }

  t.end();
});
<commit_msg>test: Fix exports test to point to proper built file<commit_after>import test from 'tape-catch';

import candela from '..';

const componentList = [
  'BarChart',
  'BoxPlot',
  'BulletChart',
  'GanttChart',
  'Geo',
  'GeoDots',
  'Heatmap',
  'Histogram',
  'LineChart',
  'LineUp',
  'OnSet',
  'ScatterPlot',
  'ScatterPlotMatrix',
  'SurvivalPlot',
  'TrackerDash',
  'UpSet'
];

function componentExists (t, cd, name) {
  const component = cd.components[name];

  t.ok(component, `candela.components.${name} exists`);
  t.equal(typeof component, 'function', `candela.components.${name} is a function`);
}

function structureTests (t, cd) {
  t.ok(cd, 'candela exists');

  t.ok(cd.VisComponent, 'candela.VisComponent exists');

  t.ok(cd.VisComponent.VisComponent, 'candela.VisComponent.VisComponent exists');
  t.equal(typeof cd.VisComponent.VisComponent, 'function', 'candela.VisComponent.VisComponent is a function');

  t.ok(cd.components, 'candela.components exists');

  t.equal(Object.keys(cd.components).length, componentList.length, `candela.components contains ${componentList.length} components`);

  componentList.forEach(name => {
    componentExists(t, cd, name);
  });
}

test('Structure and content of exported Candela library object', t => {
  structureTests(t, candela);
  t.end();
});

test('Structure and content of unminified Candela library file', t => {
  try {
    let candelaBuilt = require('../build/candela/candela.js');
    t.ok(candelaBuilt, 'The built candela.js file exists');

    if (candelaBuilt) {
      structureTests(t, candelaBuilt);
    }
  } catch (e) {
    t.skip('Built candela.js file does not exist');
  }

  t.end();
});
<|endoftext|>