<commit_before>module.exports = function(skill, info, bot, message, db) {
    var channelID = {
    channel: "bar"
    };
  db.save(channelID, function(err, id){
  // id is a unique ID
    });
  var userData = message.text.match(/\<(.*?)\>/g);  //suss out any @mentions of users
  if (userData) {
    // if there is a user is mentioned initiate the challenge!
    //console.log(userData[0].replace(/[^\w\s]/gi, ''));
    var challenged = userData[0].replace(/[^\w\s]/gi, '');

    bot.api.users.info({user: challenged}, (error, response) => {
        bot.reply(message, ':rotating_light: ' + response.user.name + ' you have been challenged to a game of ROCK:mountain: , PAPER:spiral_note_pad: , SCISSORS:scissors:! Prepare to defend your honor!');
    });
    //playerShoot(message.user);
    bot.startPrivateConversation(message, privateConvo(bot, message));
    //bot.reply(message,'Let\'s rumble' + userData[0].replace(/[^\w\s]/gi, ''));
  } else {//if no user challenged
    bot.reply(message, 'You didn\'t challenge anyone you chicken shit! :chicken: :poop:');
  }

function playerShoot(user) {
    //DM the players for thier  moves
    bot.api.im.open({ user: user }, function (err, response) {
      if (err) {
        return console.log(err)
      }
    var encouragementArray = [
    'I believe in you!',
    'I\'m sure you\'ll do your best!',
    'you\'re already a winner in my book!',
    'nothing can stop you!',
    'you have the eye of the tiger',
    'you have the heart of a champion',
    'your lucky numbers are 52, 17, 4, 61, 44, 23',
    'take no prisoners!',
    'give no quarter, and expect none either!',
    'no retreat, no surrender!',
    'I\'m not friends with losers, so try not to lose, eh?',
    'If you\'re not first you\'re last!',
    'if you lose, don\'t bother coming home.',
    'nothing can conquer your indomitable spirit!'
    ];
    var randomEncouragement = Math.floor(Math.random()* encouragementArray.length);

    var dmChannel = response.channel.id;
    var positiveVibes = 'What do you want to play? *Rock*, *Paper* or *Scissors*?' + '\n' + 'No matter what you choose, just remember- ' + encouragementArray[randomEncouragement];
    bot.say({channel: dmChannel, text: positiveVibes});
    })

}

function privateConvo(bot, message) {
  const { user, channel } = message;

  return (err, convo) => {
    if (err) throw err;
    var encouragementArray = [
    'I believe in you!',
    'I\'m sure you\'ll do your best!',
    'you\'re already a winner in my book!',
    'nothing can stop you!',
    'you have the eye of the tiger',
    'you have the heart of a champion',
    'your lucky numbers are 52, 17, 4, 61, 44, 23',
    'take no prisoners!',
    'give no quarter, and expect none either!',
    'no retreat, no surrender!',
    'I\'m not friends with losers, so try not to lose, eh?',
    'If you\'re not first you\'re last!',
    'if you lose, don\'t bother coming home.',
    'nothing can conquer your indomitable spirit!'
    ];
    var randomEncouragement = Math.floor(Math.random()* encouragementArray.length);
    convo.ask('Do you want to play `paper`, `rock`, or `scissors`? No matter what you choose, just remember- '+ encouragementArray[randomEncouragement], [
      {
        pattern: 'paper|rock|scissors',
        callback(response, convo) {
          // since no further messages are queued after this,
          // the conversation will end naturally with status === 'completed'
          convo.next();
        },
      }, {
        default: true,
        callback(response, convo) {
          convo.repeat();
          convo.next();
        },
      },
    ], { key: 'rockPaperScissors' }); // store the results in a field called rockPaperScissors

    convo.on('end', (convo) => {
      if (convo.status === 'completed') {
        const prc = convo.extractResponse('rockPaperScissors');

        db.get(channel, (err, data) => {
          if (err) throw err;

          const updateData = data;
          updateData.players[user].played = prc;

          const { players } = updateData;
          const playerIDs = Object.keys(players);

          // check if only one player has played
          const onlyOnePlayed = playerIDs.find((id) => players[id].played === '');

          if (onlyOnePlayed) {
            db.save(updateData, (err) => {
              if (err) throw err;

              bot.reply(message, `<@${user}> has played!`);
            });
          } else {
            const gameResults = playerIDs.map((id) => `<@${id}> played ${players[id].played}`);

            bot.reply(message, gameResults.join(' & '));

            // reset the game data
            db.save({ id: updateData.id }, (err) => {
              if (err) throw err;
            });
          }
        });
      } else {
        // this happens if the conversation ended prematurely for some reason
        bot.reply(message, 'OK, nevermind!');
      }
    });
  };
}

  var gameEngine = require('./Rock-Paper-Spock');

    //bot.api.users.info({user: message.user}, (error, response) => {
        //bot.reply(message, 'Rock Paper Scissors (lizard, spock, spiderman, batman, wizard, glock?)');
    //})

  var instance = new gameEngine(gameEngine.DefaultRules);
  instance.addPlayer({id: 1, sign: "paper"});
  instance.addPlayer({id: 2, sign: "rock"});
  instance.play();
  console.log(instance.winner);
  //note players that won-lost, add support for W-L records
  //bot.reply(message, 'test successful. maybe code something more useful next time?');
};
<commit_msg>Update RockPaperScissors.js<commit_after>module.exports = function(skill, info, bot, message, db) {
    var channelID = {
    channel: "bar"
    };
  db.save(channelID, function(err, id){
  // id is a unique ID
    });
  var userData = message.text.match(/\<(.*?)\>/g);  //suss out any @mentions of users
  if (userData) {
    // if there is a user is mentioned initiate the challenge!
    //console.log(userData[0].replace(/[^\w\s]/gi, ''));
    var challenged = userData[0].replace(/[^\w\s]/gi, '');

    bot.api.users.info({user: challenged}, (error, response) => {
        bot.reply(message, ':rotating_light: ' + response.user.name + ' you have been challenged to a game of ROCK:mountain: , PAPER:spiral_note_pad: , SCISSORS:scissors:! Prepare to defend your honor!');
    });
    //playerShoot(message.user);
    bot.startPrivateConversation(message, privateConvo(bot, message));
    //bot.reply(message,'Let\'s rumble' + userData[0].replace(/[^\w\s]/gi, ''));
  } else {//if no user challenged
    bot.reply(message, 'You didn\'t challenge anyone you chicken shit! :chicken: :poop:');
  }

function privateConvo(bot, message) {
  const { user, channel } = message;

  return (err, convo) => {
    if (err) throw err;
    var encouragementArray = [
    'I believe in you!',
    'I\'m sure you\'ll do your best!',
    'you\'re already a winner in my book!',
    'nothing can stop you!',
    'you have the eye of the tiger',
    'you have the heart of a champion',
    'your lucky numbers are 52, 17, 4, 61, 44, 23',
    'take no prisoners!',
    'give no quarter, and expect none either!',
    'no retreat, no surrender!',
    'I\'m not friends with losers, so try not to lose, eh?',
    'If you\'re not first you\'re last!',
    'if you lose, don\'t bother coming home.',
    'nothing can conquer your indomitable spirit!'
    ];
    var randomEncouragement = Math.floor(Math.random()* encouragementArray.length);
    convo.ask('Do you want to play `paper`, `rock`, or `scissors`? No matter what you choose, just remember- ' + encouragementArray[randomEncouragement], [
      {
        pattern: 'paper|rock|scissors',
        callback(response, convo) {
          // since no further messages are queued after this,
          // the conversation will end naturally with status === 'completed'
          convo.next();
        },
      }, {
        default: true,
        callback(response, convo) {
          convo.repeat();
          convo.next();
        },
      },
    ], { key: 'rockPaperScissors' }); // store the results in a field called rockPaperScissors

    convo.on('end', (convo) => {
      if (convo.status === 'completed') {
        const prc = convo.extractResponse('rockPaperScissors');

        db.get(channel, (err, data) => {
          if (err) throw err;

          const updateData = data;
          updateData.players[user].played = prc;

          const { players } = updateData;
          const playerIDs = Object.keys(players);

          // check if only one player has played
          const onlyOnePlayed = playerIDs.find((id) => players[id].played === '');

          if (onlyOnePlayed) {
            db.save(updateData, (err) => {
              if (err) throw err;

              bot.reply(message, `<@${user}> has played!`);
            });
          } else {
            const gameResults = playerIDs.map((id) => `<@${id}> played ${players[id].played}`);

            bot.reply(message, gameResults.join(' & '));

            // reset the game data
            db.save({ id: updateData.id }, (err) => {
              if (err) throw err;
            });
          }
        });
      } else {
        // this happens if the conversation ended prematurely for some reason
        bot.reply(message, 'OK, nevermind!');
      }
    });
  };
}

  var gameEngine = require('./Rock-Paper-Spock');

    //bot.api.users.info({user: message.user}, (error, response) => {
        //bot.reply(message, 'Rock Paper Scissors (lizard, spock, spiderman, batman, wizard, glock?)');
    //})

  var instance = new gameEngine(gameEngine.DefaultRules);
  instance.addPlayer({id: 1, sign: "paper"});
  instance.addPlayer({id: 2, sign: "rock"});
  instance.play();
  console.log(instance.winner);
  //note players that won-lost, add support for W-L records
  //bot.reply(message, 'test successful. maybe code something more useful next time?');
};
<|endoftext|>


