<commit_before>const parsers = require('../../utils/parsers');
const { AirParsingError } = require('./AirErrors');

function travelOrderReducer(acc, { TravelOrder }) {
  const x = parseInt(TravelOrder, 10);
  if (x > acc) {
    return x;
  }
  return acc;
}

function getBaggage(baggageAllowance) {
  // Checking for allowance
  if (
    !baggageAllowance
    || (
      !baggageAllowance['air:NumberOfPieces']
      && !baggageAllowance['air:MaxWeight']
    )
  ) {
    console.warn('Baggage information is not number and is not weight!', JSON.stringify(baggageAllowance));
    return { units: 'piece', amount: 0 };
  }
  // Checking for max weight
  if (baggageAllowance['air:MaxWeight']) {
    return {
      units: baggageAllowance['air:MaxWeight'].Unit.toLowerCase(),
      amount: Number(baggageAllowance['air:MaxWeight'].Value),
    };
  }
  // Returning pieces
  return {
    units: 'piece',
    amount: Number(baggageAllowance['air:NumberOfPieces']),
  };
}

function formatSegment(segment) {
  return {
    from: segment.Origin,
    to: segment.Destination,
    group: Number(segment.Group),
    departure: segment.DepartureTime,
    arrival: segment.ArrivalTime,
    airline: segment.Carrier,
    flightNumber: segment.FlightNumber,
    uapi_segment_ref: segment.Key,
  };
}

function formatServiceSegment(segment, remark) {
  return {
    ...parsers.serviceSegment(remark['passive:Text']),
    carrier: segment.SupplierCode,
    airport: segment.Origin,
    date: segment.StartDate,
    index: segment.index,
  };
}

function formatPrices(prices) {
  return {
    basePrice: prices.BasePrice,
    taxes: prices.Taxes,
    equivalentBasePrice: prices.EquivalentBasePrice,
    totalPrice: prices.TotalPrice,
  };
}

function formatTrip(segment, flightDetails) {
  const flightInfo = flightDetails
    ? Object.keys(flightDetails).map(
      detailsKey => flightDetails[detailsKey]
    )
    : [];
  const plane = flightInfo.map(details => details.Equipment || 'Unknown');
  const duration = flightInfo.map(details => details.FlightTime || 0);
  const techStops = flightInfo.slice(1).map(details => details.Origin);
  return {
    ...formatSegment(segment),
    serviceClass: segment.CabinClass,
    plane,
    duration,
    techStops,
  };
}

function formatAirExchangeBundle(bundle) {
  return {
    addCollection: bundle.AddCollection,
    changeFee: bundle.ChangeFee,
    exchangeAmount: bundle.ExchangeAmount,
    refund: bundle.Refund,
  };
}

function formatLowFaresSearch(searchRequest, searchResult) {
  const pricesList = searchResult['air:AirPricePointList'];
  const solutionsList = searchResult['air:AirPricingSolution'];
  const fareInfos = searchResult['air:FareInfoList'];
  const segments = searchResult['air:AirSegmentList'];
  const flightDetails = searchResult['air:FlightDetailsList'];
  const { provider } = searchRequest;

  // const legs = _.first(_.toArray(searchResult['air:RouteList']))['air:Leg'];

  // TODO filter pricesList by CompleteItinerary=true & ETicketability = Yes, etc

  const fares = [];

  const results = typeof solutionsList !== 'undefined' ? solutionsList : pricesList;

  Object.entries(results).forEach(([fareKey, price]) => {
    const [firstKey] = Object.keys(price['air:AirPricingInfo']);
    const thisFare = price['air:AirPricingInfo'][firstKey]; // get trips from first reservation
    if (!thisFare.PlatingCarrier) {
      return;
    }

    let directions = [];
    if (typeof solutionsList !== 'undefined') {
      directions = price['air:Journey'].map((leg) => {
        const trips = leg['air:AirSegmentRef'].map((segmentRef) => {
          const segment = segments[segmentRef];

          const tripFlightDetails = segment['air:FlightDetailsRef'].map(flightDetailsRef => flightDetails[flightDetailsRef]);

          const [bookingInfo] = thisFare['air:BookingInfo'].filter(info => info.SegmentRef === segmentRef);
          const fareInfo = fareInfos[bookingInfo.FareInfoRef];

          const seatsAvailable = Number(bookingInfo.BookingCount);

          return Object.assign(
            formatTrip(segment, tripFlightDetails),
            {
              serviceClass: bookingInfo.CabinClass,
              bookingClass: bookingInfo.BookingCode,
              baggage: [getBaggage(fareInfo['air:BaggageAllowance'])],
              fareBasisCode: fareInfo.FareBasis,
            },
            seatsAvailable ? { seatsAvailable } : null
          );
        });

        return [{
          from: trips[0].from,
          to: trips[trips.length - 1].to,
          duration: leg.TravelTime,
          // TODO get overnight stops, etc from connection
          platingCarrier: thisFare.PlatingCarrier,
          segments: trips,
        }];
      });
    } else {
      directions = thisFare['air:FlightOptionsList'].map(direction => Object.values(direction['air:Option']).map((option) => {
        const trips = option['air:BookingInfo'].map(
          (segmentInfo) => {
            const fareInfo = fareInfos[segmentInfo.FareInfoRef];
            const segment = segments[segmentInfo.SegmentRef];
            const tripFlightDetails = segment['air:FlightDetailsRef'].map(
              flightDetailsRef => flightDetails[flightDetailsRef]
            );
            const seatsAvailable = (
              segment['air:AirAvailInfo']
              && segment['air:AirAvailInfo'].ProviderCode === provider)
              ? (Number(
                segment['air:AirAvailInfo']['air:BookingCodeInfo'].BookingCounts
                  .match(new RegExp(`${segmentInfo.BookingCode}(\\d+)`))[1]
              ))
              : null;
            return Object.assign(
              formatTrip(segment, tripFlightDetails),
              {
                serviceClass: segmentInfo.CabinClass,
                bookingClass: segmentInfo.BookingCode,
                baggage: [getBaggage(fareInfo['air:BaggageAllowance'])],
                fareBasisCode: fareInfo.FareBasis,
              },
              seatsAvailable ? { seatsAvailable } : null
            );
          });

        return {
          from: direction.Origin,
          to: direction.Destination,
          // duration
          // TODO get overnight stops, etc from connection
          platingCarrier: thisFare.PlatingCarrier,
          segments: trips,
        };
      }));
    }

    const passengerCounts = {};

    const passengerCategories = Object.keys(price['air:AirPricingInfo'])
      .reduce((acc, key) => {
        const passengerFare = price['air:AirPricingInfo'][key];
        let code = passengerFare['air:PassengerType'];

        if (Object.prototype.toString.call(code) === '[object String]') { // air:PassengerType in fullCollapseList_obj ParserUapi param
          passengerCounts[code] = 1;

          // air:PassengerType in noCollapseList
        } else if (Array.isArray(code) && code.constructor === Array) { // ParserUapi param
          const count = code.length;
          const list = Array.from(new Set((code.map((item) => {
            if (Object.prototype.toString.call(item) === '[object String]') {
              return item;
            } if (Object.prototype.toString.call(item) === '[object Object]' && item.Code) {
              // air:PassengerType in fullCollapseList_obj like above,
              // but there is Age or other info, except Code
              return item.Code;
            }
            throw new AirParsingError.PTCIsNotSet();
          }))));

          [code] = list;
          if (!list[0] || list.length !== 1) { // TODO throw error
            console.log('Warning: different categories '
              + list.join() + ' in single fare calculation ' + key + ' in fare ' + fareKey);
          }
          passengerCounts[code] = count;
        } else {
          throw new AirParsingError.PTCTypeInvalid();
        }

        return { ...acc, [code]: passengerFare };
      }, {});

    if (Object.keys(passengerCategories).length
      !== Object.keys(price['air:AirPricingInfo']).length
    ) {
      console.log('Warning: duplicate categories in passengerCategories map for fare ' + fareKey);
    }

    const passengerFares = Object.keys(passengerCategories).reduce(
      (memo, ptc) => Object.assign(memo, {
        [ptc]: {
          totalPrice: passengerCategories[ptc].TotalPrice,
          basePrice: passengerCategories[ptc].BasePrice,
          taxes: passengerCategories[ptc].Taxes,
        },
      }), {}
    );

    const result = {
      totalPrice: price.TotalPrice,
      basePrice: price.BasePrice,
      taxes: price.Taxes,
      directions,
      bookingComponents: [
        {
          totalPrice: price.TotalPrice,
          basePrice: price.BasePrice,
          taxes: price.Taxes,
          uapi_fare_reference: fareKey, // TODO
        },
      ],
      passengerFares,
      passengerCounts,
    };

    fares.push(result);
  });

  fares.sort((a, b) => parseFloat(a.totalPrice.substr(3)) - parseFloat(b.totalPrice.substr(3)));

  return fares;
}

/**
 * This function used to transform segments and service segments objects
 * to arrays. After that this function try to set indexes with same as in
 * terminal response order. So it needs to check `TravelOrder` field for that.
 *
 * @param segmentsObject
 * @param serviceSegmentsObject
 * @return {*}
 */
function setIndexesForSegments(
  segmentsObject = null,
  serviceSegmentsObject = null
) {
  const segments = segmentsObject
    ? Object.keys(segmentsObject).map(k => segmentsObject[k])
    : null;

  const serviceSegments = serviceSegmentsObject
    ? Object.keys(serviceSegmentsObject).map(k => serviceSegmentsObject[k])
    : null;

  if (segments === null && serviceSegments === null) {
    return { segments, serviceSegments };
  }

  if (segments !== null && serviceSegments === null) {
    const segmentsNew = segments.map((segment, key) => ({
      ...segment,
      index: key + 1,
    }));
    return { segments: segmentsNew, serviceSegments };
  }

  if (segments === null && serviceSegments !== null) {
    const serviceSegmentsNew = serviceSegments.map(
      (segment, key) => ({
        ...segment,
        index: key + 1,
      })
    );
    return { segments, serviceSegments: serviceSegmentsNew };
  }

  const maxSegmentsTravelOrder = segments.reduce(travelOrderReducer, 0);
  const maxServiceSegmentsTravelOrder = serviceSegments.reduce(travelOrderReducer, 0);

  const maxOrder = Math.max(
    maxSegmentsTravelOrder,
    maxServiceSegmentsTravelOrder
  );

  const allSegments = [];

  for (let i = 1; i <= maxOrder; i += 1) {
    segments.forEach(s => (Number(s.TravelOrder) === i ? allSegments.push(s) : null));
    serviceSegments.forEach(s => (Number(s.TravelOrder) === i ? allSegments.push(s) : null));
  }

  const indexedSegments = allSegments.map((s, k) => ({ ...s, index: k + 1 }));

  return {
    segments: indexedSegments.filter(s => s.SegmentType === undefined),
    serviceSegments: indexedSegments.filter(s => s.SegmentType === 'Service'),
  };
}

