<commit_before>var jsonPathSyntax = (function() {

   // The regular expressions all start with ^ because we only want to find matches at the start of the jsonPath
   // spec that we are given. As we parse, substrings are taken so the string is consumed from left to right,
   // allowing new token regexes to match.
   //    For all regular expressions:
   //       The first subexpression is the $ (if the token is eligible to capture)
   //       The second subexpression is the name of the expected path node (if the token may have a name)

   var jsonPathClause = varArgs(function( componentRegexes ) {

      componentRegexes.unshift(/^/);

      return regexDescriptor(RegExp(componentRegexes.map(attr('source')).join('')));
   });

   var possiblyCapturing =           /(\$?)/
   ,   namedNode =                   /(\w+|\*)/
   ,   namePlaceholder =             /()/
   ,   nodeInArrayNotation =         /\["(\w+)"\]/
   ,   numberedNodeInArrayNotation = /\[(\d+|\*)\]/
   ,   fieldList =                      /{([\w ]*?)}/
   ,   optionalFieldList =           /(?:{([\w ]*?)})?/

   ,   jsonPathNamedNodeInObjectNotation     = jsonPathClause(possiblyCapturing, namedNode, optionalFieldList)
                                                                                //   foo or *

   ,   jsonPathNamedNodeInArrayNotation      = jsonPathClause(possiblyCapturing, nodeInArrayNotation, optionalFieldList)
                                                                                //   ["foo"]

   ,   jsonPathNumberedNodeInArrayNotation   = jsonPathClause(possiblyCapturing, numberedNodeInArrayNotation, optionalFieldList)
                                                                                //   [2] or [*]

   ,   jsonPathPureDuckTyping                = jsonPathClause(possiblyCapturing, namePlaceholder, fieldList)

   ,   jsonPathDoubleDot                     = jsonPathClause(/\.\./)                  //   ..

   ,   jsonPathDot                           = jsonPathClause(/\./)                    //   .

   ,   jsonPathBang                          = jsonPathClause(possiblyCapturing, /!/)  //   !

   ,   emptyString                           = jsonPathClause(/$/)                     //   nada!

   ;

   /** allows exporting of a regular expression under a generified function interface
    * @param regex
    */
   function regexDescriptor(regex) {
      return function(candidate){
         return regex.exec(candidate);
      }
   }

   /* we export only a single function. When called, this function injects into a scope the
      descriptor functions from this scope which we want to make available elsewhere.
    */
   return function (fn){
      return fn(
          lazyUnion(
            jsonPathNamedNodeInObjectNotation
          , jsonPathNamedNodeInArrayNotation
          , jsonPathNumberedNodeInArrayNotation
          , jsonPathPureDuckTyping
          )
      ,   jsonPathDoubleDot
      ,   jsonPathDot
      ,   jsonPathBang
      ,   emptyString
      );
   };

}());<commit_msg>reorganise a bit<commit_after>var jsonPathSyntax = (function() {

   // The regular expressions all start with ^ because we only want to find matches at the start of the jsonPath
   // spec that we are given. As we parse, substrings are taken so the string is consumed from left to right,
   // allowing new token regexes to match.
   //    For all regular expressions:
   //       The first subexpression is the $ (if the token is eligible to capture)
   //       The second subexpression is the name of the expected path node (if the token may have a name)

   /** allows exporting of a regular expression as a generified function interface by encapsulating just the exec
    *  function
    *
    *  @type {Function}
    *
    *  @param {RegExp} regex the regular expression to export
    *  @returns a function which is equivalent to calling exec on that regular expression
    */
   var regexDescriptor =   function regexDescriptor(regex) {
                              return function(candidate){
                                 return regex.exec(candidate);
                              }
                           },

       jsonPathClause =    varArgs(function( componentRegexes ) {

                              componentRegexes.unshift(/^/);

                              return regexDescriptor(RegExp(componentRegexes.map(attr('source')).join('')));
                           }),

       possiblyCapturing =           /(\$?)/
   ,   namedNode =                   /(\w+|\*)/
   ,   namePlaceholder =             /()/
   ,   nodeInArrayNotation =         /\["(\w+)"\]/
   ,   numberedNodeInArrayNotation = /\[(\d+|\*)\]/
   ,   fieldList =                      /{([\w ]*?)}/
   ,   optionalFieldList =           /(?:{([\w ]*?)})?/

   ,   jsonPathNamedNodeInObjectNotation     = jsonPathClause(possiblyCapturing, namedNode, optionalFieldList)
                                                                                //   foo or *

   ,   jsonPathNamedNodeInArrayNotation      = jsonPathClause(possiblyCapturing, nodeInArrayNotation, optionalFieldList)
                                                                                //   ["foo"]

   ,   jsonPathNumberedNodeInArrayNotation   = jsonPathClause(possiblyCapturing, numberedNodeInArrayNotation, optionalFieldList)
                                                                                //   [2] or [*]

   ,   jsonPathPureDuckTyping                = jsonPathClause(possiblyCapturing, namePlaceholder, fieldList)

   ,   jsonPathDoubleDot                     = jsonPathClause(/\.\./)                  //   ..

   ,   jsonPathDot                           = jsonPathClause(/\./)                    //   .

   ,   jsonPathBang                          = jsonPathClause(possiblyCapturing, /!/)  //   !

   ,   emptyString                           = jsonPathClause(/$/)                     //   nada!

   ;

   /* we export only a single function. When called, this function injects into a scope the
      descriptor functions from this scope which we want to make available elsewhere.
    */
   return function (fn){
      return fn(
         lazyUnion(
            jsonPathNamedNodeInObjectNotation
         ,  jsonPathNamedNodeInArrayNotation
         ,  jsonPathNumberedNodeInArrayNotation
         ,  jsonPathPureDuckTyping
         )
      ,  jsonPathDoubleDot
      ,  jsonPathDot
      ,  jsonPathBang
      ,  emptyString
      );
   };

}());<|endoftext|>


<commit_before>function adjustLocalToDenverTime(time) {
  let here = moment(time);
  let denver = here.clone().tz("America/Denver");
  denver.add(here.utcOffset() - denver.utcOffset(), "minutes");
  return denver.toDate();
}

Meteor.methods({
  /*
   * adjust inventory when an order is placed
   */
  "rentalProducts/inventoryAdjust": function (orderId) {
    check(orderId, String);
    // let Products = ReactionCore.Collections.Products;
    RentalProducts.Log.info(`adjusting rental inventory for order: ${orderId}`);
    let InventoryVariants = ReactionCore.Collections.InventoryVariants;
    let Orders = ReactionCore.Collections.Orders;
    const order = Orders.findOne(orderId);
    if (!order) { return false; } // If we can't find an order, exit.
    // TODO: Add store buffer days into dates to reserve;
    let datesToReserve = [];
    let detailsToReserve = [];
    const turnaroundTime = RentalProducts.getTurnaroundTime(); // Turnaround Time is defaulted to 1d
    let shippingTime = TransitTimes.calculateTotalShippingDaysByOrder(order); // Total days not business days
    let returnTime = TransitTimes.calculateTotalReturnDaysByOrder(order);
    let firstDayToReserve = TransitTimes.calculateShippingDayByOrder(order);
    let lastDayToReserve = moment(TransitTimes.calculateReturnDayByOrder(order)).add(turnaroundTime, "days").toDate();
    let counter = 0;

    let reservation = moment(
      TransitTimes.calculateShippingDayByOrder(order)
    ).twix(
      moment(TransitTimes.calculateReturnDayByOrder(order)).add(turnaroundTime, "days"), { allDay: true });
    let reservationLength = reservation.count("days");
    let iter = reservation.iterate("days"); // Momentjs iterator
    while (iter.hasNext()) {
      let reason = "In Use";
      let requestedDate = iter.next().toDate();
      let denverRequestedDate = adjustLocalToDenverTime(requestedDate);
      datesToReserve.push(denverRequestedDate);

      // Insert into Unavailable Details
      if (counter === 0) {
        reason = "Shipped"; // First reservation day is when order is shipped from warehouse
      } else if (counter === shippingTime) {
        reason = "Delivered";         // Second day through transitTime is delivery
      } else if (counter - 1 < shippingTime) {
        reason = "In Transit";         // Second day through transitTime is delivery
      } else if (counter === reservationLength - returnTime - turnaroundTime - 1) {
        reason = "Return Shipped";
      } else if (counter === reservationLength - turnaroundTime - 1) {
        reason = "Return Delivered";
      } else if (counter >= reservationLength - turnaroundTime) {
        reason = "Return Processing";
      } else if (counter >= reservationLength - returnTime - turnaroundTime) {
        reason = "Return In Transit";
      }

      detailsToReserve.push({
        date: denverRequestedDate,
        reason: reason,
        orderId: orderId
      });
      counter++;
    }  // Create array of requested dates

    for (let item of order.items) {
      if (item.variants.functionalType === "rentalVariant") {
        /* push start date minus pre-buffer days
         * loop through adding one day to array
         * stop when we get to end day + trailing buffer
         */
        let variantIds = Meteor.call("rentalProducts/checkInventoryAvailability",
                                      item.variants._id,
                                      {endTime: lastDayToReserve, startTime: firstDayToReserve},
                                      item.quantity, false);
        // Log details about booking item
        RentalProducts.Log.info(`Checked to see if ${item.variants._id} had ${item.quantity} available and it had`
          + ` ${variantIds.length} available`);
        RentalProducts.Log.info(`${variantIds} were the variants that should be reserved from`
          + ` ${firstDayToReserve} to ${lastDayToReserve} inclusive of shipping and turnaround time`);

        // This should be caught before getting to this point. It's too late here.
        if (variantIds.length !== item.quantity) {
          RentalProducts.Log.error(`Requested ${item.quantity} of ${item.variants._id}, but only ${variantIds.length} were available.`);
          // Bail
          throw new Meteor.Error(403, `Requested quantity not available to book for ${item.variants._id}`);
        }

        // Not using $in because we need to determine the correct position
        // to insert the new dates separately for each inventoryVariant
        for (let variantId of variantIds) {
          let reservedDates = InventoryVariants.findOne({
            _id: variantId
          }, {fields: {unavailableDates: 1}}).unavailableDates;

          // We take the time to insert unavailable dates in ascending date order
          // find the position that we should insert the reserved dates
          positionToInsert = _.sortedIndex(reservedDates, datesToReserve[0]);

          // insert datesToReserve into the correct variants at the correct position
          InventoryVariants.update({_id: variantId}, {
            $inc: {
              numberOfDatesBooked: datesToReserve.length
            },
            $push: {
              unavailableDates: {
                $each: datesToReserve,
                $position: positionToInsert
              },
              unavailableDetails: {
                $each: detailsToReserve,
                $position: positionToInsert
              }
            }
          });
        }
      }
    }
  }
});
<commit_msg>added method to remove inventory dates<commit_after>function adjustLocalToDenverTime(time) {
  let here = moment(time);
  let denver = here.clone().tz("America/Denver");
  denver.add(here.utcOffset() - denver.utcOffset(), "minutes");
  return denver.toDate();
}