<commit_before>/**
 * Copyright 2013-2020 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * 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.
 */
const chalk = require('chalk');
const statistics = require('../statistics');
const packagejs = require('../../package.json');
const generatorDefaults = require('../generator-defaults').defaultConfig;

module.exports = {
    askForInsightOptIn,
    askForApplicationType,
    askForModuleName,
    askForTestOpts,
    askForMoreModules,
};

async function askForInsightOptIn() {
    const answers = await this.prompt({
        when: () => statistics.shouldWeAskForOptIn(),
        type: 'confirm',
        name: 'insight',
        message: `May ${chalk.cyan('JHipster')} anonymously report usage statistics to improve the tool over time?`,
        default: true,
    });
    if (answers.insight !== undefined) {
        statistics.setOptoutStatus(!answers.insight);
    }
}

async function askForApplicationType() {
    if (this.existingProject) return;

    const applicationTypeChoices = [
        {
            value: 'monolith',
            name: 'Monolithic application (recommended for simple projects)',
        },
        {
            value: 'microservice',
            name: 'Microservice application',
        },
        {
            value: 'gateway',
            name: 'Microservice gateway',
        },
        {
            value: 'uaa',
            name: 'JHipster UAA server',
        },
    ];

    const answers = await this.prompt([
        {
            type: 'list',
            name: 'applicationType',
            message: `Which ${chalk.yellow('*type*')} of application would you like to create?`,
            choices: applicationTypeChoices,
            default: generatorDefaults.applicationType,
        },
        {
            when: answers => ['gateway', 'monolith', 'microservice'].includes(answers.applicationType),
            type: 'confirm',
            name: 'reactive',
            message: '[Beta] Do you want to make it reactive with Spring WebFlux?',
            default: generatorDefaults.reactive,
        },
    ]);
    this.applicationType = this.jhipsterConfig.applicationType = answers.applicationType;
    this.reactive = this.jhipsterConfig.reactive = answers.reactive;
}

function askForModuleName() {
    if (this.existingProject) return undefined;
    return this.askModuleName(this);
}

async function askForTestOpts() {
    if (this.existingProject) return undefined;

    const choices = [];
    if (!this.skipServer) {
        // all server side test frameworks should be added here
        choices.push({ name: 'Gatling', value: 'gatling' }, { name: 'Cucumber', value: 'cucumber' });
    }
    if (!this.skipClient) {
        // all client side test frameworks should be added here
        choices.push({ name: 'Protractor', value: 'protractor' });
    }
    const PROMPT = {
        type: 'checkbox',
        name: 'testFrameworks',
        message: 'Besides JUnit and Jest, which testing frameworks would you like to use?',
        choices,
        default: generatorDefaults.testFrameworks,
    };

    const answers = await this.prompt(PROMPT);
    this.testFrameworks = this.jhipsterConfig.testFrameworks = answers.testFrameworks;
    return answers;
}

function askForMoreModules() {
    if (this.existingProject) {
        return undefined;
    }

    return this.prompt({
        type: 'confirm',
        name: 'installModules',
        message: 'Would you like to install other generators from the JHipster Marketplace?',
        default: false,
    }).then(answers => {
        if (answers.installModules) {
            return new Promise(resolve => askModulesToBeInstalled(resolve, this));
        }
        return undefined;
    });
}

function askModulesToBeInstalled(done, generator) {
    const jHipsterMajorVersion = packagejs.version.match(/^(\d+)/g);

    generator.httpsGet(
        `https://api.npms.io/v2/search?q=keywords:jhipster-module+jhipster-${jHipsterMajorVersion}&from=0&size=50`,
        body => {
            try {
                const moduleResponse = JSON.parse(body);
                const choices = [];
                moduleResponse.results.forEach(modDef => {
                    choices.push({
                        value: { name: modDef.package.name, version: modDef.package.version },
                        name: `(${modDef.package.name}-${modDef.package.version}) ${modDef.package.description}`,
                    });
                });
                if (choices.length > 0) {
                    generator
                        .prompt({
                            type: 'checkbox',
                            name: 'otherModules',
                            message: 'Which other modules would you like to use?',
                            choices,
                            default: [],
                        })
                        .then(answers => {
                            // [ {name: [moduleName], version:[version]}, ...]
                            answers.otherModules.forEach(module => {
                                generator.otherModules.push({ name: module.name, version: module.version });
                            });
                            generator.jhipsterConfig.otherModules = generator.otherModules;
                            done();
                        });
                } else {
                    done();
                }
            } catch (err) {
                generator.warning(`Error while parsing. Please install the modules manually or try again later. ${err.message}`);
                generator.debug('Error:', err);
                done();
            }
        },
        error => {
            generator.warning(`Unable to contact server to fetch additional modules: ${error.message}`);
            generator.debug('Error:', error);
            done();
        }
    );
}
<commit_msg>Fix setOptOutStatus<commit_after>/**
 * Copyright 2013-2020 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * 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.
 */
const chalk = require('chalk');
const statistics = require('../statistics');
const packagejs = require('../../package.json');
const generatorDefaults = require('../generator-defaults').defaultConfig;

module.exports = {
    askForInsightOptIn,
    askForApplicationType,
    askForModuleName,
    askForTestOpts,
    askForMoreModules,
};

async function askForInsightOptIn() {
    const answers = await this.prompt({
        when: () => statistics.shouldWeAskForOptIn(),
        type: 'confirm',
        name: 'insight',
        message: `May ${chalk.cyan('JHipster')} anonymously report usage statistics to improve the tool over time?`,
        default: true,
    });
    if (answers.insight !== undefined) {
        statistics.setOptOutStatus(!answers.insight);
    }
}

async function askForApplicationType() {
    if (this.existingProject) return;

    const applicationTypeChoices = [
        {
            value: 'monolith',
            name: 'Monolithic application (recommended for simple projects)',
        },
        {
            value: 'microservice',
            name: 'Microservice application',
        },
        {
            value: 'gateway',
            name: 'Microservice gateway',
        },
        {
            value: 'uaa',
            name: 'JHipster UAA server',
        },
    ];

    const answers = await this.prompt([
        {
            type: 'list',
            name: 'applicationType',
            message: `Which ${chalk.yellow('*type*')} of application would you like to create?`,
            choices: applicationTypeChoices,
            default: generatorDefaults.applicationType,
        },
        {
            when: answers => ['gateway', 'monolith', 'microservice'].includes(answers.applicationType),
            type: 'confirm',
            name: 'reactive',
            message: '[Beta] Do you want to make it reactive with Spring WebFlux?',
            default: generatorDefaults.reactive,
        },
    ]);
    this.applicationType = this.jhipsterConfig.applicationType = answers.applicationType;
    this.reactive = this.jhipsterConfig.reactive = answers.reactive;
}

function askForModuleName() {
    if (this.existingProject) return undefined;
    return this.askModuleName(this);
}

async function askForTestOpts() {
    if (this.existingProject) return undefined;

    const choices = [];
    if (!this.skipServer) {
        // all server side test frameworks should be added here
        choices.push({ name: 'Gatling', value: 'gatling' }, { name: 'Cucumber', value: 'cucumber' });
    }
    if (!this.skipClient) {
        // all client side test frameworks should be added here
        choices.push({ name: 'Protractor', value: 'protractor' });
    }
    const PROMPT = {
        type: 'checkbox',
        name: 'testFrameworks',
        message: 'Besides JUnit and Jest, which testing frameworks would you like to use?',
        choices,
        default: generatorDefaults.testFrameworks,
    };

    const answers = await this.prompt(PROMPT);
    this.testFrameworks = this.jhipsterConfig.testFrameworks = answers.testFrameworks;
    return answers;
}

function askForMoreModules() {
    if (this.existingProject) {
        return undefined;
    }

    return this.prompt({
        type: 'confirm',
        name: 'installModules',
        message: 'Would you like to install other generators from the JHipster Marketplace?',
        default: false,
    }).then(answers => {
        if (answers.installModules) {
            return new Promise(resolve => askModulesToBeInstalled(resolve, this));
        }
        return undefined;
    });
}

function askModulesToBeInstalled(done, generator) {
    const jHipsterMajorVersion = packagejs.version.match(/^(\d+)/g);

    generator.httpsGet(
        `https://api.npms.io/v2/search?q=keywords:jhipster-module+jhipster-${jHipsterMajorVersion}&from=0&size=50`,
        body => {
            try {
                const moduleResponse = JSON.parse(body);
                const choices = [];
                moduleResponse.results.forEach(modDef => {
                    choices.push({
                        value: { name: modDef.package.name, version: modDef.package.version },
                        name: `(${modDef.package.name}-${modDef.package.version}) ${modDef.package.description}`,
                    });
                });
                if (choices.length > 0) {
                    generator
                        .prompt({
                            type: 'checkbox',
                            name: 'otherModules',
                            message: 'Which other modules would you like to use?',
                            choices,
                            default: [],
                        })
                        .then(answers => {
                            // [ {name: [moduleName], version:[version]}, ...]
                            answers.otherModules.forEach(module => {
                                generator.otherModules.push({ name: module.name, version: module.version });
                            });
                            generator.jhipsterConfig.otherModules = generator.otherModules;
                            done();
                        });
                } else {
                    done();
                }
            } catch (err) {
                generator.warning(`Error while parsing. Please install the modules manually or try again later. ${err.message}`);
                generator.debug('Error:', err);
                done();
            }
        },
        error => {
            generator.warning(`Unable to contact server to fetch additional modules: ${error.message}`);
            generator.debug('Error:', error);
            done();
        }
    );
}
<|endoftext|>