module.exports = {
  formatLowFaresSearch,
  formatTrip,
  formatSegment,
  formatServiceSegment,
  formatAirExchangeBundle,
  formatPrices,
  setIndexesForSegments,
  getBaggage,
};
<commit_msg>Fixed lint error<commit_after>const parsers = require('../../utils/parsers');
const { AirParsingError } = require('./AirErrors');

function travelOrderReducer(acc, { TravelOrder }) {
  const x = parseInt(TravelOrder, 10);
  if (x > acc) {
    return x;
  }
  return acc;
}

function getBaggage(baggageAllowance) {
  // Checking for allowance
  if (
    !baggageAllowance
    || (
      !baggageAllowance['air:NumberOfPieces']
      && !baggageAllowance['air:MaxWeight']
    )
  ) {
    console.warn('Baggage information is not number and is not weight!', JSON.stringify(baggageAllowance));
    return { units: 'piece', amount: 0 };
  }
  // Checking for max weight
  if (baggageAllowance['air:MaxWeight']) {
    return {
      units: baggageAllowance['air:MaxWeight'].Unit.toLowerCase(),
      amount: Number(baggageAllowance['air:MaxWeight'].Value),
    };
  }
  // Returning pieces
  return {
    units: 'piece',
    amount: Number(baggageAllowance['air:NumberOfPieces']),
  };
}

function formatSegment(segment) {
  return {
    from: segment.Origin,
    to: segment.Destination,
    group: Number(segment.Group),
    departure: segment.DepartureTime,
    arrival: segment.ArrivalTime,
    airline: segment.Carrier,
    flightNumber: segment.FlightNumber,
    uapi_segment_ref: segment.Key,
  };
}

function formatServiceSegment(segment, remark) {
  return {
    ...parsers.serviceSegment(remark['passive:Text']),
    carrier: segment.SupplierCode,
    airport: segment.Origin,
    date: segment.StartDate,
    index: segment.index,
  };
}

function formatPrices(prices) {
  return {
    basePrice: prices.BasePrice,
    taxes: prices.Taxes,
    equivalentBasePrice: prices.EquivalentBasePrice,
    totalPrice: prices.TotalPrice,
  };
}

function formatTrip(segment, flightDetails) {
  const flightInfo = flightDetails
    ? Object.keys(flightDetails).map(
      detailsKey => flightDetails[detailsKey]
    )
    : [];
  const plane = flightInfo.map(details => details.Equipment || 'Unknown');
  const duration = flightInfo.map(details => details.FlightTime || 0);
  const techStops = flightInfo.slice(1).map(details => details.Origin);
  return {
    ...formatSegment(segment),
    serviceClass: segment.CabinClass,
    plane,
    duration,
    techStops,
  };
}

function formatAirExchangeBundle(bundle) {
  return {
    addCollection: bundle.AddCollection,
    changeFee: bundle.ChangeFee,
    exchangeAmount: bundle.ExchangeAmount,
    refund: bundle.Refund,
  };
}

function formatLowFaresSearch(searchRequest, searchResult) {
  const pricesList = searchResult['air:AirPricePointList'];
  const solutionsList = searchResult['air:AirPricingSolution'];
  const fareInfos = searchResult['air:FareInfoList'];
  const segments = searchResult['air:AirSegmentList'];
  const flightDetails = searchResult['air:FlightDetailsList'];
  const { provider } = searchRequest;

  // const legs = _.first(_.toArray(searchResult['air:RouteList']))['air:Leg'];

  // TODO filter pricesList by CompleteItinerary=true & ETicketability = Yes, etc

  const fares = [];

  const results = typeof solutionsList !== 'undefined' ? solutionsList : pricesList;

  Object.entries(results).forEach(([fareKey, price]) => {
    const [firstKey] = Object.keys(price['air:AirPricingInfo']);
    const thisFare = price['air:AirPricingInfo'][firstKey]; // get trips from first reservation
    if (!thisFare.PlatingCarrier) {
      return;
    }

    let directions = [];
    if (typeof solutionsList !== 'undefined') {
      directions = price['air:Journey'].map((leg) => {
        const trips = leg['air:AirSegmentRef'].map((segmentRef) => {
          const segment = segments[segmentRef];

          const tripFlightDetails = segment['air:FlightDetailsRef'].map(flightDetailsRef => flightDetails[flightDetailsRef]);

          const [bookingInfo] = thisFare['air:BookingInfo'].filter(info => info.SegmentRef === segmentRef);
          const fareInfo = fareInfos[bookingInfo.FareInfoRef];

          const seatsAvailable = Number(bookingInfo.BookingCount);

          return Object.assign(
            formatTrip(segment, tripFlightDetails),
            {
              serviceClass: bookingInfo.CabinClass,
              bookingClass: bookingInfo.BookingCode,
              baggage: [getBaggage(fareInfo['air:BaggageAllowance'])],
              fareBasisCode: fareInfo.FareBasis,
            },
            seatsAvailable ? { seatsAvailable } : null
          );
        });

        return [{
          from: trips[0].from,
          to: trips[trips.length - 1].to,
          duration: leg.TravelTime,
          // TODO get overnight stops, etc from connection
          platingCarrier: thisFare.PlatingCarrier,
          segments: trips,
        }];
      });
    } else {
      directions = thisFare['air:FlightOptionsList'].map(direction => Object.values(direction['air:Option']).map((option) => {
        const trips = option['air:BookingInfo'].map(
          (segmentInfo) => {
            const fareInfo = fareInfos[segmentInfo.FareInfoRef];
            const segment = segments[segmentInfo.SegmentRef];
            const tripFlightDetails = segment['air:FlightDetailsRef'].map(
              flightDetailsRef => flightDetails[flightDetailsRef]
            );
            const seatsAvailable = (
              segment['air:AirAvailInfo']
              && segment['air:AirAvailInfo'].ProviderCode === provider)
              ? (Number(
                segment['air:AirAvailInfo']['air:BookingCodeInfo'].BookingCounts
                  .match(new RegExp(`${segmentInfo.BookingCode}(\\d+)`))[1]
              ))
              : null;
            return Object.assign(
              formatTrip(segment, tripFlightDetails),
              {
                serviceClass: segmentInfo.CabinClass,
                bookingClass: segmentInfo.BookingCode,
                baggage: [getBaggage(fareInfo['air:BaggageAllowance'])],
                fareBasisCode: fareInfo.FareBasis,
              },
              seatsAvailable ? { seatsAvailable } : null
            );
          }
        );

        return {
          from: direction.Origin,
          to: direction.Destination,
          // duration
          // TODO get overnight stops, etc from connection
          platingCarrier: thisFare.PlatingCarrier,
          segments: trips,
        };
      }));
    }

    const passengerCounts = {};

    const passengerCategories = Object.keys(price['air:AirPricingInfo'])
      .reduce((acc, key) => {
        const passengerFare = price['air:AirPricingInfo'][key];
        let code = passengerFare['air:PassengerType'];

        if (Object.prototype.toString.call(code) === '[object String]') { // air:PassengerType in fullCollapseList_obj ParserUapi param
          passengerCounts[code] = 1;

          // air:PassengerType in noCollapseList
        } else if (Array.isArray(code) && code.constructor === Array) { // ParserUapi param
          const count = code.length;
          const list = Array.from(new Set((code.map((item) => {
            if (Object.prototype.toString.call(item) === '[object String]') {
              return item;
            } if (Object.prototype.toString.call(item) === '[object Object]' && item.Code) {
              // air:PassengerType in fullCollapseList_obj like above,
              // but there is Age or other info, except Code
              return item.Code;
            }
            throw new AirParsingError.PTCIsNotSet();
          }))));

          [code] = list;
          if (!list[0] || list.length !== 1) { // TODO throw error
            console.log('Warning: different categories '
              + list.join() + ' in single fare calculation ' + key + ' in fare ' + fareKey);
          }
          passengerCounts[code] = count;
        } else {
          throw new AirParsingError.PTCTypeInvalid();
        }

        return { ...acc, [code]: passengerFare };
      }, {});

    if (Object.keys(passengerCategories).length
      !== Object.keys(price['air:AirPricingInfo']).length
    ) {
      console.log('Warning: duplicate categories in passengerCategories map for fare ' + fareKey);
    }

    const passengerFares = Object.keys(passengerCategories).reduce(
      (memo, ptc) => Object.assign(memo, {
        [ptc]: {
          totalPrice: passengerCategories[ptc].TotalPrice,
          basePrice: passengerCategories[ptc].BasePrice,
          taxes: passengerCategories[ptc].Taxes,
        },
      }), {}
    );

    const result = {
      totalPrice: price.TotalPrice,
      basePrice: price.BasePrice,
      taxes: price.Taxes,
      directions,
      bookingComponents: [
        {
          totalPrice: price.TotalPrice,
          basePrice: price.BasePrice,
          taxes: price.Taxes,
          uapi_fare_reference: fareKey, // TODO
        },
      ],
      passengerFares,
      passengerCounts,
    };

    fares.push(result);
  });

  fares.sort((a, b) => parseFloat(a.totalPrice.substr(3)) - parseFloat(b.totalPrice.substr(3)));

  return fares;
}

/**
 * This function used to transform segments and service segments objects
 * to arrays. After that this function try to set indexes with same as in
 * terminal response order. So it needs to check `TravelOrder` field for that.
 *
 * @param segmentsObject
 * @param serviceSegmentsObject
 * @return {*}
 */