Meteor.methods({
  /*
   * adjust inventory when an order is placed
   */
  "rentalProducts/inventoryAdjust": function (orderId) {
    check(orderId, String);
    // let Products = ReactionCore.Collections.Products;
    RentalProducts.Log.info(`adjusting rental inventory for order: ${orderId}`);
    let InventoryVariants = ReactionCore.Collections.InventoryVariants;
    let Orders = ReactionCore.Collections.Orders;
    const order = Orders.findOne(orderId);
    if (!order) { return false; } // If we can't find an order, exit.
    // TODO: Add store buffer days into dates to reserve;
    let datesToReserve = [];
    let detailsToReserve = [];
    const turnaroundTime = RentalProducts.getTurnaroundTime(); // Turnaround Time is defaulted to 1d
    let shippingTime = TransitTimes.calculateTotalShippingDaysByOrder(order); // Total days not business days
    let returnTime = TransitTimes.calculateTotalReturnDaysByOrder(order);
    let firstDayToReserve = TransitTimes.calculateShippingDayByOrder(order);
    let lastDayToReserve = moment(TransitTimes.calculateReturnDayByOrder(order)).add(turnaroundTime, "days").toDate();
    let counter = 0;

    let reservation = moment(
      TransitTimes.calculateShippingDayByOrder(order)
    ).twix(
      moment(TransitTimes.calculateReturnDayByOrder(order)).add(turnaroundTime, "days"), { allDay: true });
    let reservationLength = reservation.count("days");
    let iter = reservation.iterate("days"); // Momentjs iterator
    while (iter.hasNext()) {
      let reason = "In Use";
      let requestedDate = iter.next().toDate();
      let denverRequestedDate = adjustLocalToDenverTime(requestedDate);
      datesToReserve.push(denverRequestedDate);

      // Insert into Unavailable Details
      if (counter === 0) {
        reason = "Shipped"; // First reservation day is when order is shipped from warehouse
      } else if (counter === shippingTime) {
        reason = "Delivered";         // Second day through transitTime is delivery
      } else if (counter - 1 < shippingTime) {
        reason = "In Transit";         // Second day through transitTime is delivery
      } else if (counter === reservationLength - returnTime - turnaroundTime - 1) {
        reason = "Return Shipped";
      } else if (counter === reservationLength - turnaroundTime - 1) {
        reason = "Return Delivered";
      } else if (counter >= reservationLength - turnaroundTime) {
        reason = "Return Processing";
      } else if (counter >= reservationLength - returnTime - turnaroundTime) {
        reason = "Return In Transit";
      }

      detailsToReserve.push({
        date: denverRequestedDate,
        reason: reason,
        orderId: orderId
      });
      counter++;
    }  // Create array of requested dates

    for (let item of order.items) {
      if (item.variants.functionalType === "rentalVariant") {
        /* push start date minus pre-buffer days
         * loop through adding one day to array
         * stop when we get to end day + trailing buffer
         */
        let variantIds = Meteor.call("rentalProducts/checkInventoryAvailability",
                                      item.variants._id,
                                      {endTime: lastDayToReserve, startTime: firstDayToReserve},
                                      item.quantity, false);
        // Log details about booking item
        RentalProducts.Log.info(`Checked to see if ${item.variants._id} had ${item.quantity} available and it had`
          + ` ${variantIds.length} available`);
        RentalProducts.Log.info(`${variantIds} were the variants that should be reserved from`
          + ` ${firstDayToReserve} to ${lastDayToReserve} inclusive of shipping and turnaround time`);

        // This should be caught before getting to this point. It's too late here.
        if (variantIds.length !== item.quantity) {
          RentalProducts.Log.error(`Requested ${item.quantity} of ${item.variants._id}, but only ${variantIds.length} were available.`);
          // Bail
          throw new Meteor.Error(403, `Requested quantity not available to book for ${item.variants._id}`);
        }

        // Not using $in because we need to determine the correct position
        // to insert the new dates separately for each inventoryVariant
        for (let variantId of variantIds) {
          let reservedDates = InventoryVariants.findOne({
            _id: variantId
          }, {fields: {unavailableDates: 1}}).unavailableDates;

          // We take the time to insert unavailable dates in ascending date order
          // find the position that we should insert the reserved dates
          positionToInsert = _.sortedIndex(reservedDates, datesToReserve[0]);

          // insert datesToReserve into the correct variants at the correct position
          InventoryVariants.update({_id: variantId}, {
            $inc: {
              numberOfDatesBooked: datesToReserve.length
            },
            $push: {
              unavailableDates: {
                $each: datesToReserve,
                $position: positionToInsert
              },
              unavailableDetails: {
                $each: detailsToReserve,
                $position: positionToInsert
              }
            }
          });
        }
      }
    }
  },
  "rentalProducts/inventoryUnbook": function (orderId) {
    check(orderId, String);
    const impactedInventory = InventoryVariants.find({"unavailableDetails.orderId": orderId}).fetch();
    _.each(impactedInventory, function (inventory) {
      let removalDetails = _.where(inventory.unavailableDetails, {orderId: orderId});
      // grab these for the arrays
      let firstDate = removalDetails[0].date;
      let lastDate = removalDetails[removalDetails.length - 1].date;
      // this will return an array of objects without the ones that have an orderId
      let updatedUnavailableDetails = _.difference(inventory.unavailableDetails, removalDetails);
      // turn the unavailable dates to Numbers array, then get index of first and last date
      let firstIndex = inventory.unavailableDates.map(Number).indexOf(+firstDate);
      let lastIndex = inventory.unavailableDates.map(Number).indexOf(+lastDate);
      let spliceAmount = lastIndex - firstIndex + 1; // to be inclusvie;
      inventory.unavailableDates.splice(firstIndex, spliceAmount);
      InventoryVariants.update({
        _id: inventory._id
      }, {
        $set: {
          unavailableDetails: updatedUnavailableDetails,
          unavailableDates: inventory.unavailableDates,
          numberOfDatesBooked: inventory.unavailableDates.length
        }
      });
      ReactionCore.Log.warn(`Canceled Order #${orderId} removed inventory dates for Inventory item ${inventory._id}`);
    });
  }
});
<|endoftext|>


<commit_before>/**
 * Created by Sahib Pandori on 3/28/2017.
 */

/**
 * Contains a list of words used in the speech commands
 */
var allowedWords = [
    'block',
    'connect',
    'get',
    'delete',
    'under',
    'right',
    'left',
    'pen',
    'turn',
    'change',
    'to',
    'up',
    'down',
    'run',
    'the',
    'program',
    'a',
    'repeat',
    'in',
    'inside'
];

/**
 * Contains common mistakes made by the speech recognizer and their actual (probably) values
 */
var corrections = {

    'pattern': 'get a turn',
    'kinect': 'connect',
    '2': 'to',
    'white': 'right',
    'write': 'right',
    'up and': 'a pen',
    'for': '4',
    'foreign': '4',
    'ford': '4',
    'war': '4',
    'lakhs': 'block',
    'a pin': 'up in',
    'hey': 'hey jerry',
    'greta': 'get a',
    'text': 'connect',
    'k\'nex': 'connect',
    'blog': 'block',
    'xbox': 'connect block',
    'jetta': 'get a',
    'return': 'get a turn',
    'today': 'get',
    'can you': 'get a',
    'sex': '6',
    'free': 'delete',
    'tube': '2',
    'walkthrough': 'block 3',
    'force': '4',
    'netflix free': 'connect block 3',
    '100': '1 under',
    '200': '2 under',
    '300': '3 under',
    '400': '4 under',
    '500': '5 under',
    '600': '6 under',
    '700': '7 under',
    '800': '8 under',
    '900': '9 under',
    'black tooth': 'block 2',
    'clock': 'block',
    'on': 'block',
    'tattoo pattern': 'get a turn',
    'lot': 'block',
    'blockhead': 'block 8',
    'laugh': 'left',
    'price': '5',
    'glock': 'block',
    'got': 'get',
    'death': 'get',
    'ken': 'pen',
    'tutuapp': 'to up'
};

/**
 * Return all possible corrections using only the allowed words (and numbers)
 * @param speech The speech command that isn't recognized
 */
function correct(speech) {

    var possibleCommands = [];
    var words = speech.split(" ");
    for (word in words) {
        if (!(word in allowedWords)) {
            // TODO (sahibgoa): Get all possible corrections and return those
        }
    }

}<commit_msg>finished basic correcting method<commit_after>/**
 * Created by Sahib Pandori on 3/28/2017.
 */

/**
 * Contains a list of words used in the speech commands
 */
var allowedWords = [
    'block',
    'connect',
    'get',
    'delete',
    'under',
    'right',
    'left',
    'pen',
    'turn',
    'change',
    'to',
    'up',
    'down',
    'run',
    'the',
    'program',
    'a',
    'repeat',
    'in',
    'inside'
];

/**
 * Contains common mistakes made by the speech recognizer and their actual (probably) values
 */
var corrections = {

    'pattern': 'get a turn',
    'kinect': 'connect',
    '2': 'to',
    'white': 'right',
    'write': 'right',
    'up and': 'a pen',
    'for': '4',
    'foreign': '4',
    'ford': '4',
    'war': '4',
    'lakhs': 'block',
    'a pin': 'up in',
    'hey': 'hey jerry',
    'greta': 'get a',
    'text': 'connect',
    'k\'nex': 'connect',
    'blog': 'block',
    'xbox': 'connect block',
    'jetta': 'get a',
    'return': 'get a turn',
    'today': 'get',
    'can you': 'get a',
    'sex': '6',
    'free': 'delete',
    'tube': '2',
    'walkthrough': 'block 3',
    'force': '4',
    'netflix free': 'connect block 3',
    '100': '1 under',
    '200': '2 under',
    '300': '3 under',
    '400': '4 under',
    '500': '5 under',
    '600': '6 under',
    '700': '7 under',
    '800': '8 under',
    '900': '9 under',
    '1:20': '120',
    '1:44': '144',
    'black tooth': 'block 2',
    'clock': 'block',
    'on': 'block',
    'tattoo pattern': 'get a turn',
    'lot': 'block',
    'blockhead': 'block 8',
    'laugh': 'left',
    'price': '5',
    'glock': 'block',
    'got': 'get',
    'death': 'get',
    'ken': 'pen',
    'tutuapp': 'to up'
};