<commit_before>/**
 * Copyright 2013-2018 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * 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.
 */
const chalk = require('chalk');

module.exports = {
    askForInsightOptIn,
    askForApplicationType,
    askForModuleName,
    askFori18n,
    askForTestOpts,
    askForMoreModules
};

function askForInsightOptIn() {
    if (this.existingProject) return;

    const done = this.async();
    const insight = this.insight();

    this.prompt({
        when: () => insight.optOut === undefined,
        type: 'confirm',
        name: 'insight',
        message: `May ${chalk.cyan('JHipster')} anonymously report usage statistics to improve the tool over time?`,
        default: true
    }).then((prompt) => {
        if (prompt.insight !== undefined) {
            insight.optOut = !prompt.insight;
        }
        done();
    });
}

function askForApplicationType(meta) {
    if (!meta && this.existingProject) return;

    const DEFAULT_APPTYPE = 'monolith';
    const PROMPT = {
        type: 'list',
        name: 'applicationType',
        message: `Which ${chalk.yellow('*type*')} of application would you like to create?`,
        choices: [
            {
                value: DEFAULT_APPTYPE,
                name: 'Monolithic application (recommended for simple projects)'
            },
            {
                value: 'microservice',
                name: 'Microservice application'
            },
            {
                value: 'gateway',
                name: 'Microservice gateway'
            },
            {
                value: 'reactive',
                name: '[Alpha] Reactive monolithic application'
            },
            {
                value: 'uaa',
                name: 'JHipster UAA server (for microservice OAuth2 authentication)'
            }
        ],
        default: DEFAULT_APPTYPE
    };

    if (meta) return PROMPT; // eslint-disable-line consistent-return

    const done = this.async();

    const promise = this.skipServer
        ? Promise.resolve({ applicationType: DEFAULT_APPTYPE })
        : this.prompt(PROMPT);
    promise.then((prompt) => {
        this.applicationType = this.configOptions.applicationType = prompt.applicationType;
        done();
    });
}

function askForModuleName() {
    if (this.existingProject) return;

    this.askModuleName(this);
}

function askFori18n() {
    if (this.skipI18n || this.existingProject) return;
    this.aski18n(this);
}

function askForTestOpts(meta) {
    if (!meta && this.existingProject) return;

    const choices = [];
    const defaultChoice = [];
    if (meta || !this.skipServer) {
        // all server side test frameworks should be added here
        choices.push(
            { name: 'Gatling', value: 'gatling' },
            { name: 'Cucumber', value: 'cucumber' }
        );
    }
    if (meta || !this.skipClient) {
        // all client side test frameworks should be added here
        choices.push({ name: 'Protractor', value: 'protractor' });
    }
    const PROMPT = {
        type: 'checkbox',
        name: 'testFrameworks',
        message: 'Besides JUnit and Jest, which testing frameworks would you like to use?',
        choices,
        default: defaultChoice
    };

    if (meta) return PROMPT; // eslint-disable-line consistent-return

    const done = this.async();

    this.prompt(PROMPT).then((prompt) => {
        this.testFrameworks = prompt.testFrameworks;
        done();
    });
}

function askForMoreModules() {
    if (this.existingProject) {
        return;
    }

    const done = this.async();
    this.prompt({
        type: 'confirm',
        name: 'installModules',
        message: 'Would you like to install other generators from the JHipster Marketplace?',
        default: false
    }).then((prompt) => {
        if (prompt.installModules) {
            askModulesToBeInstalled(done, this);
        } else {
            done();
        }
    });
}

function askModulesToBeInstalled(done, generator) {
    generator.httpsGet('https://api.npms.io/v2/search?q=keywords:jhipster-module+jhipster-5&from=0&size=50', (body) => {
        try {
            const moduleResponse = JSON.parse(body);
            const choices = [];
            moduleResponse.results.forEach((modDef) => {
                choices.push({
                    value: { name: modDef.package.name, version: modDef.package.version },
                    name: `(${modDef.package.name}-${modDef.package.version}) ${modDef.package.description}`
                });
            });
            if (choices.length > 0) {
                generator.prompt({
                    type: 'checkbox',
                    name: 'otherModules',
                    message: 'Which other modules would you like to use?',
                    choices,
                    default: []
                }).then((prompt) => {
                    // [ {name: [moduleName], version:[version]}, ...]
                    generator.otherModules = [];
                    prompt.otherModules.forEach((module) => {
                        generator.otherModules.push({ name: module.name, version: module.version });
                    });
                    generator.configOptions.otherModules = generator.otherModules;
                    done();
                });
            } else {
                done();
            }
        } catch (err) {
            generator.warning(`Error while parsing. Please install the modules manually or try again later. ${err.message}`);
            generator.debug('Error:', err);
            done();
        }
    }, (error) => {
        generator.warning(`Unable to contact server to fetch additional modules: ${error.message}`);
        generator.debug('Error:', error);
        done();
    });
}
<commit_msg>Propose the reactive option only if the experimental flag is set<commit_after>/**
 * Copyright 2013-2018 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * 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.
 */
const chalk = require('chalk');

module.exports = {
    askForInsightOptIn,
    askForApplicationType,
    askForModuleName,
    askFori18n,
    askForTestOpts,
    askForMoreModules
};

function askForInsightOptIn() {
    if (this.existingProject) return;

    const done = this.async();
    const insight = this.insight();

    this.prompt({
        when: () => insight.optOut === undefined,
        type: 'confirm',
        name: 'insight',
        message: `May ${chalk.cyan('JHipster')} anonymously report usage statistics to improve the tool over time?`,
        default: true
    }).then((prompt) => {
        if (prompt.insight !== undefined) {
            insight.optOut = !prompt.insight;
        }
        done();
    });
}

function askForApplicationType(meta) {
    if (!meta && this.existingProject) return;

    const DEFAULT_APPTYPE = 'monolith';

    const applicationTypeChoices = [
        {
            value: DEFAULT_APPTYPE,
            name: 'Monolithic application (recommended for simple projects)'
        },
        {
            value: 'microservice',
            name: 'Microservice application'
        },
        {
            value: 'gateway',
            name: 'Microservice gateway'
        },
        {
            value: 'uaa',
            name: 'JHipster UAA server (for microservice OAuth2 authentication)'
        }
    ];

    if (this.experimental) {
        applicationTypeChoices.push({
            value: 'reactive',
            name: '[Alpha] Reactive monolithic application'
        });
    }

    const PROMPT = {
        type: 'list',
        name: 'applicationType',
        message: `Which ${chalk.yellow('*type*')} of application would you like to create?`,
        choices: applicationTypeChoices,
        default: DEFAULT_APPTYPE
    };

    if (meta) return PROMPT; // eslint-disable-line consistent-return

    const done = this.async();

    const promise = this.skipServer
        ? Promise.resolve({ applicationType: DEFAULT_APPTYPE })
        : this.prompt(PROMPT);
    promise.then((prompt) => {
        this.applicationType = this.configOptions.applicationType = prompt.applicationType;
        done();
    });
}

function askForModuleName() {
    if (this.existingProject) return;

    this.askModuleName(this);
}

function askFori18n() {
    if (this.skipI18n || this.existingProject) return;
    this.aski18n(this);
}

function askForTestOpts(meta) {
    if (!meta && this.existingProject) return;

    const choices = [];
    const defaultChoice = [];
    if (meta || !this.skipServer) {
        // all server side test frameworks should be added here
        choices.push(
            { name: 'Gatling', value: 'gatling' },
            { name: 'Cucumber', value: 'cucumber' }
        );
    }
    if (meta || !this.skipClient) {
        // all client side test frameworks should be added here
        choices.push({ name: 'Protractor', value: 'protractor' });
    }
    const PROMPT = {
        type: 'checkbox',
        name: 'testFrameworks',
        message: 'Besides JUnit and Jest, which testing frameworks would you like to use?',
        choices,
        default: defaultChoice
    };

    if (meta) return PROMPT; // eslint-disable-line consistent-return

    const done = this.async();

    this.prompt(PROMPT).then((prompt) => {
        this.testFrameworks = prompt.testFrameworks;
        done();
    });
}

function askForMoreModules() {
    if (this.existingProject) {
        return;
    }

    const done = this.async();
    this.prompt({
        type: 'confirm',
        name: 'installModules',
        message: 'Would you like to install other generators from the JHipster Marketplace?',
        default: false
    }).then((prompt) => {
        if (prompt.installModules) {
            askModulesToBeInstalled(done, this);
        } else {
            done();
        }
    });
}