function setIndexesForSegments(
  segmentsObject = null,
  serviceSegmentsObject = null
) {
  const segments = segmentsObject
    ? Object.keys(segmentsObject).map(k => segmentsObject[k])
    : null;

  const serviceSegments = serviceSegmentsObject
    ? Object.keys(serviceSegmentsObject).map(k => serviceSegmentsObject[k])
    : null;

  if (segments === null && serviceSegments === null) {
    return { segments, serviceSegments };
  }

  if (segments !== null && serviceSegments === null) {
    const segmentsNew = segments.map((segment, key) => ({
      ...segment,
      index: key + 1,
    }));
    return { segments: segmentsNew, serviceSegments };
  }

  if (segments === null && serviceSegments !== null) {
    const serviceSegmentsNew = serviceSegments.map(
      (segment, key) => ({
        ...segment,
        index: key + 1,
      })
    );
    return { segments, serviceSegments: serviceSegmentsNew };
  }

  const maxSegmentsTravelOrder = segments.reduce(travelOrderReducer, 0);
  const maxServiceSegmentsTravelOrder = serviceSegments.reduce(travelOrderReducer, 0);

  const maxOrder = Math.max(
    maxSegmentsTravelOrder,
    maxServiceSegmentsTravelOrder
  );

  const allSegments = [];

  for (let i = 1; i <= maxOrder; i += 1) {
    segments.forEach(s => (Number(s.TravelOrder) === i ? allSegments.push(s) : null));
    serviceSegments.forEach(s => (Number(s.TravelOrder) === i ? allSegments.push(s) : null));
  }

  const indexedSegments = allSegments.map((s, k) => ({ ...s, index: k + 1 }));

  return {
    segments: indexedSegments.filter(s => s.SegmentType === undefined),
    serviceSegments: indexedSegments.filter(s => s.SegmentType === 'Service'),
  };
}

module.exports = {
  formatLowFaresSearch,
  formatTrip,
  formatSegment,
  formatServiceSegment,
  formatAirExchangeBundle,
  formatPrices,
  setIndexesForSegments,
  getBaggage,
};
<|endoftext|>


<commit_before>// grunt-wuml
// https://github.com/prantlf/grunt-wuml
//
// Copyright (c) 2016 Ferdinand Prantl
// Licensed under the MIT license.
//
// Generates images from wuml diagram sources.

'use strict';

var path = require('path'),
    wuml = require('wuml'),
    Q = require('q');

module.exports = function (grunt) {

  function processDiagram(fileSrc, fileDest, options) {
    try {
      grunt.log.subhead('Processing diagram "' + fileSrc + '"');
      var input = grunt.file.read(fileSrc, {encoding: 'utf-8'});
      return Q.Promise(function (resolve, reject) {
        wuml.createDiagram(input, options, function(svg) {
          try {
            grunt.file.write(fileDest, svg, {encoding: 'utf-8'});
            resolve();
          } catch (error) {
            grunt.log.error(error);
            grunt.fail.warn('Writing diagram to "' + fileDest + '" failed\n');
            reject();
          }
        });
      });
    } catch (error) {
      grunt.log.error(error);
      grunt.fail.warn('Processing diagram "' + fileSrc + '" failed\n');
    }
  }

  grunt.registerMultiTask('wuml', "Generate images from yuml diagram sources by wuml", function () {
    var done = this.async(),
        options = this.options({
          waggly: false,
          type: 'class',     // class|sequence|usecase
          orientation: 'TD', // LR|TD
          splines: 'ortho',  // ortho|spline
          format: 'png',     // jpg|png|svg
          fontFamily: 'Purisa',
          fontSize: 10
        }),
        promises = this.files.map(function (file) {
          // If multiple source files are specified, the destination
          // path should point to a directory
          var single = file.orig.src.length === 1 &&
                file.orig.src.findIndex(function (src) {
                  return src.indexOf('*') >= 0 || src.indexOf('?') >= 0;
                }) < 0,
              promises = file.src.map(function (src) {
                // If the destination is a directory, use the source file name
                // with the target image format as extension
                var dest = single ? file.dest : path.join(file.dest,
                      path.parse(src).name + '.' + options.format),
                    dir = path.dirname(dest);
                grunt.file.mkdir(dir);
                return processDiagram(src, dest, options);
              });
          return Q.all(promises);
        });
    Q.all(promises)
     .then(done);
  });
};
<commit_msg>Fix the multiple source file support to work in node 0.12 too<commit_after>// grunt-wuml
// https://github.com/prantlf/grunt-wuml
//
// Copyright (c) 2016 Ferdinand Prantl
// Licensed under the MIT license.
//
// Generates images from wuml diagram sources.

'use strict';

var path = require('path'),
    wuml = require('wuml'),
    Q = require('q');

module.exports = function (grunt) {

  function processDiagram(fileSrc, fileDest, options) {
    try {
      grunt.log.subhead('Processing diagram "' + fileSrc + '"');
      var input = grunt.file.read(fileSrc, {encoding: 'utf-8'});
      return Q.Promise(function (resolve, reject) {
        wuml.createDiagram(input, options, function(svg) {
          try {
            grunt.file.write(fileDest, svg, {encoding: 'utf-8'});
            resolve();
          } catch (error) {
            grunt.log.error(error);
            grunt.fail.warn('Writing diagram to "' + fileDest + '" failed\n');
            reject();
          }
        });
      });
    } catch (error) {
      grunt.log.error(error);
      grunt.fail.warn('Processing diagram "' + fileSrc + '" failed\n');
    }
  }

  grunt.registerMultiTask('wuml', "Generate images from yuml diagram sources by wuml", function () {
    var done = this.async(),
        options = this.options({
          waggly: false,
          type: 'class',     // class|sequence|usecase
          orientation: 'TD', // LR|TD
          splines: 'ortho',  // ortho|spline
          format: 'png',     // jpg|png|svg
          fontFamily: 'Purisa',
          fontSize: 10
        }),
        promises = this.files.map(function (file) {
          // If multiple source files are specified, the destination
          // path should point to a directory
          var single = file.orig.src.length === 1 &&
                !file.orig.src.some(function (src) {
                  return src.indexOf('*') >= 0 || src.indexOf('?') >= 0;
                }),
              promises = file.src.map(function (src) {
                // If the destination is a directory, use the source file name
                // with the target image format as extension
                var dest = single ? file.dest : path.join(file.dest,
                      path.parse(src).name + '.' + options.format),
                    dir = path.dirname(dest);
                grunt.file.mkdir(dir);
                return processDiagram(src, dest, options);
              });
          return Q.all(promises);
        });
    Q.all(promises)
     .then(done);
  });
};
<|endoftext|>


<commit_before>/*
 * grunt-yate
 * https://github.com/lapple/grunt-yate
 *
 * Copyright (c) 2013 Aziz Yuldoshev
 * Licensed under the MIT license.
 */

'use strict';

var yate = require('yate');
var path = require('path');

var yateFolder = path.dirname(require.resolve('yate'));

module.exports = function(grunt) {

  var async = grunt.util.async;

  grunt.registerMultiTask('yate', 'Yate compiler plugin', function() {

    var options = this.options({
      runtime: false,
      autorun: false,

      // List of externals-containing files to be added to the compiled
      // templates.
      externals: [],

      // Default no-op postprocess function. Use `postprocess`
      // to define custom compiled code transformations.
      postprocess: function(code) {
        return code;
      }
    });

    var done = this.async();

    // Iterate over all specified file groups.
    async.forEachSeries(this.files, function(f, next) {

      // Building compiled templates.
      var src = f.src.filter(function(filepath) {

        // Warn on and remove invalid source
        // files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }

      }).map(function(filepath) {
        var compiled;

        try {
          compiled = yate.compile(filepath).js;
        } catch(e) {
          grunt.event.emit('yate:error', e);
          grunt.fail.warn(e);
          done();
        }

        return compiled;
      }).join(grunt.util.linefeed);

      // Building a list of files to prepend to the compiled template.
      var includes = grunt.file.expand(options.externals);

      // It is important to prepend externals with runtime.
      if (options.runtime) {
        includes.unshift(path.join(yateFolder, 'runtime.js'));
      }

      async.map(includes, readFile, function(err, scripts) {
        if (err) {
          grunt.log.warn(err);
          return next(false);
        }

        src = scripts.join(grunt.util.linefeed) + grunt.util.linefeed + src;

        if (options.autorun) {
          src = autorun(src, options.autorun);
        }

        src = options.postprocess(src);

        // Write the destination file.
        grunt.file.write(f.dest, src);

        // Print a success message.
        grunt.log.writeln('File ' + f.dest.cyan + ' created.');
        next();
      });

    }, done);
  });

  function autorun(code, module) {
    var main = typeof module === 'string' ? module : 'main';

    return iife(code + grunt.util.linefeed + 'return function(data) { return yr.run("' + main + '", data); };');
  }

  function iife(code) {
    return '(function(){' + grunt.util.linefeed + code + grunt.util.linefeed + '})()' + grunt.util.linefeed;
  }

  function readFile(path, callback) {
    return require('fs').readFile(path, { encoding: 'utf8' }, callback);
  }

};
<commit_msg>Failure update<commit_after>/*
 * grunt-yate
 * https://github.com/lapple/grunt-yate
 *
 * Copyright (c) 2013 Aziz Yuldoshev
 * Licensed under the MIT license.
 */

'use strict';

var yate = require('yate');
var path = require('path');

var yateFolder = path.dirname(require.resolve('yate'));