/**
 * Return all possible corrections using only the allowed words (and numbers)
 * @param speech The speech command that isn't recognized
 */
function correct(speech) {

    var possibleCommands = [];
    var words = speech.split(" ");
    // Stores the corrected command
    var command = '';
    // Stores whether all given words in the command are valid
    var invalid = false;

    // Convert each invalid word to a allowed word without searching for wrong pairs of words
    for (i = 0; i < words.length; i++) {
        // Check for individual invalid words if pairs aren't found
        if (!(words[i] in allowedWords)) {
            if (words[i] in corrections) {
                command += corrections[words[i]] + ' ';
            } else {
                invalid = true;
            }
        }
        // Add the word to the command as it's an allowed word
        else {
            command += words[i] + ' ';
        }
    }

    // If all words in the command were valid, then add it as a possible corrected command
    if (!invalid) {
        possibleCommands.add(command.trim());
    }

    invalid = false;
    command = '';

    // Search for any pairs of invalid words and replace them with valid ones (if not search for invalid individual words)
    for (i = 0; i < words.length - 1; i++) {
        // Check for a pair of invalid words
        if (!(words[i] in allowedWords) && !(words[i+1] in allowedWords)) {
            if ((words[i] + ' ' + words[i+1]) in corrections) {
                command += words[i] + ' ' + words[i+1] + ' ';
            }
        }
        // Check for individual invalid words if pairs aren't found
        else if (!(words[i] in allowedWords)) {
            if (words[i] in corrections) {
                command += corrections[words[i]] + ' ';
            } else {
                invalid = true;
            }
        }
        // Add the word to the command as it's an allowed word
        else {
            command += words[i] + ' ';
        }
    }

    // If all words in the command are valid and it's not the same as the previous value (if there is one), then add
    // it as a possible corrected command
    if (!invalid) {
        if ((possibleCommands.length > 0 && command != possibleCommands[0]) || possibleCommands.length == 0) {
            possibleCommands.add(command.trim());
        }
    }

}<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict'

const KNOWN_ARGS = {
    help: Boolean,
    version: Boolean
}

const SHORTHANDS = {
    h: [ '--help' ],
    v: [ '--version' ]
}

const commands = require('./commands'),
      args     = require('nopt')(KNOWN_ARGS, SHORTHANDS),
      version  = require('../package.json').version,
      command  = args.argv.remain[ 0 ]

if (args.version)
    console.log(version)
else if (args.help)
    commands.help()
else if (command in commands)
    commands[ command ]
        .apply(null, args.remain.slice(1))
        .catch(err => {
            console.error()

            const debug = process.env.DEBUG

            if (debug && ~debug.indexOf('ciex'))
                console.error(err.stack)
            else
                console.error(err.message)

            console.error()
        })
else
    commands.help()
<commit_msg>fix argv parsing<commit_after>#!/usr/bin/env node

'use strict'

const KNOWN_ARGS = {
    help: Boolean,
    version: Boolean
}

const SHORTHANDS = {
    h: [ '--help' ],
    v: [ '--version' ]
}

const commands = require('./commands'),
      args     = require('nopt')(KNOWN_ARGS, SHORTHANDS),
      argv     = args.argv.remain.slice(1),
      version  = require('../package.json').version,
      command  = args.argv.remain[ 0 ]

if (args.version)
    console.log(version)
else if (args.help)
    commands.help()
else if (command in commands)
    commands[ command ]
        .apply(null, argv)
        .catch(err => {
            console.error()

            const debug = process.env.DEBUG

            if (debug && ~debug.indexOf('ciex'))
                console.error(err.stack)
            else
                console.error(err.message)

            console.error()
        })
else
    commands.help()
<|endoftext|>