function askModulesToBeInstalled(done, generator) {
    generator.httpsGet('https://api.npms.io/v2/search?q=keywords:jhipster-module+jhipster-5&from=0&size=50', (body) => {
        try {
            const moduleResponse = JSON.parse(body);
            const choices = [];
            moduleResponse.results.forEach((modDef) => {
                choices.push({
                    value: { name: modDef.package.name, version: modDef.package.version },
                    name: `(${modDef.package.name}-${modDef.package.version}) ${modDef.package.description}`
                });
            });
            if (choices.length > 0) {
                generator.prompt({
                    type: 'checkbox',
                    name: 'otherModules',
                    message: 'Which other modules would you like to use?',
                    choices,
                    default: []
                }).then((prompt) => {
                    // [ {name: [moduleName], version:[version]}, ...]
                    generator.otherModules = [];
                    prompt.otherModules.forEach((module) => {
                        generator.otherModules.push({ name: module.name, version: module.version });
                    });
                    generator.configOptions.otherModules = generator.otherModules;
                    done();
                });
            } else {
                done();
            }
        } catch (err) {
            generator.warning(`Error while parsing. Please install the modules manually or try again later. ${err.message}`);
            generator.debug('Error:', err);
            done();
        }
    }, (error) => {
        generator.warning(`Unable to contact server to fetch additional modules: ${error.message}`);
        generator.debug('Error:', error);
        done();
    });
}
<|endoftext|>


<commit_before>var Duplex = require('stream').Duplex
, asyncMap = require('slide').asyncMap
, browserify = require('browserify')
, uglifyJS = require('uglify-js')
, stylus = require('stylus')
, util = require('util')
, fs = require('fs')

util.inherits(Compiler, Duplex)

module.exports = Compiler
// compiler prepares files for client

function Compiler (opts) {
  if (!(this instanceof Compiler)) return new Compiler(opts)
  Duplex.call(this, opts)
  console.log(opts)

  var self = this
  , DIR = './_wilds/'
  , READ1 = false
  , UPDATE = false
  , MODCOUNT = 0
  , MODS = []

  this._write = function (chunk, enc, next) {
    if (READ1===false) MODCOUNT++
    var mod = JSON.parse(chunk.toString())
    handleModule(mod)
    next()
  }

  this.end = function () {
    READ1 = true
  }

  function handleModule (mod) {
    asyncMap(mod.deps, function readModDeps (file, next) {
      var filepath = DIR+file
      var ext = file.split('.')[1]
      fs.readFile(filepath, function addDepToMod (err, buf) {
        if (err) console.error(err)
        mod[ext] = buf.toString()
        next()
      })
    }, function handledDeps () {
       if (UPDATE === false) MODS.push(mod)
       if (UPDATE === true) {
         for (var i=0;i<MODS.length;i++) {
           var m = MODS[i]
           if (m.id === mod.id) MODS[i] = mod
         }
         ready()
       }
       if (MODCOUNT === MODS.length) {
         ready()
         UPDATE = true
       }
    })
  }

  function ready () {
    var CSS = ''
    fs.readFile(DIR+'_css.styl', function (e, buf) { // load base styles
      if (e) console.error(e)
      CSS += buf.toString()
      compile(CSS)
    })
  }

  function compile (CSS) {
    var b = browserify()
    b.add('./__clnt.js')
    asyncMap(MODS, function (mod, next) {
      if (mod.styl) CSS += mod.styl // add style to css
      var fil = DIR+mod.id+'.js'
      b.add(DIR+mod.id+'.js') // add js to browserify
      next()
    }, function () {
      var bunF = fs.createWriteStream(DIR+'_bundle.js')
      b.bundle().pipe(bunF)
      bunF.on('finish', function () {
        console.log('wrote _bundle.js')
        if (opts.compress === true) {
          console.log('gonna cmp!')
          var min = uglifyJS.minify('./_wilds/_bundle.js')
          fs.writeFile('./_wilds/_bundle.js', min.code, function (e) {
            if (!e) console.log('minified bundle')
          })
        }
      })
      bunF.on('error', function (e) {
        console.error(e)
      })
      stylus.render(CSS, {filename:'_styles.css'}, function (e, css) {
        if (e) console.error(e)
        fs.writeFile(DIR+'_styles.css', css, function (e) {
          if (e) cosole.error(e)
          console.log('wrote _styles.css')
        })
      })
    })
  }
}
<commit_msg>using writeFile close event instead of finish<commit_after>var Duplex = require('stream').Duplex
, asyncMap = require('slide').asyncMap
, browserify = require('browserify')
, uglifyJS = require('uglify-js')
, stylus = require('stylus')
, util = require('util')
, fs = require('fs')

util.inherits(Compiler, Duplex)

module.exports = Compiler
// compiler prepares files for client

function Compiler (opts) {
  if (!(this instanceof Compiler)) return new Compiler(opts)
  Duplex.call(this, opts)
  console.log(opts)

  var self = this
  , DIR = './_wilds/'
  , READ1 = false
  , UPDATE = false
  , MODCOUNT = 0
  , MODS = []

  this._write = function (chunk, enc, next) {
    if (READ1===false) MODCOUNT++
    var mod = JSON.parse(chunk.toString())
    handleModule(mod)
    next()
  }

  this.end = function () {
    READ1 = true
  }

  function handleModule (mod) {
    asyncMap(mod.deps, function readModDeps (file, next) {
      var filepath = DIR+file
      var ext = file.split('.')[1]
      fs.readFile(filepath, function addDepToMod (err, buf) {
        if (err) console.error(err)
        mod[ext] = buf.toString()
        next()
      })
    }, function handledDeps () {
       if (UPDATE === false) MODS.push(mod)
       if (UPDATE === true) {
         for (var i=0;i<MODS.length;i++) {
           var m = MODS[i]
           if (m.id === mod.id) MODS[i] = mod
         }
         ready()
       }
       if (MODCOUNT === MODS.length && UPDATE === false) {
         ready()
         UPDATE = true
       }
    })
  }

  function ready () {
    var CSS = ''
    fs.readFile(DIR+'_css.styl', function (e, buf) { // load base styles
      if (e) console.error(e)
      CSS += buf.toString()
      compile(CSS)
    })
  }

  function compile (CSS) {
    var b = browserify()
    b.add('./__clnt.js')
    asyncMap(MODS, function (mod, next) {
      if (mod.styl) CSS += mod.styl // add style to css
      var fil = DIR+mod.id+'.js'
      b.add(DIR+mod.id+'.js') // add js to browserify
      next()
    }, function () {
      var bunF = fs.createWriteStream(DIR+'_bundle.js')
      b.bundle().pipe(bunF)
      bunF.on('close', function () {
        console.log('bundle written!')
        if (opts.compress === true) {
          var min = uglifyJS.minify('./_wilds/_bundle.js')
          fs.writeFile('./_wilds/_bundle.js', min.code, function (e) {
            if (!e) console.log('minified bundle')
          })
        }
      })
      bunF.on('error', function (e) {
        console.error(e)
      })
      stylus.render(CSS, {filename:'_styles.css'}, function (e, css) {
        if (e) console.error(e)
        fs.writeFile(DIR+'_styles.css', css, function (e) {
          if (e) cosole.error(e)
          console.log('wrote _styles.css')
        })
      })
    })
  }
}
<|endoftext|>