module.exports = function(grunt) {

  var async = grunt.util.async;

  grunt.registerMultiTask('yate', 'Yate compiler plugin', function() {

    var options = this.options({
      runtime: false,
      autorun: false,

      // List of externals-containing files to be added to the compiled
      // templates.
      externals: [],

      // Default no-op postprocess function. Use `postprocess`
      // to define custom compiled code transformations.
      postprocess: function(code) {
        return code;
      }
    });

    // Error flag.
    var failure = false;

    // Iterate over all specified file groups.
    async.forEachSeries(this.files, function(f, next) {

      // Building compiled templates.
      var src = f.src.filter(function(filepath) {

        // Warn on and remove invalid source
        // files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }

      }).map(function(filepath) {
        var compiled;

        try {
          compiled = yate.compile(filepath).js;
        } catch(e) {
          failure = true;

          grunt.event.emit('yate:error', e);
          grunt.fail.warn(e);
        }

        return compiled;
      }).join(grunt.util.linefeed);

      if (failure) {
        return next();
      }

      // Building a list of files to prepend to the compiled template.
      var includes = grunt.file.expand(options.externals);

      // It is important to prepend externals with runtime.
      if (options.runtime) {
        includes.unshift(path.join(yateFolder, 'runtime.js'));
      }

      async.map(includes, readFile, function(err, scripts) {
        if (err) {
          grunt.log.warn(err);
          return next(false);
        }

        src = scripts.join(grunt.util.linefeed) + grunt.util.linefeed + src;

        if (options.autorun) {
          src = autorun(src, options.autorun);
        }

        src = options.postprocess(src);

        // Write the destination file.
        grunt.file.write(f.dest, src);

        // Print a success message.
        grunt.log.writeln('File ' + f.dest.cyan + ' created.');
        next();
      });

    }, this.async());
  });

  function autorun(code, module) {
    var main = typeof module === 'string' ? module : 'main';

    return iife(code + grunt.util.linefeed + 'return function(data) { return yr.run("' + main + '", data); };');
  }

  function iife(code) {
    return '(function(){' + grunt.util.linefeed + code + grunt.util.linefeed + '})()' + grunt.util.linefeed;
  }

  function readFile(path, callback) {
    return require('fs').readFile(path, { encoding: 'utf8' }, callback);
  }

};
<|endoftext|>


<commit_before>import path from 'path';
import puppeteer from 'puppeteer';

describe('UMD build', () => {
  const hibpTest = `
    if (typeof window.hibp !== 'object') throw new Error();
    if (typeof window.hibp.breach !== 'function') throw new Error();
    if (typeof window.hibp.breachedAccount !== 'function') throw new Error();
    if (typeof window.hibp.breaches !== 'function') throw new Error();
    if (typeof window.hibp.dataClasses !== 'function') throw new Error();
    if (typeof window.hibp.pasteAccount !== 'function') throw new Error();
    if (typeof window.hibp.pwnedPassword !== 'function') throw new Error();
    if (typeof window.hibp.pwnedPasswordRange !== 'function') throw new Error();
    if (typeof window.hibp.search !== 'function') throw new Error();
  `;

  let browser;

  beforeAll(async () => {
    browser = await puppeteer.launch({ args: ['--no-sandbox'] });
  });

  afterAll(() => {
    browser.close();
  });

  it('should expose the hibp namespace on the window object', async () => {
    const page = await browser.newPage();
    const testUrl = `file://${path.join(__dirname, 'index.html')}`;

    await page.goto(testUrl);
    return page.evaluate(hibpTest);
  });
});
<commit_msg>Increase jest test timeout for UMD test<commit_after>import path from 'path';
import puppeteer from 'puppeteer';

jest.setTimeout(20000);

describe('UMD build', () => {
  const hibpTest = `
    if (typeof window.hibp !== 'object') throw new Error();
    if (typeof window.hibp.breach !== 'function') throw new Error();
    if (typeof window.hibp.breachedAccount !== 'function') throw new Error();
    if (typeof window.hibp.breaches !== 'function') throw new Error();
    if (typeof window.hibp.dataClasses !== 'function') throw new Error();
    if (typeof window.hibp.pasteAccount !== 'function') throw new Error();
    if (typeof window.hibp.pwnedPassword !== 'function') throw new Error();
    if (typeof window.hibp.pwnedPasswordRange !== 'function') throw new Error();
    if (typeof window.hibp.search !== 'function') throw new Error();
  `;

  let browser;

  beforeAll(async () => {
    browser = await puppeteer.launch({ args: ['--no-sandbox'] });
  });

  afterAll(() => {
    browser.close();
  });

  it('should expose the hibp namespace on the window object', async () => {
    const page = await browser.newPage();
    const testUrl = `file://${path.join(__dirname, 'index.html')}`;

    await page.goto(testUrl);
    return page.evaluate(hibpTest);
  });
});
<|endoftext|>


<commit_before><commit_msg>- Implemented the daterange autocompletion for scheduled task drop<commit_after><|endoftext|>


<commit_before>const assert = require('assert');
const { ActionTransport } = require('@microfleet/core');
const { NotPermitted } = require('common-errors');
const Promise = require('bluebird');
const moment = require('moment');
const get = require('get-value');

// helpers
const key = require('../../redis-key');
const resetToFreePlan = require('../../utils/reset-to-free-plan');
const { hmget } = require('../../list-utils');
const { PLANS_DATA, AGREEMENT_DATA, FREE_PLAN_ID } = require('../../constants');

// constants
const AGREEMENT_KEYS = ['agreement', 'plan', 'owner', 'state'];
const PLAN_KEYS = ['subs'];
const agreementParser = hmget(AGREEMENT_KEYS, JSON.parse, JSON);
const planParser = hmget(PLAN_KEYS, JSON.parse, JSON);
const kValidTxStatuses = {
  completed: true,
};

/**
 * Parses agreement data retrieved from redis
 * @param  {Object} service - current microfleet
 * @param  {String} id - agreement id
 * @return {Object} agreement
 */
async function parseAgreementData(service, id) {
  const { redis, log } = service;
  const agreementKey = key(AGREEMENT_DATA, id);
  const data = await redis.hmget(agreementKey, AGREEMENT_KEYS);

  try {
    const parsed = agreementParser(data);

    // assign owner
    parsed.agreement.owner = parsed.owner;

    // FIXME: PAYPAL agreement doesn't have embedded plan id...
    // bug in paypal
    parsed.agreement.plan.id = parsed.plan;

    // return data
    return parsed;
  } catch (e) {
    log.error({
      err: e,
      keys: AGREEMENT_KEYS,
      source: String(data),
      agreementKey,
    }, 'failed to fetch agreement data');

    throw e;
  }
}

const freeAgreementForUser = (username) => ({
  owner: username,
  plan: {
    id: FREE_PLAN_ID,
  },
});

const verifyAgreementState = (state) => {
  // verify state
  if (!state || state.toLowerCase() === 'cancelled') {
    throw new NotPermitted('Operation not permitted on cancelled agreements.');
  }
};

/**
 * Retrieves agreement data from redis & parses it
 * @return {Agreement}
 */
async function getAgreement(ctx) {
  const { id, username, service } = ctx;

  if (id === FREE_PLAN_ID) {
    return freeAgreementForUser(username);
  }

  // parsed correctly
  const { agreement, state } = await parseAgreementData(service, id);
  verifyAgreementState(state);

  return agreement;
}

/**
 * Retrieves associated plan for requested agreement
 * @return {Object} { plan, subs }
 */
const getPlan = async (ctx, agreement) => {
  const { service, username } = ctx;
  const { redis, log } = service;

  const planKey = key(PLANS_DATA, agreement.plan.id);
  const response = await redis.hmget(planKey, PLAN_KEYS);

  try {
    const { subs } = planParser(response);
    assert(subs, 'subs not present');
    return subs;
  } catch (e) {
    log.error({ response, planKey, username }, 'failed to fetch plan in redis');
    throw e;
  }
};

// fetch transactions from paypal
const getTransactions = async (ctx, agreement) => {
  const { service, id, start, end } = ctx;

  if (agreement.plan.id === FREE_PLAN_ID) {
    return {};
  }

  const agreementData = await service.dispatch('transaction.sync', {
    params: {
      id,
      start,
      end,
    },
  });

  return agreementData;
};

// bill next free cycle
const billNextFreeCycle = (ctx) => {
  const { params } = ctx;

  const nextCycle = moment(params.nextCycle);
  const current = moment();

  // 0 or 1
  ctx.cyclesBilled = Number(nextCycle.isBefore(current));
  ctx.nextCycle = nextCycle;

  // if we missed many cycles
  if (ctx.cyclesBilled) {
    while (nextCycle.isBefore(current)) {
      nextCycle.add(1, 'month');
    }
  }
};

function billPaidCycle(ctx, details) {
  const { params } = ctx;

  // agreement nextCycle date
  const nextCycle = moment(details.agreement.agreement_details.next_billing_date || params.nextCycle);
  const currentCycle = moment(params.nextCycle).subtract(1, 'day');
  const { transactions } = details;

  // determine how many cycles and next billing date
  ctx.nextCycle = nextCycle;
  ctx.cyclesBilled = transactions.reduce((acc, it) => {
    const status = it.status && it.status.toLowerCase();

    // TODO: does paypal charge earlier?
    // we need to filter out setup fee
    if (kValidTxStatuses[status] && moment(it.time_stamp).isAfter(currentCycle)) {
      return acc + 1;
    }

    return acc;
  }, 0);
}

// verify transactions data
const checkData = (ctx, agreement, details) => {
  if (agreement.plan.id === FREE_PLAN_ID) {
    return billNextFreeCycle(ctx);
  }

  if (details.transactions.length === 0) {
    // no outstanding transactions
    ctx.shouldUpdate = false;
    return false;
  }

  return billPaidCycle(ctx, details);
};

const saveToRedis = async (ctx, agreement, subs) => {
  const { updateMetadata, service, audience } = ctx;
  const { amqp } = service;

  // no updates yet - skip to next
  if (ctx.shouldUpdate === false) {
    return 'OK';
  }

  const planFreq = get(agreement, 'plan.payment_definitions[0].frequency', 'month').toLowerCase();
  const sub = subs.find((x) => x.name === planFreq);
  const models = sub.models * ctx.cyclesBilled;

  const updateRequest = {
    username: agreement.owner,
    audience,
    metadata: {
      $set: {
        nextCycle: ctx.nextCycle.valueOf(),
      },
      $incr: {
        models,
      },
    },
  };

  await amqp.publishAndWait(updateMetadata, updateRequest, { timeout: 15000 });

  return 'OK';
};