<commit_before>define(
  [
    'dispatchers/AppDispatcher',
    'constants/InstanceConstants',
    'react',
    'globals',
    'context',
    'controllers/NotificationController',
    'components/modals/CancelConfirmModal.react',
    'components/modals/InstanceSuspendBody.react',
    'components/modals/InstanceResumeBody.react',
    'components/modals/InstanceStopBody.react',
    'components/modals/InstanceStartBody.react',
    'components/modals/InstanceDeleteBody.react',
    'components/modals/InstanceLaunchModal.react',
    'url'
  ],
  function (AppDispatcher, InstanceConstants, React, globals, context, NotificationController, CancelConfirmModal, InstanceSuspendBody, InstanceResumeBody, InstanceStopBody, InstanceStartBody, InstanceDeleteBody, InstanceLaunchModal, URL) {

    return {
      updateInstanceAttributes: function (instance, newAttributes) {
        instance.set(newAttributes);
        AppDispatcher.handleRouteAction({
          actionType: InstanceConstants.INSTANCE_UPDATE,
          instance: instance
        });
      },

      suspend: function (instance) {

        var onConfirm = function () {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_SUSPEND,
            instance: instance
          });
        };

        var modal = CancelConfirmModal({
          header: "Suspend Instance",
          confirmButtonMessage: "Suspend Instance",
          onConfirm: onConfirm,
          body: InstanceSuspendBody.build()
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      resume: function(instance){

        var onConfirm = function () {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_RESUME,
            instance: instance
          });
        };

        var modal = CancelConfirmModal({
          header: "Resume Instance",
          confirmButtonMessage: "Resume Instance",
          onConfirm: onConfirm,
          body: InstanceResumeBody.build()
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      stop: function(instance){

        var onConfirm = function () {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_STOP,
            instance: instance
          });
        };

        var modal = CancelConfirmModal({
          header: "Stop Instance",
          confirmButtonMessage: "Stop Instance",
          onConfirm: onConfirm,
          body: InstanceStopBody.build()
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      start: function(instance){

        var onConfirm = function () {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_START,
            instance: instance
          });
        };

        var modal = CancelConfirmModal({
          header: "Start Instance",
          confirmButtonMessage: "Start Instance",
          onConfirm: onConfirm,
          body: InstanceStartBody.build()
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      terminate: function(instance, redirectUrl){

        var onConfirm = function () {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_TERMINATE,
            instance: instance
          });
          Backbone.history.navigate(redirectUrl, {trigger: true});
        };

        var modal = CancelConfirmModal({
          header: "Are you sure you want to terminate this instance?",
          confirmButtonMessage: "Yes, terminate this instance",
          onConfirm: onConfirm,
          body: InstanceDeleteBody.build(instance)
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      launch: function(application){

        var onConfirm = function (identity, machineId, sizeId, instanceName, project) {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_LAUNCH,
            identity: identity,
            machineId: machineId,
            sizeId: sizeId,
            instanceName: instanceName,
            project: project
          });
          // Since this is triggered from the images page, navigate off
          // that page and back to the instance list so the user can see
          // their instance being created
          Backbone.history.navigate('instances', {trigger: true});
        };

        var onCancel = function(){
          // Important! We need to un-mount the component so it un-registers from Stores and
          // also so that we can relaunch it again later.
          React.unmountComponentAtNode(document.getElementById('modal'));
        };

        var modal = InstanceLaunchModal({
          header: application.get('name'),
          application: application,
          confirmButtonMessage: "Launch instance",
          onConfirm: onConfirm,
          onCancel: onCancel,
          handleHidden: onCancel
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      requestImage: function(instance, requestData){
        var providerId = instance.getCreds().provider_id;
        var identityId = instance.getCreds().identity_id;
        var requestUrl = globals.API_ROOT + "/provider/" + providerId + "/identity/" + identityId + "/request_image" + globals.slash();

        $.ajax({
          url: requestUrl,
          type: 'POST',
          data: JSON.stringify(requestData),
          dataType: 'json',
          contentType: 'application/json',
          success: function (model) {
            NotificationController.info(null, "An image of your instance has been requested");
          },
          error: function (response, status, error) {
            NotificationController.error(null, response.responseText);
          }
        });
      },

      reportInstance: function(instance, reportInfo){
        var reportUrl = globals.API_ROOT + "/email/support" + globals.slash();

        var problemText = "";
        if(reportInfo.problems){
          _.each(reportInfo.problems, function(problem){
            problemText = problemText + "  -" + problem + "\n";
          })
        }

        var username = context.profile.get('username');

        var reportData = {
          username: username,
          message: "Instance IP: " + instance.get('ip_address') + "\n" +
                   "Instance ID: " + instance.id + "\n" +
                   "Provider ID: " + instance.get('identity').provider + "\n" +
                   "\n" +
                   "Problems" + "\n" +
                   problemText + "\n" +
                   "Message \n" +
                   reportInfo.message + "\n",
          subject: "Atmosphere Instance Report from " + username
        };

        $.ajax({
          url: reportUrl,
          type: 'POST',
          data: JSON.stringify(reportData),
          dataType: 'json',
          contentType: 'application/json',
          success: function (model) {
            NotificationController.info(null, "Your instance problems have been sent to support.");
            var instanceUrl = URL.instance(instance);
            Backbone.history.navigate(instanceUrl, {trigger: true});
          },
          error: function (response, status, error) {
            NotificationController.error(null, response.responseText);
          }
        });
      }

    };

  });
<commit_msg>redirect user to project page after launch<commit_after>define(
  [
    'dispatchers/AppDispatcher',
    'constants/InstanceConstants',
    'react',
    'globals',
    'context',
    'controllers/NotificationController',
    'components/modals/CancelConfirmModal.react',
    'components/modals/InstanceSuspendBody.react',
    'components/modals/InstanceResumeBody.react',
    'components/modals/InstanceStopBody.react',
    'components/modals/InstanceStartBody.react',
    'components/modals/InstanceDeleteBody.react',
    'components/modals/InstanceLaunchModal.react',
    'url'
  ],
  function (AppDispatcher, InstanceConstants, React, globals, context, NotificationController, CancelConfirmModal, InstanceSuspendBody, InstanceResumeBody, InstanceStopBody, InstanceStartBody, InstanceDeleteBody, InstanceLaunchModal, URL) {

    return {
      updateInstanceAttributes: function (instance, newAttributes) {
        instance.set(newAttributes);
        AppDispatcher.handleRouteAction({
          actionType: InstanceConstants.INSTANCE_UPDATE,
          instance: instance
        });
      },

      suspend: function (instance) {

        var onConfirm = function () {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_SUSPEND,
            instance: instance
          });
        };

        var modal = CancelConfirmModal({
          header: "Suspend Instance",
          confirmButtonMessage: "Suspend Instance",
          onConfirm: onConfirm,
          body: InstanceSuspendBody.build()
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      resume: function(instance){

        var onConfirm = function () {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_RESUME,
            instance: instance
          });
        };

        var modal = CancelConfirmModal({
          header: "Resume Instance",
          confirmButtonMessage: "Resume Instance",
          onConfirm: onConfirm,
          body: InstanceResumeBody.build()
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      stop: function(instance){

        var onConfirm = function () {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_STOP,
            instance: instance
          });
        };

        var modal = CancelConfirmModal({
          header: "Stop Instance",
          confirmButtonMessage: "Stop Instance",
          onConfirm: onConfirm,
          body: InstanceStopBody.build()
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      start: function(instance){

        var onConfirm = function () {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_START,
            instance: instance
          });
        };

        var modal = CancelConfirmModal({
          header: "Start Instance",
          confirmButtonMessage: "Start Instance",
          onConfirm: onConfirm,
          body: InstanceStartBody.build()
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      terminate: function(instance, redirectUrl){

        var onConfirm = function () {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_TERMINATE,
            instance: instance
          });
          Backbone.history.navigate(redirectUrl, {trigger: true});
        };

        var modal = CancelConfirmModal({
          header: "Are you sure you want to terminate this instance?",
          confirmButtonMessage: "Yes, terminate this instance",
          onConfirm: onConfirm,
          body: InstanceDeleteBody.build(instance)
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      launch: function(application){

        var onConfirm = function (identity, machineId, sizeId, instanceName, project) {
          AppDispatcher.handleRouteAction({
            actionType: InstanceConstants.INSTANCE_LAUNCH,
            identity: identity,
            machineId: machineId,
            sizeId: sizeId,
            instanceName: instanceName,
            project: project
          });
          // Since this is triggered from the images page, navigate off
          // that page and back to the instance list so the user can see
          // their instance being created
          var redirectUrl = URL.project(project, {relative: true});
          Backbone.history.navigate(redirectUrl, {trigger: true});
        };

        var onCancel = function(){
          // Important! We need to un-mount the component so it un-registers from Stores and
          // also so that we can relaunch it again later.
          React.unmountComponentAtNode(document.getElementById('modal'));
        };

        var modal = InstanceLaunchModal({
          header: application.get('name'),
          application: application,
          confirmButtonMessage: "Launch instance",
          onConfirm: onConfirm,
          onCancel: onCancel,
          handleHidden: onCancel
        });

        React.renderComponent(modal, document.getElementById('modal'));
      },

      requestImage: function(instance, requestData){
        var providerId = instance.getCreds().provider_id;
        var identityId = instance.getCreds().identity_id;
        var requestUrl = globals.API_ROOT + "/provider/" + providerId + "/identity/" + identityId + "/request_image" + globals.slash();

        $.ajax({
          url: requestUrl,
          type: 'POST',
          data: JSON.stringify(requestData),
          dataType: 'json',
          contentType: 'application/json',
          success: function (model) {
            NotificationController.info(null, "An image of your instance has been requested");
          },
          error: function (response, status, error) {
            NotificationController.error(null, response.responseText);
          }
        });
      },

      reportInstance: function(instance, reportInfo){
        var reportUrl = globals.API_ROOT + "/email/support" + globals.slash();

        var problemText = "";
        if(reportInfo.problems){
          _.each(reportInfo.problems, function(problem){
            problemText = problemText + "  -" + problem + "\n";
          })
        }

        var username = context.profile.get('username');

        var reportData = {
          username: username,
          message: "Instance IP: " + instance.get('ip_address') + "\n" +
                   "Instance ID: " + instance.id + "\n" +
                   "Provider ID: " + instance.get('identity').provider + "\n" +
                   "\n" +
                   "Problems" + "\n" +
                   problemText + "\n" +
                   "Message \n" +
                   reportInfo.message + "\n",
          subject: "Atmosphere Instance Report from " + username
        };

        $.ajax({
          url: reportUrl,
          type: 'POST',
          data: JSON.stringify(reportData),
          dataType: 'json',
          contentType: 'application/json',
          success: function (model) {
            NotificationController.info(null, "Your instance problems have been sent to support.");
            var instanceUrl = URL.instance(instance);
            Backbone.history.navigate(instanceUrl, {trigger: true});
          },
          error: function (response, status, error) {
            NotificationController.error(null, response.responseText);
          }
        });
      }

    };

  });
<|endoftext|>


<commit_before>const Redis = require('ioredis'); // eslint-disable-line import/no-unresolved
const { isEmpty } = require('lodash');

const client = new Redis(process.env.REDIS_URL, {
  keyPrefix: 'oidc:',
});

const grantable = new Set([
  'AccessToken',
  'AuthorizationCode',
  'RefreshToken',
  'DeviceCode',
]);

function grantKeyFor(id) {
  return `grant:${id}`;
}

function userCodeKeyFor(userCode) {
  return `userCode:${userCode}`;
}

class RedisAdapter {
  constructor(name) {
    this.name = name;
  }

  async upsert(id, payload, expiresIn) {
    const key = this.key(id);
    const store = grantable.has(this.name) ? {
      dump: JSON.stringify(payload),
      ...(payload.grantId ? { grantId: payload.grantId } : undefined),
    } : JSON.stringify(payload);

    const multi = client.multi();
    multi[grantable.has(this.name) ? 'hmset' : 'set'](key, store);

    if (expiresIn) {
      multi.expire(key, expiresIn);
    }

    if (payload.grantId) {
      const grantKey = grantKeyFor(payload.grantId);
      multi.rpush(grantKey, key);
      const ttl = await client.ttl(grantKey);
      if (expiresIn > ttl) {
        multi.expire(grantKey, expiresIn);
      }
    }

    if (payload.userCode) {
      const userCodeKey = userCodeKeyFor(payload.userCode);
      multi.set(userCodeKey, id);
      multi.expire(userCodeKey, expiresIn);
    }

    await multi.exec();
  }

  async find(id) {
    const data = grantable.has(this.name)
      ? await client.hgetall(this.key(id))
      : await client.get(this.key(id));

    if (isEmpty(data)) {
      return undefined;
    }

    if (typeof data === 'string') {
      return JSON.parse(data);
    }
    const { dump, ...rest } = data;
    return {
      ...rest,
      ...JSON.parse(dump),
    };
  }

  async findByUserCode(userCode) {
    const id = await client.get(userCodeKeyFor(userCode));
    return this.find(id);
  }

  async destroy(id) {
    const key = this.key(id);
    if (grantable.has(this.name)) {
      const multi = client.multi();
      const grantId = await client.hget(key, 'grantId');
      const tokens = await client.lrange(grantKeyFor(grantId), 0, -1);
      tokens.forEach(token => multi.del(token));
      multi.del(grantKeyFor(grantId));
      await multi.exec();
    } else {
      await client.del(key);
    }
  }

  async consume(id) {
    await client.hset(this.key(id), 'consumed', Math.floor(Date.now() / 1000));
  }

  key(id) {
    return `${this.name}:${id}`;
  }
}

module.exports = RedisAdapter;
<commit_msg>example: note on grant length lists and LTRIM<commit_after>const Redis = require('ioredis'); // eslint-disable-line import/no-unresolved
const { isEmpty } = require('lodash');

const client = new Redis(process.env.REDIS_URL, {
  keyPrefix: 'oidc:',
});

const grantable = new Set([
  'AccessToken',
  'AuthorizationCode',
  'RefreshToken',
  'DeviceCode',
]);

function grantKeyFor(id) {
  return `grant:${id}`;
}

function userCodeKeyFor(userCode) {
  return `userCode:${userCode}`;
}

class RedisAdapter {
  constructor(name) {
    this.name = name;
  }

  async upsert(id, payload, expiresIn) {
    const key = this.key(id);
    const store = grantable.has(this.name) ? {
      dump: JSON.stringify(payload),
      ...(payload.grantId ? { grantId: payload.grantId } : undefined),
    } : JSON.stringify(payload);

    const multi = client.multi();
    multi[grantable.has(this.name) ? 'hmset' : 'set'](key, store);

    if (expiresIn) {
      multi.expire(key, expiresIn);
    }

    if (payload.grantId) {
      const grantKey = grantKeyFor(payload.grantId);
      multi.rpush(grantKey, key);
      // if you're seeing grant key lists growing out of acceptable proportions consider using LTRIM
      // here to trim the list to an appropriate length
      const ttl = await client.ttl(grantKey);
      if (expiresIn > ttl) {
        multi.expire(grantKey, expiresIn);
      }
    }

    if (payload.userCode) {
      const userCodeKey = userCodeKeyFor(payload.userCode);
      multi.set(userCodeKey, id);
      multi.expire(userCodeKey, expiresIn);
    }

    await multi.exec();
  }

  async find(id) {
    const data = grantable.has(this.name)
      ? await client.hgetall(this.key(id))
      : await client.get(this.key(id));

    if (isEmpty(data)) {
      return undefined;
    }

    if (typeof data === 'string') {
      return JSON.parse(data);
    }
    const { dump, ...rest } = data;
    return {
      ...rest,
      ...JSON.parse(dump),
    };
  }

  async findByUserCode(userCode) {
    const id = await client.get(userCodeKeyFor(userCode));
    return this.find(id);
  }

  async destroy(id) {
    const key = this.key(id);
    if (grantable.has(this.name)) {
      const multi = client.multi();
      const grantId = await client.hget(key, 'grantId');
      const tokens = await client.lrange(grantKeyFor(grantId), 0, -1);
      tokens.forEach(token => multi.del(token));
      multi.del(grantKeyFor(grantId));
      await multi.exec();
    } else {
      await client.del(key);
    }
  }

  async consume(id) {
    await client.hset(this.key(id), 'consumed', Math.floor(Date.now() / 1000));
  }

  key(id) {
    return `${this.name}:${id}`;
  }
}

module.exports = RedisAdapter;
<|endoftext|>


<commit_before>var a = document.querySelector('.a');
var b = document.querySelector('.b');
var submit = document.querySelector('.onsubmit');
var clear = document.querySelector('.clear');
var answer1 = document.querySelector('.answer1');
var answer2 = document.querySelector('.answer2');

submit.addEventListener('click', onSubmit);
clear.addEventListener('click', onClear);
var simplifyAgain = document.querySelector('.simplify-again');

function onClear(){
	a.value = "";
	b.value = "";
	a.focus();
}

function check(aV, bV){
	console.log('hi');
	var answer1C;
	var answer2C;

	answer1C = aV;
	answer2C = bV;
	if(Number.isInteger(answer1C / 10) && Number.isInteger(answer2C / 10)){
		answer1C = answer1C / 10;
		answer2C = answer2C / 10;
		answer1.textContent = answer1C;
		answer2.textContent = answer2C;
	}else if(Number.isInteger(answer1C / 2) && Number.isInteger(answer2C / 2)){
		answer1C = answer1C / 2;
		answer2C = answer2C / 2;
		answer1.textContent = answer1C;
		answer2.textContent = answer2C;
	}else if(Number.isInteger(answer1C / 5) && Number.isInteger(answer2C / 5)){
		answer1C = answer1C / 5;
		answer2C = answer2C / 5;
		answer1.textContent = answer1C;
		answer2.textContent = answer2C;
	}else if(Number.isInteger(answer1C / 3) && Number.isInteger(answer2C / 3)){
		answer1C = answer1C / 3;
		answer2C = answer2C / 3;
		answer1.textContent = answer1C;
		answer2.textContent = answer2C;
	}

	simplifyAgain.onclick = function(){
		if(Number.isInteger(answer1C / 10) && Number.isInteger(answer2C / 10)){
			answer1C = answer1C / 10;
			answer2C = answer2C / 10;
			answer1.textContent = answer1C;
			answer2.textContent = answer2C;
		}else if(Number.isInteger(answer1C / 2) && Number.isInteger(answer2C / 2)){
			answer1C = answer1C / 2;
			answer2C = answer2C / 2;
			answer1.textContent = answer1C;
			answer2.textContent = answer2C;
		}else if(Number.isInteger(answer1C / 5) && Number.isInteger(answer2C / 5)){
			answer1C = answer1C / 5;
			answer2C = answer2C / 5;
			answer1.textContent = answer1C;
			answer2.textContent = answer2C;
		}else if(Number.isInteger(answer1C / 3) && Number.isInteger(answer2C / 3)){
			answer1C = answer1C / 3;
			answer2C = answer2C / 3;
			answer1.textContent = answer1C;
			answer2.textContent = answer2C;
		}else{
			answer1.textContent = answer1C;
			answer2.textContent = answer2C;
		}
	}
}

function onSubmit(){
	aV = parseInt(a.value);
	bV = parseInt(b.value);
	check(aV, bV);
}
<commit_msg>another quick bug fix<commit_after>var a = document.querySelector('.a');
var b = document.querySelector('.b');
var submit = document.querySelector('.onsubmit');
var clear = document.querySelector('.clear');
var answer1 = document.querySelector('.answer1');
var answer2 = document.querySelector('.answer2');

submit.addEventListener('click', onSubmit);
clear.addEventListener('click', onClear);
var simplifyAgain = document.querySelector('.simplify-again');

function onClear(){
	a.value = "";
	b.value = "";
	a.focus();
}

function check(aV, bV){
	console.log('hi');
	var answer1C;
	var answer2C;

	answer1C = aV;
	answer2C = bV;
	if(Number.isInteger(answer1C / 10) && Number.isInteger(answer2C / 10)){
		answer1C = answer1C / 10;
		answer2C = answer2C / 10;
		answer1.textContent = answer1C;
		answer2.textContent = answer2C;
	}else if(Number.isInteger(answer1C / 2) && Number.isInteger(answer2C / 2)){
		answer1C = answer1C / 2;
		answer2C = answer2C / 2;
		answer1.textContent = answer1C;
		answer2.textContent = answer2C;
	}else if(Number.isInteger(answer1C / 5) && Number.isInteger(answer2C / 5)){
		answer1C = answer1C / 5;
		answer2C = answer2C / 5;
		answer1.textContent = answer1C;
		answer2.textContent = answer2C;
	}else if(Number.isInteger(answer1C / 3) && Number.isInteger(answer2C / 3)){
		answer1C = answer1C / 3;
		answer2C = answer2C / 3;
		answer1.textContent = answer1C;
		answer2.textContent = answer2C;
	}else{
		answer1.textContent = answer1C;
		answer2C.textContent = answer2C;
	}

	simplifyAgain.onclick = function(){
		if(Number.isInteger(answer1C / 10) && Number.isInteger(answer2C / 10)){
			answer1C = answer1C / 10;
			answer2C = answer2C / 10;
			answer1.textContent = answer1C;
			answer2.textContent = answer2C;
		}else if(Number.isInteger(answer1C / 2) && Number.isInteger(answer2C / 2)){
			answer1C = answer1C / 2;
			answer2C = answer2C / 2;
			answer1.textContent = answer1C;
			answer2.textContent = answer2C;
		}else if(Number.isInteger(answer1C / 5) && Number.isInteger(answer2C / 5)){
			answer1C = answer1C / 5;
			answer2C = answer2C / 5;
			answer1.textContent = answer1C;
			answer2.textContent = answer2C;
		}else if(Number.isInteger(answer1C / 3) && Number.isInteger(answer2C / 3)){
			answer1C = answer1C / 3;
			answer2C = answer2C / 3;
			answer1.textContent = answer1C;
			answer2.textContent = answer2C;
		}else{
			answer1.textContent = answer1C;
			answer2.textContent = answer2C;
		}
	}
}

function onSubmit(){
	aV = parseInt(a.value);
	bV = parseInt(b.value);
	check(aV, bV);
}
<|endoftext|>


<commit_before>// When

this.When(/^я введу в текстовое поле "([^"]*)" значение "([^"]*)"$/, function (fieldName, value, next) {
    var haveField = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };
    var success = function () {
        try {
            window.testHelpers.getControlByName(fieldName).setValue(value);
            next();
        } catch (err) {
            next(err);
        }
    };
    var fail = function () {
        next(new Error(fieldName + ' not found!'));
    };
    window.testHelpers.waitCondition(haveField, success, fail);
});

this.When(/^я введу в числовое поле "([^"]*)" значение "([^"]*)"$/, function (fieldName, value, next) {
    var haveField = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };
    var success = function () {
        try {
            var numValue = parseInt(value);
            chai.assert.isNumber(numValue);

            window.testHelpers.getControlByName(fieldName).setValue(numValue);

            next();
        } catch (err) {
            next(err);
        }
    };
    var fail = function () {
        next(new Error(fieldName + ' not found!'));
    };
    window.testHelpers.waitCondition(haveField, success, fail);
});

this.When(/^я введу в поле типа дата "([^"]*)" значение "([^"]*)"$/, function (fieldName, dateString, next) {
    var haveField = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };
    var success = function () {
        try {
            var date = dateString.match(/[а-я]*/i)[0];
            var iterator = dateString.match(/\w+/g) != null ? parseInt(dateString.match(/\w+/g)[0]) : 0;

            if (date === "Сегодня" && !isNaN(iterator)) {
                var value = window.testHelpers.getDate(iterator);
                window.testHelpers.getControlByName(fieldName).setValue(value);
            } else {
                date = window.testHelpers.getFormattedDate(dateString);
                window.testHelpers.getControlByName(fieldName).setValue(date);
            }

            next();
        } catch (err) {
            next(err);
        }
    };
    var fail = function () {
        next(new Error(fieldName + ' not found!'));
    };
    window.testHelpers.waitCondition(haveField, success, fail);
});

// Then

this.Then(/^значение в поле "([^"]*)" равно "([^"]*)"$/, function (fieldName, value, next) {
    var haveValue = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };

    var checkValue = function () {
        try {
            var field = window.testHelpers.getControlByName(fieldName);
            chai.assert.isDefined(field);

            var actValue = field.getDisplayValue();
            chai.assert.isTrue((actValue == value), actValue + ' != ' + value);

            next();
        } catch (err) {
            next(err);
        }
    };

    var fail = function () {
        next(new Error(fieldName + ' is undefined'));
    };

    window.testHelpers.waitCondition(haveValue, checkValue, fail);
});

this.Then(/^значение в поле типа дата "([^"]*)" равно "([^"]*)"$/, function (fieldName, dateString, next) {
    var haveValue = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };

    var checkValue = function () {
        try {
            var field = window.testHelpers.getControlByName(fieldName);
            chai.assert.isDefined(field);

            var actValue = field.getValue();

            if (field.getDisplayValue) {
                actValue = field.getDisplayValue();
            }

            if (typeof actValue == "string") {
                actValue = window.testHelpers.convertDate(actValue);
                actValue = new Date(actValue).getTime();
            } else {
                var standartDate = window.testHelpers.getStandartDate(actValue);
                var formattedDate = window.testHelpers.getFormattedDate(standartDate);
                actValue = new Date(formattedDate).getTime();
            }

            var date = dateString.match(/[а-я]*/i)[0];
            var iterator = dateString.match(/\w+/g) != null ? parseInt(dateString.match(/\w+/g)[0]) : 0;

            if (date === "Сегодня" && !isNaN(iterator)) {
                var value = window.testHelpers.getDate(iterator);
                chai.assert.equal(new Date(value).getTime(), actValue);
            } else {
                date = window.testHelpers.getFormattedDate(dateString);
                chai.assert.equal(new Date(date).getTime(), actValue);
            }

            next();
        } catch (err) {
            next(err);
        }
    };

    var fail = function () {
        next(new Error(fieldName + ' is undefined'));
    };

    window.testHelpers.waitCondition(haveValue, checkValue, fail);
});

this.Then(/^значение в выпадающем списке "([^"]*)" равно "([^"]*)"$/, function (fieldName, value, next) {
    var haveValue = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };

    var checkValue = function () {
        try {
            var field = window.testHelpers.getControlByName(fieldName);
            chai.assert.isDefined(field);

            var actValue = field.getValue().DisplayName;
            chai.assert.isTrue((actValue == value), actValue + ' != ' + value);

            next();
        } catch (err) {
            next(err);
        }
    };

    var fail = function () {
        next(new Error(fieldName + ' is undefined'));
    };

    window.testHelpers.waitCondition(haveValue, checkValue, fail);
});

this.Then(/^флаг "([^"]*)" будет иметь значение "([^"]*)"$/, function (flagName, value, next) {
    var haveFlag = function () {
        return window.testHelpers.getControlByName(flagName) != undefined;
    };
    var success = function () {
        var flag = window.testHelpers.getControlByName(flagName);

        try {
            chai.assert.isDefined(flag);
            chai.assert.equal(flag.getValue(), JSON.parse(value));
            next();
        } catch (err) {
            next(err);
        }
    };
    var fail = function () {
        next(new Error(flagName + ' not found!'));
    };
    window.testHelpers.waitCondition(haveFlag, success, fail);
});

this.When(/^я поменяю значение флага "([^"]*)" на "([^"]*)"$/, function (flagName, value, next) {
    var haveFlag = function () {
        return window.testHelpers.getControlByName(flagName) != undefined;
    };
    var success = function () {
        var flag = window.testHelpers.getControlByName(flagName);
        var parseValue = JSON.parse(value);

        if (typeof parseValue == "boolean") {
            flag.setValue(parseValue);
            next();
        } else {
            next(new Error("'" + value + "' is not correct value"));
        }
    };
    var fail = function () {
        next(new Error(flagName + ' not found!'));
    };

    window.testHelpers.waitCondition(haveFlag, success, fail);
});

this.Then(/^значение в текстовом поле "([^"]*)" равно "([^"]*)"$/, function (fieldName, value, next) {
    var haveField = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };

    var checkValue = function () {
        try {
            var field = window.testHelpers.getControlByName(fieldName);
            chai.assert.isDefined(field);

            var actValue = field.getValue();

            if (typeof actValue == "number") {
                value = parseInt(value);
            }

            chai.assert.isTrue((actValue === value), actValue + ' != ' + value);

            next();
        } catch (err) {
            next(err);
        }
    };

    var fail = function () {
        next(new Error(fieldName + ' is undefined'));
    };

    window.testHelpers.waitCondition(haveField, checkValue, fail);
});

this.Then(/^значение в числовом поле "([^"]*)" равно "([^"]*)"$/, function (fieldName, value, next) {
    var haveField = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };

    var checkValue = function () {
        try {
            var field = window.testHelpers.getControlByName(fieldName);
            chai.assert.isDefined(field);

            value = parseInt(value);

            var actValue = field.getDisplayValue();
            chai.assert.typeOf(actValue, 'number');
            chai.assert.isTrue((actValue === value), actValue + ' != ' + value);

            next();
        } catch (err) {
            next(err);
        }
    };

    var fail = function () {
        next(new Error(fieldName + ' is undefined'));
    };

    window.testHelpers.waitCondition(haveField, checkValue, fail);
});

this.Then(/^элемент "([^"]*)" будет недоступным$/, function (elementName, next) {
    var haveElement = function () {
        return window.testHelpers.getControlByName(elementName) != undefined;
    };
    var success = function () {
        try {
            if (!window.testHelpers.getControlByName(elementName).getEnabled()) {
                next();
            } else {
                next(new Error(elementName + ' is enabled!'));
            }
        } catch (err) {
            next(err);
        }
    };
    var fail = function () {
        next(new Error(elementName + ' not found!'));
    };

    window.testHelpers.waitCondition(haveElement, success, fail);
});<commit_msg>JK-714: Возможность чтения дробных чисел в текстовых полях #resolved<commit_after>// When