<commit_before>(function (root, factory) {
    'use strict';

    /* istanbul ignore next: untestable with Node-style definition */
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['ably-test', 'ably-subscriber'], factory);
    } else {
        /* istanbul ignore else: untestable with Node-style definition */
        if (typeof exports === 'object') {
            // Node-style
            module.exports = factory(require('./ably-test.js'), require('./ably-subscriber.js'));
        } else {
            // Browser globals (root is window)
            root.Ably = factory(root.AblyTest, root.AblySubscriber);
        }
    }
}(this, function (AblyTest, AblySubscriber) {
    'use strict';

    var Ably = function Ably() {

        function relayPendingSubscribers(test) {

            var unmatchedSubscribers = [];

            self.pendingSubscribers.forEach(function(pendingSubscriber) {
                if (pendingSubscriber.matchesTest(test)) {
                    test.addSubscriber(pendingSubscriber);
                } else {
                    unmatchedSubscribers.push(pendingSubscriber);
                }
            });

            self.pendingSubscribers = unmatchedSubscribers;
        }

        function uniformRandomizer(callback, test) {
            callback(test.variants[Math.floor(Math.random() * test.variants.length)]);
        }

        function interpretRandomizer(options) {
            if (!options.hasOwnProperty('randomizer')) {
                return self.randomizers['default'];
            }

            if (typeof options.randomizer === 'function') {
                return options.randomizer;
            }

            if (self.randomizers.hasOwnProperty(options.randomizer)) {
                return self.randomizers[options.randomizer];
            }

            throw new Error('randomizer \'' + options.randomizer + '\' not found');
        }

        this.tests = [];
        this.pendingSubscribers = [];
        this.randomizers = {
            uniform: uniformRandomizer,
            'default': uniformRandomizer
        };

        var self = this;

        // Privileged methods
        this.when = function (testName, variant, callback) {

            var subscriber = new AblySubscriber({
                test: testName,
                variant: variant,
                callback: callback
            });
            var test;

            try {
                test = this.getTest(subscriber.test);
            } catch (e) {
                this.pendingSubscribers.push(subscriber);
                return this;
            }

            test.addSubscriber(subscriber);

            return this;
        };

        this.addTest = function (options) {

            var test = new AblyTest({
                name: options.name,
                variants: options.variants,
                randomizer: interpretRandomizer(options),
                scope: options.scope
            });

            this.tests.push(test);

            relayPendingSubscribers(test);

            return this;
        };
    };

    Ably.prototype.addTests = function (tests) {
        var self = this;
        tests.forEach(function(test) {
            self.addTest(test);
        });
        return this;
    };

    Ably.prototype.getTest = function (name) {
        for (var i = 0; i < this.tests.length; i++) {
            if (this.tests[i].name === name) {
                return this.tests[i];
            }
        }
        throw new Error('test \'' + name + '\' not found');
    };

    Ably.prototype.getTests = function () {
        return this.tests;
    };

    return Ably;
}));
<commit_msg>Rename interpretRandomizer to interpretRandomizerOptions<commit_after>(function (root, factory) {
    'use strict';

    /* istanbul ignore next: untestable with Node-style definition */
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['ably-test', 'ably-subscriber'], factory);
    } else {
        /* istanbul ignore else: untestable with Node-style definition */
        if (typeof exports === 'object') {
            // Node-style
            module.exports = factory(require('./ably-test.js'), require('./ably-subscriber.js'));
        } else {
            // Browser globals (root is window)
            root.Ably = factory(root.AblyTest, root.AblySubscriber);
        }
    }
}(this, function (AblyTest, AblySubscriber) {
    'use strict';

    var Ably = function Ably() {

        function relayPendingSubscribers(test) {

            var unmatchedSubscribers = [];

            self.pendingSubscribers.forEach(function(pendingSubscriber) {
                if (pendingSubscriber.matchesTest(test)) {
                    test.addSubscriber(pendingSubscriber);
                } else {
                    unmatchedSubscribers.push(pendingSubscriber);
                }
            });

            self.pendingSubscribers = unmatchedSubscribers;
        }

        function uniformRandomizer(callback, test) {
            callback(test.variants[Math.floor(Math.random() * test.variants.length)]);
        }

        function interpretRandomizerOptions(options) {
            if (!options.hasOwnProperty('randomizer')) {
                return self.randomizers['default'];
            }

            if (typeof options.randomizer === 'function') {
                return options.randomizer;
            }

            if (self.randomizers.hasOwnProperty(options.randomizer)) {
                return self.randomizers[options.randomizer];
            }

            throw new Error('randomizer \'' + options.randomizer + '\' not found');
        }

        this.tests = [];
        this.pendingSubscribers = [];
        this.randomizers = {
            uniform: uniformRandomizer,
            'default': uniformRandomizer
        };

        var self = this;

        // Privileged methods
        this.when = function (testName, variant, callback) {

            var subscriber = new AblySubscriber({
                test: testName,
                variant: variant,
                callback: callback
            });
            var test;

            try {
                test = this.getTest(subscriber.test);
            } catch (e) {
                this.pendingSubscribers.push(subscriber);
                return this;
            }

            test.addSubscriber(subscriber);

            return this;
        };

        this.addTest = function (options) {

            var test = new AblyTest({
                name: options.name,
                variants: options.variants,
                randomizer: interpretRandomizerOptions(options),
                scope: options.scope
            });

            this.tests.push(test);

            relayPendingSubscribers(test);

            return this;
        };
    };

    Ably.prototype.addTests = function (tests) {
        var self = this;
        tests.forEach(function(test) {
            self.addTest(test);
        });
        return this;
    };

    Ably.prototype.getTest = function (name) {
        for (var i = 0; i < this.tests.length; i++) {
            if (this.tests[i].name === name) {
                return this.tests[i];
            }
        }
        throw new Error('test \'' + name + '\' not found');
    };

    Ably.prototype.getTests = function () {
        return this.tests;
    };

    return Ably;
}));
<|endoftext|>


<commit_before>/*
 * Minio Javascript Library for Amazon S3 Compatible Cloud Storage, (C) 2016 Minio, Inc.
 *
 * 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.
 */

import { Transform } from 'stream'
import { Hash } from 'crypto'
import * as querystring from 'querystring'

// We extend Transform because Writable does not implement ._flush().
export default class ObjectUploader extends Transform {
  constructor(client, bucketName, objectName, partSize, contentType, callback) {
    super()

    this.client = client
    this.bucketName = bucketName
    this.objectName = objectName
    // The size of each multipart, chunked by BlockStream2.
    this.partSize = partSize

    // This contentType for the object.
    this.contentType = contentType

    // Call like: callback(error, etag).
    this.callback = callback

    // We need to keep track of what number chunk/part we're on. This increments
    // each time _write() is called. Starts with 1, not 0.
    this.partNumber = 1

    // A list of the previously uploaded chunks, for resuming a file upload. This
    // will be null if we aren't resuming an upload.
    this.oldParts = null

    // Keep track of the etags for aggregating the chunks together later. Each
    // etag represents a single chunk of the file.
    this.etags = []

    // This is for the multipart upload request — if null, we're either not initiated
    // yet or we're flushing in one packet.
    this.id = null

    // Handle errors.
    this.on('error', err => {
      callback(err)
    })
  }