// check agreement bill
async function agreementBill({ params }) {
  const { agreement: id, subscriptionInterval, username } = params;
  const { config, log } = this;
  const { users: { prefix, postfix } } = config;
  const start = moment().subtract(2, subscriptionInterval).format('YYYY-MM-DD');
  const end = moment().add(1, 'day').format('YYYY-MM-DD');

  log.debug('billing %s on %s', username, id);

  const ctx = {
    service: this,

    // used params
    id,
    username,
    start,
    end,
    params,
    audience: config.users.audience,

    // pre-calculated vars
    updateMetadata: `${prefix}.${postfix.updateMetadata}`,
  };

  try {
    const agreement = await getAgreement(ctx);

    const [subs, details] = await Promise.all([
      getPlan(ctx, agreement),
      getTransactions(ctx, agreement),
    ]);

    checkData(ctx, agreement, details);

    return await saveToRedis(ctx, agreement, subs);
  } catch (e) {
    if (e instanceof NotPermitted) {
      log.warn({ err: e }, 'Agreement %s was cancelled by user %s', username, id);
      return resetToFreePlan.call(this, username);
    }

    if (e.statusCode === 400 && e.message === 'The profile ID is invalid') {
      return resetToFreePlan.call(this, username);
    }

    log.warn({ err: e }, 'Failed to sync', username, id);
    throw e;
  }
}

agreementBill.transports = [ActionTransport.amqp, ActionTransport.internal];

module.exports = agreementBill;
<commit_msg>fix: add another log handler to bill<commit_after>const assert = require('assert');
const { ActionTransport } = require('@microfleet/core');
const { NotPermitted, HttpStatusError } = require('common-errors');
const Promise = require('bluebird');
const moment = require('moment');
const get = require('get-value');

// helpers
const key = require('../../redis-key');
const resetToFreePlan = require('../../utils/reset-to-free-plan');
const { hmget } = require('../../list-utils');
const { PLANS_DATA, AGREEMENT_DATA, FREE_PLAN_ID } = require('../../constants');

// constants
const AGREEMENT_KEYS = ['agreement', 'plan', 'owner', 'state'];
const PLAN_KEYS = ['subs'];
const agreementParser = hmget(AGREEMENT_KEYS, JSON.parse, JSON);
const planParser = hmget(PLAN_KEYS, JSON.parse, JSON);
const kValidTxStatuses = {
  completed: true,
};

/**
 * Parses agreement data retrieved from redis
 * @param  {Object} service - current microfleet
 * @param  {String} id - agreement id
 * @return {Object} agreement
 */
async function parseAgreementData(service, id) {
  const { redis, log } = service;
  const agreementKey = key(AGREEMENT_DATA, id);
  const data = await redis.hmget(agreementKey, AGREEMENT_KEYS);

  try {
    const parsed = agreementParser(data);

    // assign owner
    parsed.agreement.owner = parsed.owner;

    // FIXME: PAYPAL agreement doesn't have embedded plan id...
    // bug in paypal
    parsed.agreement.plan.id = parsed.plan;

    // return data
    return parsed;
  } catch (e) {
    log.error({
      err: e,
      keys: AGREEMENT_KEYS,
      source: String(data),
      agreementKey,
    }, 'failed to fetch agreement data');

    throw e;
  }
}

const freeAgreementForUser = (username) => ({
  owner: username,
  plan: {
    id: FREE_PLAN_ID,
  },
});

const verifyAgreementState = (state) => {
  // verify state
  if (!state || state.toLowerCase() === 'cancelled') {
    throw new NotPermitted('Operation not permitted on cancelled agreements.');
  }
};

/**
 * Retrieves agreement data from redis & parses it
 * @return {Agreement}
 */
async function getAgreement(ctx) {
  const { id, username, service } = ctx;

  if (id === FREE_PLAN_ID) {
    return freeAgreementForUser(username);
  }

  // parsed correctly
  const { agreement, state } = await parseAgreementData(service, id);
  verifyAgreementState(state);

  return agreement;
}

/**
 * Retrieves associated plan for requested agreement
 * @return {Object} { plan, subs }
 */
const getPlan = async (ctx, agreement) => {
  const { service, username } = ctx;
  const { redis, log } = service;

  const planKey = key(PLANS_DATA, agreement.plan.id);
  const response = await redis.hmget(planKey, PLAN_KEYS);

  try {
    const { subs } = planParser(response);
    assert(subs, 'subs not present');
    return subs;
  } catch (e) {
    log.error({ response, planKey, username }, 'failed to fetch plan in redis');
    throw e;
  }
};

// fetch transactions from paypal
const getTransactions = async (ctx, agreement) => {
  const { service, id, start, end } = ctx;

  if (agreement.plan.id === FREE_PLAN_ID) {
    return {};
  }

  const agreementData = await service.dispatch('transaction.sync', {
    params: {
      id,
      start,
      end,
    },
  });

  return agreementData;
};

// bill next free cycle
const billNextFreeCycle = (ctx) => {
  const { params } = ctx;

  const nextCycle = moment(params.nextCycle);
  const current = moment();

  // 0 or 1
  ctx.cyclesBilled = Number(nextCycle.isBefore(current));
  ctx.nextCycle = nextCycle;

  // if we missed many cycles
  if (ctx.cyclesBilled) {
    while (nextCycle.isBefore(current)) {
      nextCycle.add(1, 'month');
    }
  }
};

function billPaidCycle(ctx, details) {
  const { params } = ctx;

  // agreement nextCycle date
  const nextCycle = moment(details.agreement.agreement_details.next_billing_date || params.nextCycle);
  const currentCycle = moment(params.nextCycle).subtract(1, 'day');
  const { transactions } = details;

  // determine how many cycles and next billing date
  ctx.nextCycle = nextCycle;
  ctx.cyclesBilled = transactions.reduce((acc, it) => {
    const status = it.status && it.status.toLowerCase();

    // TODO: does paypal charge earlier?
    // we need to filter out setup fee
    if (kValidTxStatuses[status] && moment(it.time_stamp).isAfter(currentCycle)) {
      return acc + 1;
    }

    return acc;
  }, 0);
}

// verify transactions data
const checkData = (ctx, agreement, details) => {
  if (agreement.plan.id === FREE_PLAN_ID) {
    return billNextFreeCycle(ctx);
  }

  if (details.transactions.length === 0) {
    // no outstanding transactions
    ctx.shouldUpdate = false;
    return false;
  }

  return billPaidCycle(ctx, details);
};

const saveToRedis = async (ctx, agreement, subs) => {
  const { updateMetadata, service, audience } = ctx;
  const { amqp } = service;

  // no updates yet - skip to next
  if (ctx.shouldUpdate === false) {
    return 'OK';
  }

  const planFreq = get(agreement, 'plan.payment_definitions[0].frequency', 'month').toLowerCase();
  const sub = subs.find((x) => x.name === planFreq);

  if (!sub) {
    ctx.log.error({ subs, agreement, planFreq }, 'failed to fetch subs');
    throw new HttpStatusError(500, 'internal application error');
  }

  const models = sub.models * ctx.cyclesBilled;

  const updateRequest = {
    username: agreement.owner,
    audience,
    metadata: {
      $set: {
        nextCycle: ctx.nextCycle.valueOf(),
      },
      $incr: {
        models,
      },
    },
  };

  await amqp.publishAndWait(updateMetadata, updateRequest, { timeout: 15000 });

  return 'OK';
};

// check agreement bill
async function agreementBill({ log, params }) {
  const { agreement: id, subscriptionInterval, username } = params;
  const { config } = this;
  const { users: { prefix, postfix } } = config;
  const start = moment().subtract(2, subscriptionInterval).format('YYYY-MM-DD');
  const end = moment().add(1, 'day').format('YYYY-MM-DD');

  log.debug('billing %s on %s', username, id);

  const ctx = {
    service: this,
    log,

    // used params
    id,
    username,
    start,
    end,
    params,
    audience: config.users.audience,

    // pre-calculated vars
    updateMetadata: `${prefix}.${postfix.updateMetadata}`,
  };

  try {
    const agreement = await getAgreement(ctx);

    const [subs, details] = await Promise.all([
      getPlan(ctx, agreement),
      getTransactions(ctx, agreement),
    ]);

    checkData(ctx, agreement, details);

    return await saveToRedis(ctx, agreement, subs);
  } catch (e) {
    if (e instanceof NotPermitted) {
      log.warn({ err: e }, 'Agreement %s was cancelled by user %s', username, id);
      return resetToFreePlan.call(this, username);
    }

    if (e.statusCode === 400 && e.message === 'The profile ID is invalid') {
      return resetToFreePlan.call(this, username);
    }

    log.warn({ err: e }, 'Failed to sync', username, id);
    throw e;
  }
}

agreementBill.transports = [ActionTransport.amqp, ActionTransport.internal];

module.exports = agreementBill;
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name angular-scroll-animate.directive:when-visible
 * @restrict A
 *
 * @description
 * Allows method hooks into the detection of when an element is scrolled into or out of view.
 *
 * @example
 * <example module="angular-scroll-animate">
 *   <file name="index.html">
 *
Broom

 *   </file>
 *   <file name="controller.js">
 *   angular.module('example', []).controller('ExampleCtrl', function($scope) {
 *
 *     $scope.animateIn = function($el) {
 *       $el.removeClass('hidden');
 *       $el.addClass('fadeIn');
 *     };
 *
 *     $scope.animateOut = function($el) {
 *      $el.addClass('hidden');
 *       $el.removeClassClass('fadeIn');
 *     };
 *   });
 *
 *   </file>
 *   <file name="animations.css">
 *     .hidden { visibility: hidden; }
 *     .fadeIn { transition: all 300ms ease-in 2s; }
 *   </file>
 * </example>
 */