this.When(/^я введу в текстовое поле "([^"]*)" значение "([^"]*)"$/, function (fieldName, value, next) {
    var haveField = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };
    var success = function () {
        try {
            window.testHelpers.getControlByName(fieldName).setValue(value);
            next();
        } catch (err) {
            next(err);
        }
    };
    var fail = function () {
        next(new Error(fieldName + ' not found!'));
    };
    window.testHelpers.waitCondition(haveField, success, fail);
});

this.When(/^я введу в числовое поле "([^"]*)" значение "([^"]*)"$/, function (fieldName, value, next) {
    var haveField = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };
    var success = function () {
        try {
            var numValue = parseInt(value);
            chai.assert.isNumber(numValue);

            window.testHelpers.getControlByName(fieldName).setValue(numValue);

            next();
        } catch (err) {
            next(err);
        }
    };
    var fail = function () {
        next(new Error(fieldName + ' not found!'));
    };
    window.testHelpers.waitCondition(haveField, success, fail);
});

this.When(/^я введу в поле типа дата "([^"]*)" значение "([^"]*)"$/, function (fieldName, dateString, next) {
    var haveField = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };
    var success = function () {
        try {
            var date = dateString.match(/[а-я]*/i)[0];
            var iterator = dateString.match(/\w+/g) != null ? parseInt(dateString.match(/\w+/g)[0]) : 0;

            if (date === "Сегодня" && !isNaN(iterator)) {
                var value = window.testHelpers.getDate(iterator);
                window.testHelpers.getControlByName(fieldName).setValue(value);
            } else {
                date = window.testHelpers.getFormattedDate(dateString);
                window.testHelpers.getControlByName(fieldName).setValue(date);
            }

            next();
        } catch (err) {
            next(err);
        }
    };
    var fail = function () {
        next(new Error(fieldName + ' not found!'));
    };
    window.testHelpers.waitCondition(haveField, success, fail);
});