  _transform(chunk, encoding, callback) {
    // Calculate the md5 sum.
    let md5 = new Hash('md5')
    md5.update(chunk)
    let md5sum = md5.digest()

    let method = 'PUT'
    let headers = {
      'Content-Length': chunk.length,
      'Content-Type': this.contentType,
      'Content-MD5': md5sum.toString('base64')
    }

    // We can flush the object in one packet if it fits in one chunk. This is true
    // if the chunk size is smaller than the part size, signifying the end of the
    // stream.
    if (this.partNumber == 1 && chunk.length < this.partSize) {
      // PUT the chunk in a single request — use an empty query.
      let options = {
        method, headers,
        query: '',
        bucketName: this.bucketName,
        objectName: this.objectName
      }

      this.client.makeRequest(options, chunk, 200, '', (err, response) => {
        if (err) return callback(err)

        let etag = response.headers.etag
        if (etag) {
          etag = etag.replace(/^\"/, '').replace(/\"$/, '')
        }

        // Ignore the 'data' event so that the stream closes. (nodejs stream requirement)
        response.on('data', () => {})

        // Give the etag back, we're done!
        process.nextTick(() => {
          this.callback(null, etag)
        })

        // Because we're sure the stream has ended, allow it to flush and end.
        callback()
      })

      return
    }

    // If we aren't flushing in one packet, we need to initiate the multipart upload,
    // if it hasn't already been done. The write will be buffered until the upload has been
    // initiated.
    if (this.id === null) {
      this.once('ready', () => {
        this._transform(chunk, encoding, callback)
      })

      // Check for an incomplete previous upload.
      this.client.findUploadId(this.bucketName, this.objectName, (err, id) => {
        if (err) return this.emit('error', err)

        // If no upload ID exists, initiate a new one.
        if (!id) {
          this.client.initiateNewMultipartUpload(this.bucketName, this.objectName, this.contentType, (err, id) => {
            if (err) return callback(err)

            this.id = id

            // We are now ready to accept new chunks — this will flush the buffered chunk.
            this.emit('ready')
          })

          return
        }

        this.id = id

        // Retrieve the pre-uploaded parts, if we need to resume the upload.
        this.client.listParts(this.bucketName, this.objectName, id, (err, etags) => {
          if (err) return this.emit('error', err)

          // It is possible for no parts to be already uploaded.
          if (!etags) etags = []

          // oldParts will become an object, allowing oldParts[partNumber].etag
          this.oldParts = etags.reduce(function(prev, item) {
            if (!prev[item.part]) {
              prev[item.part] = item
            }
            return prev
          }, {})

          this.emit('ready')
        })
      })

      return
    }

    // Continue uploading various parts if we have initiated multipart upload.
    let partNumber = this.partNumber++

    // Check to see if we've already uploaded this chunk. If the hash sums match,
    // we can skip to the next chunk.
    if (this.oldParts) {
      let oldPart = this.oldParts[partNumber]

      if (oldPart && md5sum.toString('hex') === oldPart.etag) {
        // The md5 matches, the chunk has already been uploaded.
        this.etags.push({part: partNumber, etag: oldPart.etag})

        callback()
        return
      }
    }

    // Write the chunk with an uploader.
    let query = querystring.stringify({
      partNumber: partNumber,
      uploadId: this.id
    })

    let options = {
      method, query, headers,
      bucketName: this.bucketName,
      objectName: this.objectName
    }

    this.client.makeRequest(options, chunk, 200, '', (err, response) => {
      if (err) return callback(err)

      // In order to aggregate the parts together, we need to collect the etags.
      let etag = response.headers.etag
      if (etag)
        etag = etag.replace(/^\"/, '').replace(/\"$/, '')

      this.etags.push({part: partNumber, etag})

      // We're ready for the next chunk.
      callback()
    })
  }

  _flush(callback) {
    // If it has been uploaded in a single packet, we don't have to do anything.
    if (this.id === null) {
      return
    }

    // This is called when all of the chunks uploaded successfully, thus
    // completing the multipart upload.
    this.client.completeMultipartUpload(this.bucketName, this.objectName, this.id,
                                        this.etags, (err, etag) => {
      if (err) return callback(err)

      // Call our callback on the next tick to allow the streams infrastructure
      // to finish what its doing before we continue.
      process.nextTick(() => {
        this.callback(null, etag)
      })

      callback()
    })
  }

}
<commit_msg>api: Use createHash instead of new Hash() (#583)<commit_after>/*
 * Minio Javascript Library for Amazon S3 Compatible Cloud Storage, (C) 2016 Minio, Inc.
 *
 * 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.
 */

import { Transform } from 'stream'
import Crypto from 'crypto'
import * as querystring from 'querystring'

// We extend Transform because Writable does not implement ._flush().
export default class ObjectUploader extends Transform {
  constructor(client, bucketName, objectName, partSize, contentType, callback) {
    super()

    this.client = client
    this.bucketName = bucketName
    this.objectName = objectName
    // The size of each multipart, chunked by BlockStream2.
    this.partSize = partSize

    // This contentType for the object.
    this.contentType = contentType

    // Call like: callback(error, etag).
    this.callback = callback

    // We need to keep track of what number chunk/part we're on. This increments
    // each time _write() is called. Starts with 1, not 0.
    this.partNumber = 1

    // A list of the previously uploaded chunks, for resuming a file upload. This
    // will be null if we aren't resuming an upload.
    this.oldParts = null

    // Keep track of the etags for aggregating the chunks together later. Each
    // etag represents a single chunk of the file.
    this.etags = []

    // This is for the multipart upload request — if null, we're either not initiated
    // yet or we're flushing in one packet.
    this.id = null

    // Handle errors.
    this.on('error', err => {
      callback(err)
    })
  }

  _transform(chunk, encoding, callback) {
    // Calculate the md5sum.
    let md5 = Crypto.createHash('md5')
    md5.update(chunk)
    let md5sum = md5.digest()

    let method = 'PUT'
    let headers = {
      'Content-Length': chunk.length,
      'Content-Type': this.contentType,
      'Content-MD5': md5sum.toString('base64')
    }

    // We can flush the object in one packet if it fits in one chunk. This is true
    // if the chunk size is smaller than the part size, signifying the end of the
    // stream.
    if (this.partNumber == 1 && chunk.length < this.partSize) {
      // PUT the chunk in a single request — use an empty query.
      let options = {
        method, headers,
        query: '',
        bucketName: this.bucketName,
        objectName: this.objectName
      }

      this.client.makeRequest(options, chunk, 200, '', (err, response) => {
        if (err) return callback(err)

        let etag = response.headers.etag
        if (etag) {
          etag = etag.replace(/^\"/, '').replace(/\"$/, '')
        }

        // Ignore the 'data' event so that the stream closes. (nodejs stream requirement)
        response.on('data', () => {})

        // Give the etag back, we're done!
        process.nextTick(() => {
          this.callback(null, etag)
        })

        // Because we're sure the stream has ended, allow it to flush and end.
        callback()
      })

      return
    }

    // If we aren't flushing in one packet, we need to initiate the multipart upload,
    // if it hasn't already been done. The write will be buffered until the upload has been
    // initiated.
    if (this.id === null) {
      this.once('ready', () => {
        this._transform(chunk, encoding, callback)
      })

      // Check for an incomplete previous upload.
      this.client.findUploadId(this.bucketName, this.objectName, (err, id) => {
        if (err) return this.emit('error', err)

        // If no upload ID exists, initiate a new one.
        if (!id) {
          this.client.initiateNewMultipartUpload(this.bucketName, this.objectName, this.contentType, (err, id) => {
            if (err) return callback(err)

            this.id = id

            // We are now ready to accept new chunks — this will flush the buffered chunk.
            this.emit('ready')
          })

          return
        }

        this.id = id

        // Retrieve the pre-uploaded parts, if we need to resume the upload.
        this.client.listParts(this.bucketName, this.objectName, id, (err, etags) => {
          if (err) return this.emit('error', err)

          // It is possible for no parts to be already uploaded.
          if (!etags) etags = []

          // oldParts will become an object, allowing oldParts[partNumber].etag
          this.oldParts = etags.reduce(function(prev, item) {
            if (!prev[item.part]) {
              prev[item.part] = item
            }
            return prev
          }, {})

          this.emit('ready')
        })
      })

      return
    }

    // Continue uploading various parts if we have initiated multipart upload.
    let partNumber = this.partNumber++

    // Check to see if we've already uploaded this chunk. If the hash sums match,
    // we can skip to the next chunk.
    if (this.oldParts) {
      let oldPart = this.oldParts[partNumber]

      if (oldPart && md5sum.toString('hex') === oldPart.etag) {
        // The md5 matches, the chunk has already been uploaded.
        this.etags.push({part: partNumber, etag: oldPart.etag})

        callback()
        return
      }
    }

    // Write the chunk with an uploader.
    let query = querystring.stringify({
      partNumber: partNumber,
      uploadId: this.id
    })

    let options = {
      method, query, headers,
      bucketName: this.bucketName,
      objectName: this.objectName
    }

    this.client.makeRequest(options, chunk, 200, '', (err, response) => {
      if (err) return callback(err)

      // In order to aggregate the parts together, we need to collect the etags.
      let etag = response.headers.etag
      if (etag)
        etag = etag.replace(/^\"/, '').replace(/\"$/, '')

      this.etags.push({part: partNumber, etag})

      // We're ready for the next chunk.
      callback()
    })
  }

  _flush(callback) {
    // If it has been uploaded in a single packet, we don't have to do anything.
    if (this.id === null) {
      return
    }

    // This is called when all of the chunks uploaded successfully, thus
    // completing the multipart upload.
    this.client.completeMultipartUpload(this.bucketName, this.objectName, this.id,
                                        this.etags, (err, etag) => {
      if (err) return callback(err)

      // Call our callback on the next tick to allow the streams infrastructure
      // to finish what its doing before we continue.
      process.nextTick(() => {
        this.callback(null, etag)
      })

      callback()
    })
  }

}
<|endoftext|>


<commit_before>const find = (list, match) => {
  for (let i = 0; i < list.length; i++) {
    if (match(list[i])) {
      return list[i]
    }
  }
  return undefined
}

const productsGroupedByStatus = (stock, products) => {
  return Object.keys(stock).reduce((grouped, productId) => {
    const isRelevant = !!find(products, (product) => product._id === productId)
    const status = stock[productId].status
    if (!isRelevant) {
      return grouped
    }
    if (status) {
      grouped[status].push(productId)
    }
    return grouped
  }, { understock: [], 're-stock': [], ok: [], overstock: [] })
}

const sumAllocations = (sum, stock) => {
  return Object.keys(stock).reduce((total, product) => {
    total[product] = total[product] || 0
    if (stock[product].allocation > 0) {
      total[product] += stock[product].allocation
    }
    return total
  }, sum)
}

// TODO: make sure stock_statuses is availalbe
class StateIndicatorsService {
  constructor (
    $q,
    smartId,
    STOCK_STATUSES,
    lgasService,
    statesService,
    zonesService,
    locationsService,
    thresholdsService,
    productListService
  ) {
    this.$q = $q
    this.smartId = smartId
    this.STOCK_STATUSES = STOCK_STATUSES
    this.lgasService = lgasService
    this.statesService = statesService
    this.zonesService = zonesService
    this.locationsService = locationsService
    this.thresholdsService = thresholdsService
    this.productListService = productListService
  }

  stateRequiredAllocationsByZone (stockCounts) {
    return stockCounts.reduce((allocations, stockCount) => {
      if (stockCount.location && stockCount.location.state && !stockCount.location.lga && stockCount.reStockNeeded) {
        const zone = this.smartId.idify({ zone: stockCount.location.zone }, 'locationId')
        allocations[zone] = allocations[zone] || {}
        allocations[zone] = sumAllocations(allocations[zone], stockCount.stock)
      }
      return allocations
    }, {})
  }

  decorateWithIndicators (stockCounts, opts = {}) {
    let lgas
    let states
    let zones
    let products
    let national

    const getStockAmount = (stock, product, amount = 0) => {
      if (!isNaN(parseInt(stock[product].amount, 10))) {
        amount = stock[product].amount
      } else if (!isNaN(parseInt(stock[product], 10))) {
        amount = stock[product]
      }
      return amount
    }

    const getLocation = (lgas, states, zones, stockCount) => {
      if (!stockCount.location) {
        return
      }
      const locationId = this.smartId.idify(stockCount.location, 'locationId')

      let locations = zones
      if (stockCount.location.state) {
        locations = stockCount.location.lga ? lgas : states
      }
      return find(locations, (locationDoc) => locationDoc._id === locationId)
    }

    const decorateStockField = (stockCount, requiredAllocations) => {
      let location
      if (stockCount.location.national) {
        location = national
      } else {
        location = getLocation(lgas, states, zones, stockCount)
      }

      let locationThresholds
      if (location && location.level === 'zone' && requiredAllocations) {
        locationThresholds = this.thresholdsService.calculateThresholds(location, stockCount, products, requiredAllocations[location._id])
      } else {
        locationThresholds = this.thresholdsService.calculateThresholds(location, stockCount, products)
      }
      const stock = stockCount.stock

      const decoratedStock = Object.keys(stock).reduce((decorated, product) => {
        // v2 stock count report
        const amount = getStockAmount(stock, product)
        let status
        let allocation
        let productThresholds
        let selectedProduct = find(products, (prod) => {
          return prod._id === product
        })

        if (locationThresholds) {
          productThresholds = locationThresholds[product]

          if (productThresholds) {
            status = 'overstock'
            if (amount < productThresholds.min) {
              status = 'understock'
            } else if (amount < productThresholds.reOrder) {
              status = 're-stock'
            } else if (amount <= productThresholds.max) {
              status = 'ok'
            }

            const productBalance = productThresholds.max - amount
            allocation = productBalance
            if (selectedProduct) {
              const unitBalance = productBalance % selectedProduct.presentation
              allocation = unitBalance > 0 ? productBalance + (selectedProduct.presentation - unitBalance) : productBalance
            }
          }
        }

        decorated[product] = {
          status: status,
          amount: amount,
          allocation: allocation,
          thresholds: productThresholds
        }

        return decorated
      }, {})

      stockCount.stock = decoratedStock
      return stockCount
    }

    const addReStockField = (stockCount) => {
      const addAllocationIfPositive = (sum, productId) => {
        if (stockCount.stock[productId].allocation > 0) {
          sum = sum + stockCount.stock[productId].allocation
        }
        return sum
      }

      if (stockCount.location && stockCount.location.lga) {
        const groupedByStatus = productsGroupedByStatus(stockCount.stock, products)
        stockCount.reStockNeeded = !!(groupedByStatus.understock.length + groupedByStatus['re-stock'].length)
      } else { // states and zones
        if (stockCount.stock) {
          const sumOfPositiveAllocations = Object.keys(stockCount.stock).reduce(addAllocationIfPositive, 0)
          stockCount.reStockNeeded = sumOfPositiveAllocations > 0
        }
      }
      return stockCount
    }

    const addStockLevelStatusField = (stockCount) => {
      const grouped = productsGroupedByStatus(stockCount.stock, products)
      const understockedProducts = grouped.understock.length
      const totalGrouped = Object.keys(grouped).reduce((sum, group) => sum + grouped[group].length, 0)

      stockCount.stockLevelStatus = 'unknown'
      if (stockCount.location) {
        if (understockedProducts >= this.STOCK_STATUSES.alert.threshold) {
          stockCount.stockLevelStatus = this.STOCK_STATUSES.alert.id
        } else if (understockedProducts >= this.STOCK_STATUSES.warning.threshold) {
          stockCount.stockLevelStatus = this.STOCK_STATUSES.warning.id
        } else if (totalGrouped > 0) {
          stockCount.stockLevelStatus = this.STOCK_STATUSES.ok.id
        }
      }

      return stockCount
    }

    const hasNonEmptyStock = (stockCount) => {
      return (stockCount.stock && Object.keys(stockCount.stock).length)
    }

    const isZoneStockCount = (stockCount) => {
      return (stockCount.location && stockCount.location.zone && !stockCount.location.state)
    }

    const isNonZoneStockCount = (stockCount) => {
      return !isZoneStockCount(stockCount)
    }

    const isNationalStockCount = (stockCount) => {
      return (stockCount.location && stockCount.location.national)
    }

    const decorateStockCounts = (nonZoneStockCounts, zoneStockCounts, promiseResults) => {
      lgas = promiseResults.lgas
      states = promiseResults.states
      zones = promiseResults.zones || [] // not available for the state dashboard
      products = promiseResults.products
      national = promiseResults.national || {}

      nonZoneStockCounts = nonZoneStockCounts
                            .map(nonZoneStockCount => decorateStockField(nonZoneStockCount))
                            .map(addReStockField)
      zoneStockCounts = zoneStockCounts
                            .map(zoneStockCount => {
                              if (opts.requireChildAllocations === false) {
                                return decorateStockField(zoneStockCount)
                              }
                              return decorateStockField(zoneStockCount, this.stateRequiredAllocationsByZone(nonZoneStockCounts))
                            })
                            .map(addReStockField)

      return nonZoneStockCounts.concat(zoneStockCounts)
              .map(addStockLevelStatusField)
    }

    let promises = {
      lgas: this.lgasService.list(),
      states: this.statesService.list(),
      products: this.productListService.relevant()
    }

    stockCounts = stockCounts.filter(hasNonEmptyStock)

    if (!stockCounts.length) {
      return this.$q.when(stockCounts)
    }

    let zoneStockCounts = stockCounts.filter(isZoneStockCount)
    let nonZoneStockCounts = stockCounts.filter(isNonZoneStockCount)
    let nationalStockCounts = stockCounts.filter(isNationalStockCount)

    if (zoneStockCounts.length) {
      promises.zones = this.zonesService.list()
    }

    if (nationalStockCounts.length) {
      promises.national = this.locationsService.get('national')
    }

    return this.$q
            .all(promises)
            .then(decorateStockCounts.bind(null, nonZoneStockCounts, zoneStockCounts))
  }
}

StateIndicatorsService.$inject = ['$q', 'smartId', 'STOCK_STATUSES', 'lgasService', 'statesService', 'zonesService', 'locationsService', 'thresholdsService', 'productListService']

export default StateIndicatorsService
<commit_msg>fix: handle undefined stock product<commit_after>const find = (list, match) => {
  for (let i = 0; i < list.length; i++) {
    if (match(list[i])) {
      return list[i]
    }
  }
  return undefined
}

const productsGroupedByStatus = (stock, products) => {
  return Object.keys(stock).reduce((grouped, productId) => {
    const isRelevant = !!find(products, (product) => product._id === productId)
    const status = stock[productId].status
    if (!isRelevant) {
      return grouped
    }
    if (status) {
      grouped[status].push(productId)
    }
    return grouped
  }, { understock: [], 're-stock': [], ok: [], overstock: [] })
}

const sumAllocations = (sum, stock) => {
  return Object.keys(stock).reduce((total, product) => {
    total[product] = total[product] || 0
    if (stock[product].allocation > 0) {
      total[product] += stock[product].allocation
    }
    return total
  }, sum)
}

// TODO: make sure stock_statuses is availalbe
class StateIndicatorsService {
  constructor (
    $q,
    smartId,
    STOCK_STATUSES,
    lgasService,
    statesService,
    zonesService,
    locationsService,
    thresholdsService,
    productListService
  ) {
    this.$q = $q
    this.smartId = smartId
    this.STOCK_STATUSES = STOCK_STATUSES
    this.lgasService = lgasService
    this.statesService = statesService
    this.zonesService = zonesService
    this.locationsService = locationsService
    this.thresholdsService = thresholdsService
    this.productListService = productListService
  }

  stateRequiredAllocationsByZone (stockCounts) {
    return stockCounts.reduce((allocations, stockCount) => {
      if (stockCount.location && stockCount.location.state && !stockCount.location.lga && stockCount.reStockNeeded) {
        const zone = this.smartId.idify({ zone: stockCount.location.zone }, 'locationId')
        allocations[zone] = allocations[zone] || {}
        allocations[zone] = sumAllocations(allocations[zone], stockCount.stock)
      }
      return allocations
    }, {})
  }

  decorateWithIndicators (stockCounts, opts = {}) {
    let lgas
    let states
    let zones
    let products
    let national

    const getStockAmount = function (stock, product) {
      var amount = 0
      if (stock[product].amount && typeof stock[product].amount === 'number') {
        amount = stock[product].amount
      } else if (typeof stock[product] === 'number' || !isNaN(parseInt(stock[product], 10))) {
        amount = stock[product]
      }
      return amount
    }

    const getLocation = (lgas, states, zones, stockCount) => {
      if (!stockCount.location) {
        return
      }
      const locationId = this.smartId.idify(stockCount.location, 'locationId')

      let locations = zones
      if (stockCount.location.state) {
        locations = stockCount.location.lga ? lgas : states
      }
      return find(locations, (locationDoc) => locationDoc._id === locationId)
    }

    const decorateStockField = (stockCount, requiredAllocations) => {
      let location
      if (stockCount.location.national) {
        location = national
      } else {
        location = getLocation(lgas, states, zones, stockCount)
      }

      let locationThresholds
      if (location && location.level === 'zone' && requiredAllocations) {
        locationThresholds = this.thresholdsService.calculateThresholds(location, stockCount, products, requiredAllocations[location._id])
      } else {
        locationThresholds = this.thresholdsService.calculateThresholds(location, stockCount, products)
      }
      const stock = stockCount.stock

      const decoratedStock = Object.keys(stock).reduce((decorated, product) => {
        // v2 stock count report
        const amount = getStockAmount(stock, product)
        let status
        let allocation
        let productThresholds
        let selectedProduct = find(products, (prod) => {
          return prod._id === product
        })

        if (locationThresholds) {
          productThresholds = locationThresholds[product]

          if (productThresholds) {
            status = 'overstock'
            if (amount < productThresholds.min) {
              status = 'understock'
            } else if (amount < productThresholds.reOrder) {
              status = 're-stock'
            } else if (amount <= productThresholds.max) {
              status = 'ok'
            }

            const productBalance = productThresholds.max - amount
            allocation = productBalance
            if (selectedProduct) {
              const unitBalance = productBalance % selectedProduct.presentation
              allocation = unitBalance > 0 ? productBalance + (selectedProduct.presentation - unitBalance) : productBalance
            }
          }
        }

        decorated[product] = {
          status: status,
          amount: amount,
          allocation: allocation,
          thresholds: productThresholds
        }

        return decorated
      }, {})

      stockCount.stock = decoratedStock
      return stockCount
    }

    const addReStockField = (stockCount) => {
      const addAllocationIfPositive = (sum, productId) => {
        if (stockCount.stock[productId].allocation > 0) {
          sum = sum + stockCount.stock[productId].allocation
        }
        return sum
      }

      if (stockCount.location && stockCount.location.lga) {
        const groupedByStatus = productsGroupedByStatus(stockCount.stock, products)
        stockCount.reStockNeeded = !!(groupedByStatus.understock.length + groupedByStatus['re-stock'].length)
      } else { // states and zones
        if (stockCount.stock) {
          const sumOfPositiveAllocations = Object.keys(stockCount.stock).reduce(addAllocationIfPositive, 0)
          stockCount.reStockNeeded = sumOfPositiveAllocations > 0
        }
      }
      return stockCount
    }

    const addStockLevelStatusField = (stockCount) => {
      const grouped = productsGroupedByStatus(stockCount.stock, products)
      const understockedProducts = grouped.understock.length
      const totalGrouped = Object.keys(grouped).reduce((sum, group) => sum + grouped[group].length, 0)

      stockCount.stockLevelStatus = 'unknown'
      if (stockCount.location) {
        if (understockedProducts >= this.STOCK_STATUSES.alert.threshold) {
          stockCount.stockLevelStatus = this.STOCK_STATUSES.alert.id
        } else if (understockedProducts >= this.STOCK_STATUSES.warning.threshold) {
          stockCount.stockLevelStatus = this.STOCK_STATUSES.warning.id
        } else if (totalGrouped > 0) {
          stockCount.stockLevelStatus = this.STOCK_STATUSES.ok.id
        }
      }

      return stockCount
    }

    const hasNonEmptyStock = (stockCount) => {
      return (stockCount.stock && Object.keys(stockCount.stock).length)
    }

    const isZoneStockCount = (stockCount) => {
      return (stockCount.location && stockCount.location.zone && !stockCount.location.state)
    }

    const isNonZoneStockCount = (stockCount) => {
      return !isZoneStockCount(stockCount)
    }

    const isNationalStockCount = (stockCount) => {
      return (stockCount.location && stockCount.location.national)
    }

    const decorateStockCounts = (nonZoneStockCounts, zoneStockCounts, promiseResults) => {
      lgas = promiseResults.lgas
      states = promiseResults.states
      zones = promiseResults.zones || [] // not available for the state dashboard
      products = promiseResults.products
      national = promiseResults.national || {}

      nonZoneStockCounts = nonZoneStockCounts
                            .map(nonZoneStockCount => decorateStockField(nonZoneStockCount))
                            .map(addReStockField)
      zoneStockCounts = zoneStockCounts
                            .map(zoneStockCount => {
                              if (opts.requireChildAllocations === false) {
                                return decorateStockField(zoneStockCount)
                              }
                              return decorateStockField(zoneStockCount, this.stateRequiredAllocationsByZone(nonZoneStockCounts))
                            })
                            .map(addReStockField)

      return nonZoneStockCounts.concat(zoneStockCounts)
              .map(addStockLevelStatusField)
    }

    let promises = {
      lgas: this.lgasService.list(),
      states: this.statesService.list(),
      products: this.productListService.relevant()
    }

    stockCounts = stockCounts.filter(hasNonEmptyStock)

    if (!stockCounts.length) {
      return this.$q.when(stockCounts)
    }

    let zoneStockCounts = stockCounts.filter(isZoneStockCount)
    let nonZoneStockCounts = stockCounts.filter(isNonZoneStockCount)
    let nationalStockCounts = stockCounts.filter(isNationalStockCount)

    if (zoneStockCounts.length) {
      promises.zones = this.zonesService.list()
    }

    if (nationalStockCounts.length) {
      promises.national = this.locationsService.get('national')
    }

    return this.$q
            .all(promises)
            .then(decorateStockCounts.bind(null, nonZoneStockCounts, zoneStockCounts))
  }
}

StateIndicatorsService.$inject = ['$q', 'smartId', 'STOCK_STATUSES', 'lgasService', 'statesService', 'zonesService', 'locationsService', 'thresholdsService', 'productListService']

export default StateIndicatorsService
<|endoftext|>


<commit_before>/**
 *
 * @authors Ted Shiu (ted@gmail.com)
 * @date    2016-04-11 14:44:03
 * source https://github.com/tedshd/ajax
 */

(function() {
    function ajax(option) {
        var setting, method, url, data, xhr, success;
        if (option) {
            setting = option;
        } else {
            return console.error('not set arguments');
        }
        if (setting.method) {
            method = setting.method;
        } else {
            return console.error('not set method');
        }
        if (setting.url) {
            url = setting.url;
        } else {
            return console.error('not set url');
        }
        if (setting.success) {
            success = setting.success;
        } else {
            return console.error('not set success callback');
        }
        data = setting.data || '';
        if (setting.method === 'GET' && data) {
            url = url + '?' + formUrlEncode(data);
        }

        function formUrlEncode(obj) {
            if (!obj) {
                return '';
            }
            var urlData = '';
            for (var x in obj) {
                urlData = urlData + x + '=' + obj[x] + '&';
            }
            urlData = urlData.substr(0, (urlData.length - 1));
            return urlData;
        }

        // handle IE8 IE9 CORS
        if (XDomainRequest) {
            var host = location.host,
                matchUrl = url.replace('https://', '').replace('http://', '');
                matchUrl = matchUrl.slice(0, matchUrl.indexOf('/'));
            if (url.indexOf('//') === 0 || matchUrl !== host) {
                var xdr = new XDomainRequest();
                xdr.open(method, url);
                xdr.onprogress = function () {
                    // console.log('progress');
                };
                xdr.ontimeout = function () {
                    // console.log('timeout');
                };
                xdr.onerror = function () {
                    // console.log('error');
                };
                xdr.onload = function() {
                    // console.log('onload');
                    success(JSON.parse(xdr.responseText));
                };
                setTimeout(function () {
                    xdr.send();
                }, 0);

                return;
            }
        }
        // handle IE8 IE9 CORS end

        xhr = new XMLHttpRequest();
        xhr.open(method, url);
        if (setting.setRequestHeader) {
            for (var key in setting.setRequestHeader) {
                xhr.setRequestHeader(key, setting.setRequestHeader[key]);
            }
        }
        if (setting.withCredentials) {
            xhr.withCredentials = true;
        }
        if (setting.method !== 'GET') {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        }
        xhr.send(formUrlEncode(data));
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    var response;
                    switch (setting.response) {
                        case 'json':
                            response = JSON.parse(xhr.responseText);
                            break;
                        case 'xml':
                            response = xhr.responseXML;
                            break;
                        default:
                            response = JSON.parse(xhr.responseText);
                            break;
                    }
                    success(response);
                } else {
                    if (setting.error) {
                        setting.error(xhr.status);
                    } else {
                        return console.error('xhr.status', xhr.status);
                    }
                }
            }
        };
    }

    window.ajax = ajax;
})();<commit_msg>[HOTFIX] Fixed check XDomainRequest.<commit_after>/**
 *
 * @authors Ted Shiu (ted@gmail.com)
 * @date    2016-04-11 14:44:03
 * source https://github.com/tedshd/ajax
 */

(function() {
    function ajax(option) {
        var setting, method, url, data, xhr, success;
        if (option) {
            setting = option;
        } else {
            return console.error('not set arguments');
        }
        if (setting.method) {
            method = setting.method;
        } else {
            return console.error('not set method');
        }
        if (setting.url) {
            url = setting.url;
        } else {
            return console.error('not set url');
        }
        if (setting.success) {
            success = setting.success;
        } else {
            return console.error('not set success callback');
        }
        data = setting.data || '';
        if (setting.method === 'GET' && data) {
            url = url + '?' + formUrlEncode(data);
        }

        function formUrlEncode(obj) {
            if (!obj) {
                return '';
            }
            var urlData = '';
            for (var x in obj) {
                urlData = urlData + x + '=' + obj[x] + '&';
            }
            urlData = urlData.substr(0, (urlData.length - 1));
            return urlData;
        }

        // handle IE8 IE9 CORS
        if (typeof(XDomainRequest) !== 'undefined') {
            var host = location.host,
                matchUrl = url.replace('https://', '').replace('http://', '');
                matchUrl = matchUrl.slice(0, matchUrl.indexOf('/'));
            if (url.indexOf('//') === 0 || matchUrl !== host) {
                var xdr = new XDomainRequest();
                xdr.open(method, url);
                xdr.onprogress = function () {
                    // console.log('progress');
                };
                xdr.ontimeout = function () {
                    // console.log('timeout');
                };
                xdr.onerror = function () {
                    // console.log('error');
                };
                xdr.onload = function() {
                    // console.log('onload');
                    success(JSON.parse(xdr.responseText));
                };
                setTimeout(function () {
                    xdr.send();
                }, 0);

                return;
            }
        }
        // handle IE8 IE9 CORS end

        xhr = new XMLHttpRequest();
        xhr.open(method, url);
        if (setting.setRequestHeader) {
            for (var key in setting.setRequestHeader) {
                xhr.setRequestHeader(key, setting.setRequestHeader[key]);
            }
        }
        if (setting.withCredentials) {
            xhr.withCredentials = true;
        }
        if (setting.method !== 'GET') {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        }
        xhr.send(formUrlEncode(data));
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    var response;
                    switch (setting.response) {
                        case 'json':
                            response = JSON.parse(xhr.responseText);
                            break;
                        case 'xml':
                            response = xhr.responseXML;
                            break;
                        default:
                            response = JSON.parse(xhr.responseText);
                            break;
                    }
                    success(response);
                } else {
                    if (setting.error) {
                        setting.error(xhr.status);
                    } else {
                        return console.error('xhr.status', xhr.status);
                    }
                }
            }
        };
    }

    window.ajax = ajax;
})();<|endoftext|>