angular.module('angular-scroll-animate', []).directive('whenVisible', ['$document', '$window',
 function($document, $window) {

    var determineWhereElementIsInViewport =
      function($el, viewportHeight, whenVisibleFn, whenNotVisibleFn, delayPercent, scope) {

        var elementBounds = $el[0].getBoundingClientRect();

        var panelTop = elementBounds.top;
        var panelBottom = elementBounds.bottom;

        // pixel buffer until deciding to show the element
        var delayPx = delayPercent * elementBounds.height;

        var bottomVisible = (panelBottom - delayPx > 0) && (panelBottom < viewportHeight);
        var topVisible = (panelTop + delayPx <= viewportHeight) && (panelTop > 0);

        if ($el.data('hidden') && bottomVisible || topVisible) {
          whenVisibleFn($el, scope);
          $el.data('hidden', false);
        }

        // scrolled out from scrolling down or up
        else if (!($el.data('hidden')) && (panelBottom < 0 || panelTop > viewportHeight)) {
          whenNotVisibleFn($el, scope);
          $el.data('hidden', true);
        }
      };

    return {
      restrict: 'A',
      scope: {
        whenVisible: '&',
        whenNotVisible: '&?',
        delayPercent: '=?'
      },

      controller: ['$scope', function(scope) {
        if (!scope.whenVisible || !angular.isFunction(scope.whenVisible())) {
          throw new Error('Directive: angular-scroll-animate \'when-visible\' attribute must specify a function.');
        }

        if (scope.whenNotVisible && !angular.isFunction(scope.whenNotVisible())) {
          throw new Error('Directive: angular-scroll-animate \'when-not-visible\' attribute must specify a function.');
        }

        if (scope.delayPercent) {

          var delayPercent = parseFloat(scope.delayPercent);

          if ((Number.isNaN(delayPercent)) || (delayPercent < 0 || delayPercent > 1)) {
            throw new Error('Directive: angular-scroll-animate \'delay-percent\' attribute must be a decimal fraction between 0 and 1.');
          }
        }
    }],

      link: function(scope, el, attributes) {

        var delayPercent = attributes.delayPercent || 0.25; // lower = more eager to hide / show, higher = less eager

        var onScroll = function() {

          var document = $document[0].documentElement;
          var viewportHeight = document.clientHeight;

          determineWhereElementIsInViewport(el, viewportHeight,
            scope.whenVisible(), scope.whenNotVisible(), delayPercent, scope);
        };

        var documentListenerEvents = 'scroll';
        $document.on(documentListenerEvents, onScroll);

        scope.$on('$destroy', function() {
          $document.off(documentListenerEvents, onScroll);
        });

        var $elWindow = angular.element($window);
        var windowListenerEvents = 'resize orientationchange';
        $elWindow.on(windowListenerEvents, onScroll);

        scope.$on('$destroy', function() {
          $elWindow.off(windowListenerEvents, onScroll);
        });

        // initialise
        el.data('hidden', true);
        scope.$evalAsync(onScroll);
      }
    };
 }]);