// Then

this.Then(/^значение в поле "([^"]*)" равно "([^"]*)"$/, function (fieldName, value, next) {
    var haveValue = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };

    var checkValue = function () {
        try {
            var field = window.testHelpers.getControlByName(fieldName);
            chai.assert.isDefined(field);

            var actValue = field.getDisplayValue();
            chai.assert.isTrue((actValue == value), actValue + ' != ' + value);

            next();
        } catch (err) {
            next(err);
        }
    };

    var fail = function () {
        next(new Error(fieldName + ' is undefined'));
    };

    window.testHelpers.waitCondition(haveValue, checkValue, fail);
});

this.Then(/^значение в поле типа дата "([^"]*)" равно "([^"]*)"$/, function (fieldName, dateString, next) {
    var haveValue = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };

    var checkValue = function () {
        try {
            var field = window.testHelpers.getControlByName(fieldName);
            chai.assert.isDefined(field);

            var actValue = field.getValue();

            if (field.getDisplayValue) {
                actValue = field.getDisplayValue();
            }

            if (typeof actValue == "string") {
                actValue = window.testHelpers.convertDate(actValue);
                actValue = new Date(actValue).getTime();
            } else {
                var standartDate = window.testHelpers.getStandartDate(actValue);
                var formattedDate = window.testHelpers.getFormattedDate(standartDate);
                actValue = new Date(formattedDate).getTime();
            }

            var date = dateString.match(/[а-я]*/i)[0];
            var iterator = dateString.match(/\w+/g) != null ? parseInt(dateString.match(/\w+/g)[0]) : 0;

            if (date === "Сегодня" && !isNaN(iterator)) {
                var value = window.testHelpers.getDate(iterator);
                chai.assert.equal(new Date(value).getTime(), actValue);
            } else {
                date = window.testHelpers.getFormattedDate(dateString);
                chai.assert.equal(new Date(date).getTime(), actValue);
            }

            next();
        } catch (err) {
            next(err);
        }
    };

    var fail = function () {
        next(new Error(fieldName + ' is undefined'));
    };

    window.testHelpers.waitCondition(haveValue, checkValue, fail);
});

this.Then(/^значение в выпадающем списке "([^"]*)" равно "([^"]*)"$/, function (fieldName, value, next) {
    var haveValue = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };

    var checkValue = function () {
        try {
            var field = window.testHelpers.getControlByName(fieldName);
            chai.assert.isDefined(field);

            var actValue = field.getValue().DisplayName;
            chai.assert.isTrue((actValue == value), actValue + ' != ' + value);

            next();
        } catch (err) {
            next(err);
        }
    };

    var fail = function () {
        next(new Error(fieldName + ' is undefined'));
    };

    window.testHelpers.waitCondition(haveValue, checkValue, fail);
});

this.Then(/^флаг "([^"]*)" будет иметь значение "([^"]*)"$/, function (flagName, value, next) {
    var haveFlag = function () {
        return window.testHelpers.getControlByName(flagName) != undefined;
    };
    var success = function () {
        var flag = window.testHelpers.getControlByName(flagName);

        try {
            chai.assert.isDefined(flag);
            chai.assert.equal(flag.getValue(), JSON.parse(value));
            next();
        } catch (err) {
            next(err);
        }
    };
    var fail = function () {
        next(new Error(flagName + ' not found!'));
    };
    window.testHelpers.waitCondition(haveFlag, success, fail);
});

this.When(/^я поменяю значение флага "([^"]*)" на "([^"]*)"$/, function (flagName, value, next) {
    var haveFlag = function () {
        return window.testHelpers.getControlByName(flagName) != undefined;
    };
    var success = function () {
        var flag = window.testHelpers.getControlByName(flagName);
        var parseValue = JSON.parse(value);

        if (typeof parseValue == "boolean") {
            flag.setValue(parseValue);
            next();
        } else {
            next(new Error("'" + value + "' is not correct value"));
        }
    };
    var fail = function () {
        next(new Error(flagName + ' not found!'));
    };

    window.testHelpers.waitCondition(haveFlag, success, fail);
});

this.Then(/^значение в текстовом поле "([^"]*)" равно "([^"]*)"$/, function (fieldName, value, next) {
    var haveField = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };

    var checkValue = function () {
        try {
            var field = window.testHelpers.getControlByName(fieldName);
            chai.assert.isDefined(field);

            var actValue = field.getValue();

            if (typeof actValue == "number") {
                value = parseFloat(value.replace(/,/g, '.'));
            }

            chai.assert.isTrue((actValue === value), actValue + ' != ' + value);

            next();
        } catch (err) {
            next(err);
        }
    };

    var fail = function () {
        next(new Error(fieldName + ' is undefined'));
    };

    window.testHelpers.waitCondition(haveField, checkValue, fail);
});

this.Then(/^значение в числовом поле "([^"]*)" равно "([^"]*)"$/, function (fieldName, value, next) {
    var haveField = function () {
        return window.testHelpers.getControlByName(fieldName) != undefined;
    };

    var checkValue = function () {
        try {
            var field = window.testHelpers.getControlByName(fieldName);
            chai.assert.isDefined(field);

            value = parseFloat(value.replace(/,/g, '.'));

            var actValue = field.getValue();

            if(field.getDisplayValue) {
                actValue = field.getDisplayValue();
            }

            chai.assert.typeOf(actValue, 'number');
            chai.assert.isTrue((actValue === value), actValue + ' != ' + value);

            next();
        } catch (err) {
            next(err);
        }
    };

    var fail = function () {
        next(new Error(fieldName + ' is undefined'));
    };

    window.testHelpers.waitCondition(haveField, checkValue, fail);
});

this.Then(/^элемент "([^"]*)" будет недоступным$/, function (elementName, next) {
    var haveElement = function () {
        return window.testHelpers.getControlByName(elementName) != undefined;
    };
    var success = function () {
        try {
            if (!window.testHelpers.getControlByName(elementName).getEnabled()) {
                next();
            } else {
                next(new Error(elementName + ' is enabled!'));
            }
        } catch (err) {
            next(err);
        }
    };
    var fail = function () {
        next(new Error(elementName + ' not found!'));
    };

    window.testHelpers.waitCondition(haveElement, success, fail);
});<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'