<commit_msg>Fixed bug when not defining 'when-not-visible' attribute (see https://github.com/rpocklin/angular-scroll-animate/issues/1).<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name angular-scroll-animate.directive:when-visible
 * @restrict A
 * @param {string} when-not-visible function to execute when element is scrolled into viewport
 * @param {string=} when-not-visible function to execute when element is scrolled out of viewport
 *
 * @description
 * Allows method hooks into the detection of when an element is scrolled into or out of view.
 *
 * @example
 * <example module="angular-scroll-animate">
 *   <file name="index.html">
 *
Broom

 *   </file>
 *   <file name="controller.js">
 *   angular.module('example', []).controller('ExampleCtrl', function($scope) {
 *
 *     $scope.animateIn = function($el) {
 *       $el.removeClass('hidden');
 *       $el.addClass('fadeIn');
 *     };
 *
 *     $scope.animateOut = function($el) {
 *      $el.addClass('hidden');
 *       $el.removeClassClass('fadeIn');
 *     };
 *   });
 *
 *   </file>
 *   <file name="animations.css">
 *     .hidden { visibility: hidden; }
 *     .fadeIn { transition: all 300ms ease-in 2s; }
 *   </file>
 * </example>
 */
angular.module('angular-scroll-animate', []).directive('whenVisible', ['$document', '$window',
 function($document, $window) {

    var determineWhereElementIsInViewport =
      function($el, viewportHeight, whenVisibleFn, whenNotVisibleFn, delayPercent, scope) {

        var elementBounds = $el[0].getBoundingClientRect();

        var panelTop = elementBounds.top;
        var panelBottom = elementBounds.bottom;

        // pixel buffer until deciding to show the element
        var delayPx = delayPercent * elementBounds.height;

        var bottomVisible = (panelBottom - delayPx > 0) && (panelBottom < viewportHeight);
        var topVisible = (panelTop + delayPx <= viewportHeight) && (panelTop > 0);

        if ($el.data('hidden') && bottomVisible || topVisible) {
          whenVisibleFn($el, scope);
          $el.data('hidden', false);
        }

        // scrolled out from scrolling down or up
        else if (!($el.data('hidden')) && (panelBottom < 0 || panelTop > viewportHeight)) {
          whenNotVisibleFn($el, scope);
          $el.data('hidden', true);
        }
      };

    return {
      restrict: 'A',
      scope: {
        whenVisible: '&',
        whenNotVisible: '&?',
        delayPercent: '=?'
      },

      controller: ['$scope', function(scope) {
        if (!scope.whenVisible || !angular.isFunction(scope.whenVisible())) {
          throw new Error('Directive: angular-scroll-animate \'when-visible\' attribute must specify a function.');
        }

        if (scope.whenNotVisible && !angular.isFunction(scope.whenNotVisible())) {
          throw new Error('Directive: angular-scroll-animate \'when-not-visible\' attribute must specify a function.');
        }
        else if (!scope.whenNotVisible) {
          scope.whenNotVisible = angular.noop;
        }

        if (scope.delayPercent) {

          var delayPercent = parseFloat(scope.delayPercent);

          if ((Number.isNaN(delayPercent)) || (delayPercent < 0 || delayPercent > 1)) {
            throw new Error('Directive: angular-scroll-animate \'delay-percent\' attribute must be a decimal fraction between 0 and 1.');
          }
        }
    }],

      link: function(scope, el, attributes) {

        var delayPercent = attributes.delayPercent || 0.25; // lower = more eager to hide / show, higher = less eager

        var onScroll = function() {

          var document = $document[0].documentElement;
          var viewportHeight = document.clientHeight;

          determineWhereElementIsInViewport(el, viewportHeight,
            scope.whenVisible(), scope.whenNotVisible(), delayPercent, scope);
        };

        var documentListenerEvents = 'scroll';
        $document.on(documentListenerEvents, onScroll);

        scope.$on('$destroy', function() {
          $document.off(documentListenerEvents, onScroll);
        });

        var $elWindow = angular.element($window);
        var windowListenerEvents = 'resize orientationchange';
        $elWindow.on(windowListenerEvents, onScroll);

        scope.$on('$destroy', function() {
          $elWindow.off(windowListenerEvents, onScroll);
        });

        // initialise
        el.data('hidden', true);
        scope.$evalAsync(onScroll);
      }
    };
 }]);
<|endoftext|>


<commit_before>var TransactionService = require('../transaction/transaction_service')

var OpbeatBackend = require('../backend/opbeat_backend')
var transport = require('../lib/transport')

var Opbeat = require('../opbeat')

function ngOpbeatProvider () {
  this.config = function config (properties) {
    Opbeat.config(properties)
  }

  this.install = function install () {
    Opbeat.install()
  }

  this.version = '%%VERSION%%'

  this.$get = [
    function () {
      return {
        getConfig: function config () {
          return Opbeat.config()
        },
        captureException: function captureException (exception, cause) {
          Opbeat.captureException(exception, cause)
        },

        setUserContext: function setUser (user) {
          Opbeat.setUserContext(user)
        },

        setExtraContext: function setExtraContext (data) {
          Opbeat.setExtraContext(data)
        }
      }
    }
  ]
}

function patchExceptionHandler ($provide) {
  $provide.decorator('$exceptionHandler', ['$delegate', '$opbeat', function $ExceptionHandlerDecorator ($delegate, $opbeat) {
    return function $ExceptionHandler (exception, cause) {
      $opbeat.captureException(exception)
      return $delegate(exception, cause)
    }
  }])
}

var patchHttp = require('./httpPatch')
var patchController = require('./controllerPatch')
var patchCompile = require('./compilePatch')
var patchTemplateRequest = require('./templateRequestPatch')
var patchRootScope = require('./rootScopePatch')
function patchAll ($provide, transactionService) {
  patchExceptionHandler($provide)
  patchHttp($provide, transactionService)
  patchController($provide, transactionService)
  patchCompile($provide, transactionService)
  patchTemplateRequest($provide, transactionService)
  patchRootScope($provide, transactionService)
}

var logger = require('loglevel')

function initialize () {
  var config = Opbeat.config()
  var transactionOptions = {'performance.enableStackFrames': config.get('performance.enableStackFrames')}

  logger.setLevel('debug', false)

  var transactionService = new TransactionService(logger, transactionOptions)

  function moduleRun ($rootScope) {
    function onRouteChangeStart (event, current) {
      logger.debug('Route change started')
      var transactionName
      if (current.$$route) { // ngRoute
        transactionName = current.$$route.originalPath
      } else if (current.url) { // UI Router
        transactionName = current.name // Use state name over URL
      }
      if (transactionName === '') {
        transactionName = 'Main page load'
      }
      transactionService.startTransaction(transactionName, 'transaction', transactionOptions)
    }
    $rootScope.$on('$routeChangeStart', onRouteChangeStart) // ng-router
    $rootScope.$on('$routeChangeSuccess', function () {
      setTimeout(function () {
        logger.debug('Route change success')
        transactionService.endCurrentTransaction()
      }, 0)
    })

    $rootScope.$on('$stateChangeSuccess', onRouteChangeStart) // ui-router
  }

  var opbeatBackend = new OpbeatBackend(transport, logger)

  setInterval(function () {
    var transactions = transactionService.getTransactions()

    if (transactions.length === 0) {
      return
    }
    logger.debug('Sending Transactions to opbeat.', transactions.length)
    // todo: if transactions are already being sent, should check
    opbeatBackend.sendTransactions(transactions)
    transactionService.clearTransactions()
  }, 5000)

  // Ignoring zonejs for now
  // var useZoneJS = false
  // if (useZoneJS) {
  //   var ZoneService = require('../transaction/zone_service')
  //   var zoneService = new ZoneService(window.zone, transactionService, logger)
  //   window.angular.bootstrap = zoneService.zone.bind(window.angular.bootstrap)
  // }

  function moduleConfig ($provide) {
    patchAll($provide, transactionService)
  }

  window.angular.module('ngOpbeat', [])
    .provider('$opbeat', ngOpbeatProvider)
    .config(['$provide', moduleConfig])
    .run(['$rootScope', moduleRun])
  window.angular.module('angular-opbeat', ['ngOpbeat'])
}

initialize()
<commit_msg>minor refactoring angular-opbeat<commit_after>var TransactionService = require('../transaction/transaction_service')

var OpbeatBackend = require('../backend/opbeat_backend')
var transport = require('../lib/transport')

var Opbeat = require('../opbeat')

function ngOpbeatProvider () {
  this.config = function config (properties) {
    Opbeat.config(properties)
  }

  this.install = function install () {
    Opbeat.install()
  }

  this.version = '%%VERSION%%'

  this.$get = [
    function () {
      return {
        getConfig: function config () {
          return Opbeat.config()
        },
        captureException: function captureException (exception, cause) {
          Opbeat.captureException(exception, cause)
        },

        setUserContext: function setUser (user) {
          Opbeat.setUserContext(user)
        },

        setExtraContext: function setExtraContext (data) {
          Opbeat.setExtraContext(data)
        }
      }
    }
  ]
}

function patchExceptionHandler ($provide) {
  $provide.decorator('$exceptionHandler', ['$delegate', '$opbeat', function $ExceptionHandlerDecorator ($delegate, $opbeat) {
    return function $ExceptionHandler (exception, cause) {
      $opbeat.captureException(exception)
      return $delegate(exception, cause)
    }
  }])
}

var patchHttp = require('./httpPatch')
var patchController = require('./controllerPatch')
var patchCompile = require('./compilePatch')
var patchTemplateRequest = require('./templateRequestPatch')
var patchRootScope = require('./rootScopePatch')
function patchAll ($provide, transactionService) {
  patchExceptionHandler($provide)
  patchHttp($provide, transactionService)
  patchController($provide, transactionService)
  patchCompile($provide, transactionService)
  patchTemplateRequest($provide, transactionService)
  patchRootScope($provide, transactionService)
}

var logger = require('loglevel')

function initialize () {
  var config = Opbeat.config()
  var transactionOptions = {'performance.enableStackFrames': config.get('performance.enableStackFrames')}

  logger.setLevel('debug', false)

  var transactionService = new TransactionService(logger, transactionOptions)

  function moduleRun ($rootScope) {
    function onRouteChangeStart (event, current) {
      logger.debug('Route change started')
      var transactionName
      if (current.$$route) { // ngRoute
        transactionName = current.$$route.originalPath
      } else if (current.url) { // UI Router
        transactionName = current.name // Use state name over URL
      }
      if (transactionName === '') {
        transactionName = 'Main page load'
      }
      transactionService.startTransaction(transactionName, 'transaction', transactionOptions)
    }

    function onRouteChangeSuccess () {
      setTimeout(function () {
        logger.debug('Route change success')
        transactionService.endCurrentTransaction()
      }, 0)
    }

    // ng-router
    $rootScope.$on('$routeChangeStart', onRouteChangeStart)
    $rootScope.$on('$routeChangeSuccess', onRouteChangeSuccess)

    // ui-router
    $rootScope.$on('$stateChangeStart', onRouteChangeStart)
    $rootScope.$on('$stateChangeSuccess', onRouteChangeSuccess)
  }

  var opbeatBackend = new OpbeatBackend(transport, logger)

  setInterval(function () {
    var transactions = transactionService.getTransactions()

    if (transactions.length === 0) {
      return
    }
    logger.debug('Sending Transactions to opbeat.', transactions.length)
    // todo: if transactions are already being sent, should check
    opbeatBackend.sendTransactions(transactions)
    transactionService.clearTransactions()
  }, 5000)

  // Ignoring zonejs for now
  // var useZoneJS = false
  // if (useZoneJS) {
  //   var ZoneService = require('../transaction/zone_service')
  //   var zoneService = new ZoneService(window.zone, transactionService, logger)
  //   window.angular.bootstrap = zoneService.zone.bind(window.angular.bootstrap)
  // }

  function moduleConfig ($provide) {
    patchAll($provide, transactionService)
  }

  window.angular.module('ngOpbeat', [])
    .provider('$opbeat', ngOpbeatProvider)
    .config(['$provide', moduleConfig])
    .run(['$rootScope', moduleRun])
  window.angular.module('angular-opbeat', ['ngOpbeat'])
}

initialize()
<|endoftext|>


<commit_before>'use strict'
var EthJSTX = require('ethereumjs-tx')
var EthJSBlock = require('ethereumjs-block')
var ethJSUtil = require('ethereumjs-util')
var BN = ethJSUtil.BN
var executionContext = require('../../execution-context')
var modalDialog = require('../ui/modaldialog')
var yo = require('yo-yo')
var typeConversion = require('../../lib/typeConversion')
var csjs = require('csjs-inject')
var remixLib = require('remix-lib')
var styleGuide = remixLib.ui.styleGuide
var styles = styleGuide()

var css = csjs`
  .txInfoBox {
    ${styles.rightPanel.compileTab.box_CompileContainer};  // add askToConfirmTXContainer to Remix and then replace this styling
  }
  .wrapword {
    white-space: pre-wrap;       /* Since CSS 2.1 */
    white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
    white-space: -pre-wrap;      /* Opera 4-6 */
    white-space: -o-pre-wrap;    /* Opera 7 */
    word-wrap: break-word;       /* Internet Explorer 5.5+ */
  }
`

function TxRunner (vmaccounts, opts) {
  this.personalMode = opts.personalMode
  this.blockNumber = 0
  this.runAsync = true
  this.config = opts.config
  this.detectNetwork = opts.detectNetwork
  if (executionContext.isVM()) {
    this.blockNumber = 1150000 // The VM is running in Homestead mode, which started at this block.
    this.runAsync = false // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time.
  }
  this.pendingTxs = {}
  this.vmaccounts = vmaccounts
  this.queusTxs = []
}

TxRunner.prototype.rawRun = function (args, cb) {
  run(this, args, Date.now(), cb)
}

TxRunner.prototype.execute = function (args, callback) {
  function execute (gasPrice) {
    if (gasPrice) tx.gasPrice = executionContext.web3().toHex(gasPrice)

    var sendTransaction = self.personalMode ? executionContext.web3().personal.sendTransaction : executionContext.web3().eth.sendTransaction
    try {
      sendTransaction(tx, function (err, resp) {
        if (err) {
          return callback(err, resp)
        }

        tryTillResponse(resp, callback)
      })
    } catch (e) {
      return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `)
    }
  }
  var self = this
  var from = args.from
  var to = args.to
  var data = args.data
  if (data.slice(0, 2) !== '0x') {
    data = '0x' + data
  }
  var value = args.value
  var gasLimit = args.gasLimit

  var tx
  if (!executionContext.isVM()) {
    tx = {
      from: from,
      to: to,
      data: data,
      value: value
    }

    if (args.useCall) {
      tx.gas = gasLimit
      executionContext.web3().eth.call(tx, function (error, result) {
        callback(error, {
          result: result,
          transactionHash: result.transactionHash
        })
      })
    } else {
      executionContext.web3().eth.estimateGas(tx, function (err, gasEstimation) {
        if (err) {
          return callback(err, gasEstimation)
        }
        var blockGasLimit = executionContext.currentblockGasLimit()
        // NOTE: estimateGas very likely will return a large limit if execution of the code failed
        //       we want to be able to run the code in order to debug and find the cause for the failure

        var warnEstimation = " An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that's also the reason of strong gas estimation)."
        if (gasEstimation > gasLimit) {
          return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation)
        }
        if (gasEstimation > blockGasLimit) {
          return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation)
        }

        tx.gas = gasEstimation

        if (!self.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) {
          self.detectNetwork((err, network) => {
            if (err) {
              console.log(err)
            } else {
              if (network.name === 'Main') {
                var content = confirmDialog(tx, gasEstimation, self)
                modalDialog('Confirm transaction', content,
                  { label: 'Confirm',
                    fn: () => {
                      self.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
                      if (!content.gasPriceStatus) {
                        callback('Given gas grice is not correct')
                      } else {
                        var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei')
                        execute(gasPrice)
                      }
                    }}, {
                      label: 'Cancel',
                      fn: () => {
                        return callback('Transaction canceled by user.')
                      }
                    })
              } else {
                execute()
              }
            }
          })
        } else {
          execute()
        }
      })
    }
  } else {
    try {
      var account = self.vmaccounts[from]
      if (!account) {
        return callback('Invalid account selected')
      }
      tx = new EthJSTX({
        nonce: new BN(account.nonce++),
        gasPrice: new BN(1),
        gasLimit: new BN(gasLimit, 10),
        to: to,
        value: new BN(value, 10),
        data: new Buffer(data.slice(2), 'hex')
      })
      tx.sign(account.privateKey)

      const coinbases = [ '0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e' ]
      const difficulties = [ new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10) ]
      var block = new EthJSBlock({
        header: {
          timestamp: new Date().getTime() / 1000 | 0,
          number: self.blockNumber,
          coinbase: coinbases[self.blockNumber % coinbases.length],
          difficulty: difficulties[self.blockNumber % difficulties.length],
          gasLimit: new BN(gasLimit, 10).imuln(2)
        },
        transactions: [],
        uncleHeaders: []
      })
      if (!args.useCall) {
        ++self.blockNumber
      } else {
        executionContext.vm().stateManager.checkpoint()
      }

      executionContext.vm().runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) {
        if (args.useCall) {
          executionContext.vm().stateManager.revert(function () {})
        }
        err = err ? err.message : err
        result.status = '0x' + result.vm.exception.toString(16)
        callback(err, {
          result: result,
          transactionHash: ethJSUtil.bufferToHex(new Buffer(tx.hash()))
        })
      })
    } catch (e) {
      callback(e, null)
    }
  }
}

function tryTillResponse (txhash, done) {
  executionContext.web3().eth.getTransactionReceipt(txhash, function (err, result) {
    if (!err && !result) {
      // Try again with a bit of delay
      setTimeout(function () { tryTillResponse(txhash, done) }, 500)
    } else {
      done(err, {
        result: result,
        transactionHash: result.transactionHash
      })
    }
  })
}

function run (self, tx, stamp, callback) {
  if (!self.runAsync && Object.keys(self.pendingTxs).length) {
    self.queusTxs.push({ tx, stamp, callback })
  } else {
    self.pendingTxs[stamp] = tx
    self.execute(tx, (error, result) => {
      delete self.pendingTxs[stamp]
      callback(error, result)
      if (self.queusTxs.length) {
        var next = self.queusTxs.pop()
        run(self, next.tx, next.stamp, next.callback)
      }
    })
  }
}

function confirmDialog (tx, gasEstimation, self) {
  var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether')
  var input = yo`<input id='confirmsetting' type="checkbox">`
  var el = yo`

You are creating a transaction on the main network. Click confirm if you are sure to continue.

From: ${tx.from}

To: ${tx.to ? tx.to : '(Contract Creation)'}

Amount: ${amount} Ether

Gas estimation: ${gasEstimation}

Gas limit: ${tx.gas}

Gas price: <input id='gasprice' oninput=${gasPriceChanged} /> Gwei

Max transaction fee:

Data:

${tx.data}

      ${input}
       Do not ask for confirmation again. (the setting will not be persisted for the next page reload)

  `
  function gasPriceChanged () {
    try {
      var gasPrice = el.querySelector('#gasprice').value
      var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei')))
      el.querySelector('#txfee').innerHTML = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether'
      el.gasPriceStatus = true
    } catch (e) {
      el.querySelector('#txfee').innerHTML = ' Please fix this issue before sending any transaction. ' + e.message
      el.gasPriceStatus = false
    }
  }

  executionContext.web3().eth.getGasPrice((error, gasPrice) => {
    if (error) {
      el.querySelector('#txfee').innerHTML = 'Unable to retrieve the current network gas price. Please fix this issue before sending any transaction.
' + error
    } else {
      try {
        var fee = executionContext.web3().toBigNumber(tx.gas).mul(gasPrice)
        el.querySelector('#txfee').innerHTML = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether'
        el.querySelector('#gasprice').value = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei')
        el.gasPriceStatus = true
      } catch (e) {
        el.querySelector('#txfee').innerHTML = ' Please fix this issue before sending any transaction. ' + e.message
        el.gasPriceStatus = false
      }
    }
  })
  return el
}

module.exports = TxRunner
<commit_msg>refactor gasprice format<commit_after>'use strict'
var EthJSTX = require('ethereumjs-tx')
var EthJSBlock = require('ethereumjs-block')
var ethJSUtil = require('ethereumjs-util')
var BN = ethJSUtil.BN
var executionContext = require('../../execution-context')
var modalDialog = require('../ui/modaldialog')
var yo = require('yo-yo')
var typeConversion = require('../../lib/typeConversion')
var csjs = require('csjs-inject')
var remixLib = require('remix-lib')
var styleGuide = remixLib.ui.styleGuide
var styles = styleGuide()

var css = csjs`
  .txInfoBox {
    ${styles.rightPanel.compileTab.box_CompileContainer};  // add askToConfirmTXContainer to Remix and then replace this styling
  }
  .wrapword {
    white-space: pre-wrap;       /* Since CSS 2.1 */
    white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
    white-space: -pre-wrap;      /* Opera 4-6 */
    white-space: -o-pre-wrap;    /* Opera 7 */
    word-wrap: break-word;       /* Internet Explorer 5.5+ */
  }
`

function TxRunner (vmaccounts, opts) {
  this.personalMode = opts.personalMode
  this.blockNumber = 0
  this.runAsync = true
  this.config = opts.config
  this.detectNetwork = opts.detectNetwork
  if (executionContext.isVM()) {
    this.blockNumber = 1150000 // The VM is running in Homestead mode, which started at this block.
    this.runAsync = false // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time.
  }
  this.pendingTxs = {}
  this.vmaccounts = vmaccounts
  this.queusTxs = []
}

TxRunner.prototype.rawRun = function (args, cb) {
  run(this, args, Date.now(), cb)
}

TxRunner.prototype.execute = function (args, callback) {
  function execute (gasPrice) {
    if (gasPrice) tx.gasPrice = executionContext.web3().toHex(gasPrice)

    var sendTransaction = self.personalMode ? executionContext.web3().personal.sendTransaction : executionContext.web3().eth.sendTransaction
    try {
      sendTransaction(tx, function (err, resp) {
        if (err) {
          return callback(err, resp)
        }

        tryTillResponse(resp, callback)
      })
    } catch (e) {
      return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `)
    }
  }
  var self = this
  var from = args.from
  var to = args.to
  var data = args.data
  if (data.slice(0, 2) !== '0x') {
    data = '0x' + data
  }
  var value = args.value
  var gasLimit = args.gasLimit

  var tx
  if (!executionContext.isVM()) {
    tx = {
      from: from,
      to: to,
      data: data,
      value: value
    }

    if (args.useCall) {
      tx.gas = gasLimit
      executionContext.web3().eth.call(tx, function (error, result) {
        callback(error, {
          result: result,
          transactionHash: result.transactionHash
        })
      })
    } else {
      executionContext.web3().eth.estimateGas(tx, function (err, gasEstimation) {
        if (err) {
          return callback(err, gasEstimation)
        }
        var blockGasLimit = executionContext.currentblockGasLimit()
        // NOTE: estimateGas very likely will return a large limit if execution of the code failed
        //       we want to be able to run the code in order to debug and find the cause for the failure

        var warnEstimation = " An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that's also the reason of strong gas estimation)."
        if (gasEstimation > gasLimit) {
          return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation)
        }
        if (gasEstimation > blockGasLimit) {
          return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation)
        }

        tx.gas = gasEstimation

        if (!self.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) {
          self.detectNetwork((err, network) => {
            if (err) {
              console.log(err)
            } else {
              if (network.name === 'Main') {
                var content = confirmDialog(tx, gasEstimation, self)
                modalDialog('Confirm transaction', content,
                  { label: 'Confirm',
                    fn: () => {
                      self.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
                      if (!content.gasPriceStatus) {
                        callback('Given gas grice is not correct')
                      } else {
                        var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei')
                        execute(gasPrice)
                      }
                    }}, {
                      label: 'Cancel',
                      fn: () => {
                        return callback('Transaction canceled by user.')
                      }
                    })
              } else {
                execute()
              }
            }
          })
        } else {
          execute()
        }
      })
    }
  } else {
    try {
      var account = self.vmaccounts[from]
      if (!account) {
        return callback('Invalid account selected')
      }
      tx = new EthJSTX({
        nonce: new BN(account.nonce++),
        gasPrice: new BN(1),
        gasLimit: new BN(gasLimit, 10),
        to: to,
        value: new BN(value, 10),
        data: new Buffer(data.slice(2), 'hex')
      })
      tx.sign(account.privateKey)

      const coinbases = [ '0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e' ]
      const difficulties = [ new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10) ]
      var block = new EthJSBlock({
        header: {
          timestamp: new Date().getTime() / 1000 | 0,
          number: self.blockNumber,
          coinbase: coinbases[self.blockNumber % coinbases.length],
          difficulty: difficulties[self.blockNumber % difficulties.length],
          gasLimit: new BN(gasLimit, 10).imuln(2)
        },
        transactions: [],
        uncleHeaders: []
      })
      if (!args.useCall) {
        ++self.blockNumber
      } else {
        executionContext.vm().stateManager.checkpoint()
      }

      executionContext.vm().runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) {
        if (args.useCall) {
          executionContext.vm().stateManager.revert(function () {})
        }
        err = err ? err.message : err
        result.status = '0x' + result.vm.exception.toString(16)
        callback(err, {
          result: result,
          transactionHash: ethJSUtil.bufferToHex(new Buffer(tx.hash()))
        })
      })
    } catch (e) {
      callback(e, null)
    }
  }
}