import TitleMessage from '../../components/TitleMessage'
import {StyledModalContent, basePadding} from './StyledModalContent'

export const StyledCloseButton = styled.button`
  // copy: react-redux-toastr (index.scss)
  background-color: transparent;
  border: none;
  cursor: pointer;
  font-size: 22px;
  height: auto;  // reset
  opacity: .8;  // reset
  outline: none;
  position: sticky;
  top: ${basePadding};  // reset
  padding-right: 0;
  width: auto;  // reset
  z-index: 2;
  display: flex;
  align-self: flex-end;

  &:hover {
    opacity: 1;
  }

  &:focus {
    outline: none;
  }
`

export class ModalContent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {isClosing: false}
    this.handleCloseClick = this.handleCloseClick.bind(this)
  }

  handleCloseClick() {
    this.setState({isClosing: true})
    setTimeout(() => {
      this.props.close(this.props.id)
    }, 300) // Toastr fade out takes 300ms
  }

  render() {
    const {
      closable,
      message,
      title
    } = this.props

    const {isClosing} = this.state

    return (

        }
          <TitleMessage title={title} message={message} closable={closable}>
            <this.props.component close={this.handleCloseClick}/>
          </TitleMessage>
        </StyledModalContent>
        <div className={`shadow animated ${isClosing ? 'fadeOut' : 'fadeIn'}`} />

    )
  }
}

ModalContent.propTypes = {
  id: PropTypes.any.isRequired,
  component: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
  close: PropTypes.func.isRequired,
  title: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
  message: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
  closable: PropTypes.bool
}

export default ModalContent
<commit_msg>refactor(app-extensions): refactor ModalContent.js<commit_after>import React, {useState} from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'

import TitleMessage from '../../components/TitleMessage'
import {StyledModalContent, basePadding} from './StyledModalContent'

export const StyledCloseButton = styled.button`
  // copy: react-redux-toastr (index.scss)
  background-color: transparent;
  border: none;
  cursor: pointer;
  font-size: 22px;
  height: auto;  // reset
  opacity: .8;  // reset
  outline: none;
  position: sticky;
  top: ${basePadding};  // reset
  padding-right: 0;
  width: auto;  // reset
  z-index: 2;
  display: flex;
  align-self: flex-end;

  &:hover {
    opacity: 1;
  }

  &:focus {
    outline: none;
  }
`

const ModalContent = props => {
  const {
    closable,
    message,
    title,
    close,
    id
  } = props

  const [isClosing, setIsClosing] = useState(false)

  const handleCloseClick = () => {
    setIsClosing(true)
    setTimeout(() => {
      close(id)
    }, 300) // Toastr fade out takes 300ms
  }

  return

    }
      <TitleMessage title={title} message={message} closable={closable}>
        <props.component close={handleCloseClick}/>
      </TitleMessage>
    </StyledModalContent>
    <div className={`shadow animated ${isClosing ? 'fadeOut' : 'fadeIn'}`} />

}

ModalContent.propTypes = {
  id: PropTypes.any.isRequired,
  component: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
  close: PropTypes.func.isRequired,
  title: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
  message: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
  closable: PropTypes.bool
}

export default ModalContent
<|endoftext|>


<commit_before>/**
 * This is a global callback pattern, called by all asynchronous functions of the Kuzzle object.
 *
 * @callback responseCallback
 * @param {Object} err - Error object, NULL if the query is successful
 * @param {Object} data - The content of the query response
 */

/**
 * Kuzzle handles documents either as realtime messages or as stored documents.
 * KuzzleDocument is the object representation of one of these documents.
 *
 * Notes:
 *   - this constructor may be called either with a documentId, a content, neither or both.
 *   - providing a documentID to the constructor will automatically call refresh, unless a content is also provided
 *
 *
 * @param {object} kuzzleDataCollection - an instanciated KuzzleDataCollection object
 * @param {string} [documentId] - ID of an existing document
 * @param {object} [content] - Initializes this document with the provided content
 * @constructor
 */
function KuzzleDocument(kuzzleDataCollection, documentId, content) {
  Object.defineProperties(this, {
    // private properties
    queue: {
      value: [],
      writable: true
    },
    refreshing: {
      value: false,
      writable: true
    },

    // read-only properties
    collection: {
      value: kuzzleDataCollection.collection,
      enumerable: true
    },
    createdTimestamp: {
      value: 'not yet implemented',
      enumerable: true
    },
    dataCollection: {
      value: kuzzleDataCollection,
      enumerable: true
    },
    kuzzle: {
      value: kuzzleDataCollection.kuzzle,
      enumerable: true
    },
    modifiedTimestamp: {
      value: 'not yet implemented',
      enumerable: true
    },
    // writable properties
    content: {
      value: {},
      writable: true,
      enumerable: true
    },
    headers: {
      value: JSON.parse(JSON.stringify(kuzzleDataCollection.headers)),
      enumerable: true,
      writable: true
    },
    version: {
      value: undefined,
      enumerable: true,
      writable: true
    }
  });

  // handling provided arguments
  if (!content && documentId && typeof documentId === 'object') {
    content = documentId;
    documentId = null;
  }

  if (content) {
    this.setContent(content, true);
  }

  if (documentId) {
    Object.defineProperty(this, 'id', {
      value: documentId,
      enumerable: true
    });

    if (!content) {
      this.refresh();
    }
  }

  // promisifying
  if (this.kuzzle.bluebird) {
    return this.kuzzle.bluebird.promisifyAll(this, {suffix: 'Promise'});
  }

  return this;
}

/**
 * Serialize this object into a JSON object
 *
 * @return {object} JSON object representing this document
 */
KuzzleDocument.prototype.toJSON = function () {
  var
    data = {};

  if (this.id) {
    data._id = this.id;
  }

  data.body = this.content;
  data._version = this.version;
  data = this.kuzzle.addHeaders(data, this.headers);

  return data;
};

/**
 * Overrides the toString() method in order to return a serialized version of the document
 *
 * @return {string} serialized version of this object
 */
KuzzleDocument.prototype.toString = function () {
  return JSON.stringify(this.toJSON());
};

/**
 * Deletes this document in Kuzzle.
 *
 * Takes an optional argument object with the following properties:
 *    - metadata (object, default: null):
 *        Additional information passed to notifications to other users
 *
 * @param {object} [options] - Optional parameters
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {*} this
 */
KuzzleDocument.prototype.delete = function (options, cb) {
  var self = this;

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  if (this.refreshing) {
    this.queue.push({action: 'delete', args: [options, cb]});
    return this;
  }

  if (!this.id) {
    throw new Error('KuzzleDocument.delete: cannot delete a document that has not been saved into Kuzzle');
  }

  if (cb) {
    this.kuzzle.query(this.collection, 'write', 'delete', this.toJSON(), options, function (err) {
      if (err) {
        return cb(err);
      }

      cb(null, self);
    });
  } else {
    this.kuzzle.query(this.collection, 'write', 'delete', this.toJSON(), options);
  }

  return this;
};

/**
 * Replaces the current content with the last version of this document stored in Kuzzle.
 *
 * @param {object} [options] - Optional parameters
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {*} this
 */
KuzzleDocument.prototype.refresh = function (options, cb) {
  var self = this;

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  if (this.refreshing) {
    this.queue.push({action: 'refresh', args: [options, cb]});
    return this;
  }

  if (!self.id) {
    throw new Error('KuzzleDocument.refresh: cannot retrieve a document if no ID has been provided');
  }

  self.refreshing = true;

  self.kuzzle.query(self.collection, 'read', 'get', {_id: self.id}, options, function (error, result) {
    if (error) {
      self.refreshing = false;
      self.queue = [];
      return cb ? cb(error) : false;
    }

    self.version = result._version;
    self.content = result._source;

    if (cb) {
      cb(null, self);
    }

    self.refreshing = false;
    dequeue.call(self);
  });

  return this;
};

/**
 * Saves this document into Kuzzle.
 *
 * If this is a new document, this function will create it in Kuzzle and the id property will be made available.
 * Otherwise, this method will replace the latest version of this document in Kuzzle by the current content
 * of this object.
 *
 * Takes an optional argument object with the following properties:
 *    - metadata (object, default: null):
 *        Additional information passed to notifications to other users
 *
 * @param {object} [options] - Optional parameters
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {*} this
 */
KuzzleDocument.prototype.save = function (options, cb) {
  var
    data = this.toJSON(),
    self = this;

  if (options && cb === undefined && typeof options === 'function') {
    cb = options;
    options = null;
  }

  var queryCB = function (error, result) {
    if (error) {
      return cb ? cb(error) : false;
    }

    if (!self.id) {
      Object.defineProperty(self, 'id', {
        value: result._id,
        enumerable: true
      });
    }

    if (cb) {
      cb(null, self);
    }
  };

  if (self.refreshing) {
    self.queue.push({action: 'save', args: [options, cb]});
    return self;
  }

  data.persist = true;

  self.kuzzle.query(this.collection, 'write', 'createOrUpdate', data, options, queryCB);

  return self;
};

/**
 * Sends the content of this document as a realtime message.
 *
 * Takes an optional argument object with the following properties:
 *    - metadata (object, default: null):
 *        Additional information passed to notifications to other users
 *
 * @param {object} [options] - Optional parameters
 * @returns {*} this
 */
KuzzleDocument.prototype.publish = function (options) {
  var data = this.toJSON();

  if (this.refreshing) {
    this.queue.push({action: 'publish', args: [options]});
    return this;
  }

  data.persist = false;

  this.kuzzle.query(this.collection, 'write', 'create', data, options);

  return this;
};

/**
 * Replaces the current content with new data.
 * Changes made by this function won’t be applied until the save method is called.
 *
 * @param {object} data - New content
 * @param {boolean} replace - if true: replace this document content with the provided data
 */
KuzzleDocument.prototype.setContent = function (data, replace) {
  var self = this;

  if (this.refreshing) {
    this.queue.push({action: 'setContent', args: [data, replace]});
    return this;
  }

  if (replace) {
    this.content = data;
  }
  else {
    Object.keys(data).forEach(function (key) {
      self.content[key] = data[key];
    });
  }

  return this;
};

/**
 * Listens to events concerning this document. Has no effect if the document does not have an ID
 * (i.e. if the document has not yet been created as a persisted document).
 *
 * @param {object} [options] - subscription options
 * @param {responseCallback} cb - callback that will be called each time a change has been detected on this document
 */
KuzzleDocument.prototype.subscribe = function (options, cb) {
  var filters;

  if (options && !cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  this.kuzzle.callbackRequired('KuzzleDocument.subscribe', cb);

  if (this.refreshing) {
    this.queue.push({action: 'subscribe', args: [cb]});
    return this;
  }

  if (!this.id) {
    throw new Error('KuzzleDocument.subscribe: cannot subscribe to a document if no ID has been provided');
  }

  filters = { ids: { values: [this.id] } };

  return this.dataCollection.subscribe(filters, cb, options);
};

/**
 * Helper function allowing to set headers while chaining calls.
 *
 * If the replace argument is set to true, replace the current headers with the provided content.
 * Otherwise, it appends the content to the current headers, only replacing already existing values
 *
 * @param content - new headers content
 * @param [replace] - default: false = append the content. If true: replace the current headers with tj
 */
KuzzleDocument.prototype.setHeaders = function (content, replace) {
  this.kuzzle.setHeaders.call(this, content, replace);
  return this;
};

/**
 * internal function used to dequeue calls which were put on hold while refreshing the content of this document
 */
function dequeue() {
  var element;

  while (this.queue.length > 0) {
    element = this.queue.shift();
    this[element.action].apply(this, element.args);
  }
}

module.exports = KuzzleDocument;
<commit_msg>fixed eslint error<commit_after>/**
 * This is a global callback pattern, called by all asynchronous functions of the Kuzzle object.
 *
 * @callback responseCallback
 * @param {Object} err - Error object, NULL if the query is successful
 * @param {Object} data - The content of the query response
 */

/**
 * Kuzzle handles documents either as realtime messages or as stored documents.
 * KuzzleDocument is the object representation of one of these documents.
 *
 * Notes:
 *   - this constructor may be called either with a documentId, a content, neither or both.
 *   - providing a documentID to the constructor will automatically call refresh, unless a content is also provided
 *
 *
 * @param {object} kuzzleDataCollection - an instanciated KuzzleDataCollection object
 * @param {string} [documentId] - ID of an existing document
 * @param {object} [content] - Initializes this document with the provided content
 * @constructor
 */
function KuzzleDocument(kuzzleDataCollection, documentId, content) {
  Object.defineProperties(this, {
    // private properties
    queue: {
      value: [],
      writable: true
    },
    refreshing: {
      value: false,
      writable: true
    },

    // read-only properties
    collection: {
      value: kuzzleDataCollection.collection,
      enumerable: true
    },
    createdTimestamp: {
      value: 'not yet implemented',
      enumerable: true
    },
    dataCollection: {
      value: kuzzleDataCollection,
      enumerable: true
    },
    kuzzle: {
      value: kuzzleDataCollection.kuzzle,
      enumerable: true
    },
    modifiedTimestamp: {
      value: 'not yet implemented',
      enumerable: true
    },
    // writable properties
    content: {
      value: {},
      writable: true,
      enumerable: true
    },
    headers: {
      value: JSON.parse(JSON.stringify(kuzzleDataCollection.headers)),
      enumerable: true,
      writable: true
    },
    version: {
      value: undefined,
      enumerable: true,
      writable: true
    }
  });

  // handling provided arguments
  if (!content && documentId && typeof documentId === 'object') {
    content = documentId;
    documentId = null;
  }

  if (content) {
    this.setContent(content, true);
  }

  if (documentId) {
    Object.defineProperty(this, 'id', {
      value: documentId,
      enumerable: true
    });

    if (!content) {
      this.refresh();
    }
  }

  // promisifying
  if (this.kuzzle.bluebird) {
    return this.kuzzle.bluebird.promisifyAll(this, {suffix: 'Promise'});
  }

  return this;
}

/**
 * Serialize this object into a JSON object
 *
 * @return {object} JSON object representing this document
 */
KuzzleDocument.prototype.toJSON = function () {
  var
    data = {};

  if (this.id) {
    data._id = this.id;
  }

  data.body = this.content;
  data._version = this.version;
  data = this.kuzzle.addHeaders(data, this.headers);

  return data;
};

/**
 * Overrides the toString() method in order to return a serialized version of the document
 *
 * @return {string} serialized version of this object
 */
KuzzleDocument.prototype.toString = function () {
  return JSON.stringify(this.toJSON());
};

/**
 * Deletes this document in Kuzzle.
 *
 * Takes an optional argument object with the following properties:
 *    - metadata (object, default: null):
 *        Additional information passed to notifications to other users
 *
 * @param {object} [options] - Optional parameters
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {*} this
 */
KuzzleDocument.prototype.delete = function (options, cb) {
  var self = this;

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  if (this.refreshing) {
    this.queue.push({action: 'delete', args: [options, cb]});
    return this;
  }

  if (!this.id) {
    throw new Error('KuzzleDocument.delete: cannot delete a document that has not been saved into Kuzzle');
  }

  if (cb) {
    this.kuzzle.query(this.collection, 'write', 'delete', this.toJSON(), options, function (err) {
      if (err) {
        return cb(err);
      }

      cb(null, self);
    });
  } else {
    this.kuzzle.query(this.collection, 'write', 'delete', this.toJSON(), options);
  }

  return this;
};

/**
 * Replaces the current content with the last version of this document stored in Kuzzle.
 *
 * @param {object} [options] - Optional parameters
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {*} this
 */
KuzzleDocument.prototype.refresh = function (options, cb) {
  var self = this;

  if (!cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  if (this.refreshing) {
    this.queue.push({action: 'refresh', args: [options, cb]});
    return this;
  }

  if (!self.id) {
    throw new Error('KuzzleDocument.refresh: cannot retrieve a document if no ID has been provided');
  }

  self.refreshing = true;

  self.kuzzle.query(self.collection, 'read', 'get', {_id: self.id}, options, function (error, result) {
    if (error) {
      self.refreshing = false;
      self.queue = [];
      return cb ? cb(error) : false;
    }

    self.version = result._version;
    self.content = result._source;

    if (cb) {
      cb(null, self);
    }

    self.refreshing = false;
    dequeue.call(self);
  });

  return this;
};

/**
 * Saves this document into Kuzzle.
 *
 * If this is a new document, this function will create it in Kuzzle and the id property will be made available.
 * Otherwise, this method will replace the latest version of this document in Kuzzle by the current content
 * of this object.
 *
 * Takes an optional argument object with the following properties:
 *    - metadata (object, default: null):
 *        Additional information passed to notifications to other users
 *
 * @param {object} [options] - Optional parameters
 * @param {responseCallback} [cb] - Handles the query response
 * @returns {*} this
 */
KuzzleDocument.prototype.save = function (options, cb) {
  var
    data = this.toJSON(),
    self = this,
    queryCB;

  if (options && cb === undefined && typeof options === 'function') {
    cb = options;
    options = null;
  }

  queryCB = function (error, result) {
    if (error) {
      return cb ? cb(error) : false;
    }

    if (!self.id) {
      Object.defineProperty(self, 'id', {
        value: result._id,
        enumerable: true
      });
    }

    if (cb) {
      cb(null, self);
    }
  };

  if (self.refreshing) {
    self.queue.push({action: 'save', args: [options, cb]});
    return self;
  }

  data.persist = true;

  self.kuzzle.query(this.collection, 'write', 'createOrUpdate', data, options, queryCB);

  return self;
};

/**
 * Sends the content of this document as a realtime message.
 *
 * Takes an optional argument object with the following properties:
 *    - metadata (object, default: null):
 *        Additional information passed to notifications to other users
 *
 * @param {object} [options] - Optional parameters
 * @returns {*} this
 */
KuzzleDocument.prototype.publish = function (options) {
  var data = this.toJSON();

  if (this.refreshing) {
    this.queue.push({action: 'publish', args: [options]});
    return this;
  }

  data.persist = false;

  this.kuzzle.query(this.collection, 'write', 'create', data, options);

  return this;
};

/**
 * Replaces the current content with new data.
 * Changes made by this function won’t be applied until the save method is called.
 *
 * @param {object} data - New content
 * @param {boolean} replace - if true: replace this document content with the provided data
 */
KuzzleDocument.prototype.setContent = function (data, replace) {
  var self = this;

  if (this.refreshing) {
    this.queue.push({action: 'setContent', args: [data, replace]});
    return this;
  }

  if (replace) {
    this.content = data;
  }
  else {
    Object.keys(data).forEach(function (key) {
      self.content[key] = data[key];
    });
  }

  return this;
};

/**
 * Listens to events concerning this document. Has no effect if the document does not have an ID
 * (i.e. if the document has not yet been created as a persisted document).
 *
 * @param {object} [options] - subscription options
 * @param {responseCallback} cb - callback that will be called each time a change has been detected on this document
 */
KuzzleDocument.prototype.subscribe = function (options, cb) {
  var filters;

  if (options && !cb && typeof options === 'function') {
    cb = options;
    options = null;
  }

  this.kuzzle.callbackRequired('KuzzleDocument.subscribe', cb);

  if (this.refreshing) {
    this.queue.push({action: 'subscribe', args: [cb]});
    return this;
  }

  if (!this.id) {
    throw new Error('KuzzleDocument.subscribe: cannot subscribe to a document if no ID has been provided');
  }

  filters = { ids: { values: [this.id] } };

  return this.dataCollection.subscribe(filters, cb, options);
};

/**
 * Helper function allowing to set headers while chaining calls.
 *
 * If the replace argument is set to true, replace the current headers with the provided content.
 * Otherwise, it appends the content to the current headers, only replacing already existing values
 *
 * @param content - new headers content
 * @param [replace] - default: false = append the content. If true: replace the current headers with tj
 */
KuzzleDocument.prototype.setHeaders = function (content, replace) {
  this.kuzzle.setHeaders.call(this, content, replace);
  return this;
};

/**
 * internal function used to dequeue calls which were put on hold while refreshing the content of this document
 */
function dequeue() {
  var element;

  while (this.queue.length > 0) {
    element = this.queue.shift();
    this[element.action].apply(this, element.args);
  }
}

module.exports = KuzzleDocument;
<|endoftext|>