function tryTillResponse (txhash, done) {
  executionContext.web3().eth.getTransactionReceipt(txhash, function (err, result) {
    if (!err && !result) {
      // Try again with a bit of delay
      setTimeout(function () { tryTillResponse(txhash, done) }, 500)
    } else {
      done(err, {
        result: result,
        transactionHash: result.transactionHash
      })
    }
  })
}

function run (self, tx, stamp, callback) {
  if (!self.runAsync && Object.keys(self.pendingTxs).length) {
    self.queusTxs.push({ tx, stamp, callback })
  } else {
    self.pendingTxs[stamp] = tx
    self.execute(tx, (error, result) => {
      delete self.pendingTxs[stamp]
      callback(error, result)
      if (self.queusTxs.length) {
        var next = self.queusTxs.pop()
        run(self, next.tx, next.stamp, next.callback)
      }
    })
  }
}

function confirmDialog (tx, gasEstimation, self) {
  var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether')
  var input = yo`<input id='confirmsetting' type="checkbox">`
  var el = yo`

You are creating a transaction on the main network. Click confirm if you are sure to continue.

From: ${tx.from}

To: ${tx.to ? tx.to : '(Contract Creation)'}

Amount: ${amount} Ether

Gas estimation: ${gasEstimation}

Gas limit: ${tx.gas}

Gas price: <input id='gasprice' oninput=${gasPriceChanged} /> Gwei

Max transaction fee:

Data:

${tx.data}

      ${input}
       Do not ask for confirmation again. (the setting will not be persisted for the next page reload)

  `

  var warnMessage = ' Please fix this issue before sending any transaction. '
  function gasPriceChanged () {
    try {
      var gasPrice = el.querySelector('#gasprice').value
      var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei')))
      el.querySelector('#txfee').innerHTML = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether'
      el.gasPriceStatus = true
    } catch (e) {
      el.querySelector('#txfee').innerHTML = warnMessage + e.message
      el.gasPriceStatus = false
    }
  }

  executionContext.web3().eth.getGasPrice((error, gasPrice) => {
    if (error) {
      el.querySelector('#txfee').innerHTML = 'Unable to retrieve the current network gas price.' + warnMessage + error
    } else {
      try {
        el.querySelector('#gasprice').value = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei')
        gasPriceChanged()
      } catch (e) {
        el.querySelector('#txfee').innerHTML = warnMessage + e.message
        el.gasPriceStatus = false
      }
    }
  })
  return el
}

module.exports = TxRunner
<|endoftext|>