<commit_before>goog.provide('ol.test.parser.polyline');

describe('ol.parser.polyline', function() {

  var flatPoints = [38.50000, -120.20000,
                    40.70000, -120.95000,
                    43.25200, -126.45300];

  describe('encodeFlatCoordinates', function() {
    it('returns expected value', function() {
      var encodeFlatCoordinates = ol.parser.polyline.encodeFlatCoordinates;

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(encodeFlatCoordinates(
          flatPoints.slice())).toEqual('_p~iF~ps|U_ulLnnqC_mqNvxq`@');
    });
  });

  describe('decodeFlatCoordinates', function() {
    it('returns expected value', function() {
      var decodeFlatCoordinates = ol.parser.polyline.decodeFlatCoordinates;

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(decodeFlatCoordinates(
          '_p~iF~ps|U_ulLnnqC_mqNvxq`@')).toEqual(flatPoints);
    });
  });

  describe('encodeDeltas', function() {
    it('returns expected value', function() {
      var encodeDeltas = ol.parser.polyline.encodeDeltas;

      expect(encodeDeltas(
          flatPoints.slice(), 2)).toEqual('_p~iF~ps|U_ulLnnqC_mqNvxq`@');
    });
  });

  describe('decodeDeltas', function() {
    it('returns expected value', function() {
      var decodeDeltas = ol.parser.polyline.decodeDeltas;

      expect(decodeDeltas(
          '_p~iF~ps|U_ulLnnqC_mqNvxq`@', 2)).toEqual(flatPoints);
    });
  });

  var floats = [0.00, 0.15, -0.01, -0.16, 0.16, 0.01];
  var smallFloats = [0.00000, 0.00015, -0.00001, -0.00016, 0.00016, 0.00001];
  var encodedFloats = '?]@^_@A';

  describe('encodeFloats', function() {
    it('returns expected value', function() {
      var encodeFloats = ol.parser.polyline.encodeFloats;

      expect(encodeFloats(smallFloats.slice())).toEqual(encodedFloats);
      expect(encodeFloats(smallFloats.slice(), 1e5)).toEqual(encodedFloats);
      expect(encodeFloats(floats.slice(), 1e2)).toEqual(encodedFloats);
    });
  });

  describe('decodeFloats', function() {
    it('returns expected value', function() {
      var decodeFloats = ol.parser.polyline.decodeFloats;

      expect(decodeFloats(encodedFloats)).toEqual(smallFloats);
      expect(decodeFloats(encodedFloats, 1e5)).toEqual(smallFloats);
      expect(decodeFloats(encodedFloats, 1e2)).toEqual(floats);
    });
  });

  var signedIntegers = [0, 15, -1, -16, 16, 1];
  var encodedSignedIntegers = '?]@^_@A';

  describe('encodeSignedIntegers', function() {
    it('returns expected value', function() {
      var encodeSignedIntegers = ol.parser.polyline.encodeSignedIntegers;

      expect(encodeSignedIntegers(
          signedIntegers.slice())).toEqual(encodedSignedIntegers);
    });
  });

  describe('decodeSignedIntegers', function() {
    it('returns expected value', function() {
      var decodeSignedIntegers = ol.parser.polyline.decodeSignedIntegers;

      expect(decodeSignedIntegers(
          encodedSignedIntegers)).toEqual(signedIntegers);
    });
  });

  var unsignedIntegers = [0, 30, 1, 31, 32, 2, 174];
  var encodedUnsignedIntegers = '?]@^_@AmD';

  describe('encodeUnsignedIntegers', function() {
    it('returns expected value', function() {
      var encodeUnsignedIntegers = ol.parser.polyline.encodeUnsignedIntegers;

      expect(encodeUnsignedIntegers(
          unsignedIntegers)).toEqual(encodedUnsignedIntegers);
    });
  });

  describe('decodeUnsignedIntegers', function() {
    it('returns expected value', function() {
      var decodeUnsignedIntegers = ol.parser.polyline.decodeUnsignedIntegers;

      expect(decodeUnsignedIntegers(
          encodedUnsignedIntegers)).toEqual(unsignedIntegers);
    });
  });

  describe('encodeFloat', function() {
    it('returns expected value', function() {
      var encodeFloat = ol.parser.polyline.encodeFloat;

      expect(encodeFloat(0.00000)).toEqual('?');
      expect(encodeFloat(-0.00001)).toEqual('@');
      expect(encodeFloat(0.00001)).toEqual('A');
      expect(encodeFloat(-0.00002)).toEqual('B');
      expect(encodeFloat(0.00002)).toEqual('C');
      expect(encodeFloat(0.00015)).toEqual(']');
      expect(encodeFloat(-0.00016)).toEqual('^');

      expect(encodeFloat(-0.1, 10)).toEqual('@');
      expect(encodeFloat(0.1, 10)).toEqual('A');

      expect(encodeFloat(16 * 32 / 1e5)).toEqual('__@');
      expect(encodeFloat(16 * 32 * 32 / 1e5)).toEqual('___@');

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(encodeFloat(-179.9832104)).toEqual('`~oia@');
    });
  });

  describe('decodeFloat', function() {
    it('returns expected value', function() {
      var decodeFloat = ol.parser.polyline.decodeFloat;

      expect(decodeFloat('?')).toEqual(0.00000);
      expect(decodeFloat('@')).toEqual(-0.00001);
      expect(decodeFloat('A')).toEqual(0.00001);
      expect(decodeFloat('B')).toEqual(-0.00002);
      expect(decodeFloat('C')).toEqual(0.00002);
      expect(decodeFloat(']')).toEqual(0.00015);
      expect(decodeFloat('^')).toEqual(-0.00016);

      expect(decodeFloat('@', 10)).toEqual(-0.1);
      expect(decodeFloat('A', 10)).toEqual(0.1);

      expect(decodeFloat('__@')).toEqual(16 * 32 / 1e5);
      expect(decodeFloat('___@')).toEqual(16 * 32 * 32 / 1e5);

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(decodeFloat('`~oia@')).toEqual(-179.98321);
    });
  });

  describe('encodeSignedInteger', function() {
    it('returns expected value', function() {
      var encodeSignedInteger = ol.parser.polyline.encodeSignedInteger;

      expect(encodeSignedInteger(0)).toEqual('?');
      expect(encodeSignedInteger(-1)).toEqual('@');
      expect(encodeSignedInteger(1)).toEqual('A');
      expect(encodeSignedInteger(-2)).toEqual('B');
      expect(encodeSignedInteger(2)).toEqual('C');
      expect(encodeSignedInteger(15)).toEqual(']');
      expect(encodeSignedInteger(-16)).toEqual('^');

      expect(encodeSignedInteger(16)).toEqual('_@');
      expect(encodeSignedInteger(16 * 32)).toEqual('__@');
      expect(encodeSignedInteger(16 * 32 * 32)).toEqual('___@');
    });
  });

  describe('decodeSignedInteger', function() {
    it('returns expected value', function() {
      var decodeSignedInteger = ol.parser.polyline.decodeSignedInteger;

      expect(decodeSignedInteger('?')).toEqual(0);
      expect(decodeSignedInteger('@')).toEqual(-1);
      expect(decodeSignedInteger('A')).toEqual(1);
      expect(decodeSignedInteger('B')).toEqual(-2);
      expect(decodeSignedInteger('C')).toEqual(2);
      expect(decodeSignedInteger(']')).toEqual(15);
      expect(decodeSignedInteger('^')).toEqual(-16);

      expect(decodeSignedInteger('_@')).toEqual(16);
      expect(decodeSignedInteger('__@')).toEqual(16 * 32);
      expect(decodeSignedInteger('___@')).toEqual(16 * 32 * 32);
    });
  });

  describe('encodeUnsignedInteger', function() {
    it('returns expected value', function() {
      var encodeUnsignedInteger = ol.parser.polyline.encodeUnsignedInteger;

      expect(encodeUnsignedInteger(0)).toEqual('?');
      expect(encodeUnsignedInteger(1)).toEqual('@');
      expect(encodeUnsignedInteger(2)).toEqual('A');
      expect(encodeUnsignedInteger(30)).toEqual(']');
      expect(encodeUnsignedInteger(31)).toEqual('^');
      expect(encodeUnsignedInteger(32)).toEqual('_@');

      expect(encodeUnsignedInteger(32 * 32)).toEqual('__@');
      expect(encodeUnsignedInteger(5 * 32 * 32)).toEqual('__D');
      expect(encodeUnsignedInteger(32 * 32 * 32)).toEqual('___@');

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(encodeUnsignedInteger(174)).toEqual('mD');
    });
  });

  describe('decodeUnsignedInteger', function() {
    it('returns expected value', function() {
      var decodeUnsignedInteger = ol.parser.polyline.decodeUnsignedInteger;

      expect(decodeUnsignedInteger('?')).toEqual(0);
      expect(decodeUnsignedInteger('@')).toEqual(1);
      expect(decodeUnsignedInteger('A')).toEqual(2);
      expect(decodeUnsignedInteger(']')).toEqual(30);
      expect(decodeUnsignedInteger('^')).toEqual(31);
      expect(decodeUnsignedInteger('_@')).toEqual(32);

      expect(decodeUnsignedInteger('__@')).toEqual(32 * 32);
      expect(decodeUnsignedInteger('__D')).toEqual(5 * 32 * 32);
      expect(decodeUnsignedInteger('___@')).toEqual(32 * 32 * 32);

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(decodeUnsignedInteger('mD')).toEqual(174);
    });
  });
});

goog.require('ol.parser.polyline');
<commit_msg>test/polyline: Reset testing data before each test<commit_after>goog.provide('ol.test.parser.polyline');

describe('ol.parser.polyline', function() {

  var flatPoints;
  var floats, smallFloats, encodedFloats;
  var signedIntegers, encodedSignedIntegers;
  var unsignedIntegers, encodedUnsignedIntegers;

  // Reset testing data
  beforeEach(function() {
    flatPoints = [38.50000, -120.20000,
                  40.70000, -120.95000,
                  43.25200, -126.45300];

    floats = [0.00, 0.15, -0.01, -0.16, 0.16, 0.01];
    smallFloats = [0.00000, 0.00015, -0.00001, -0.00016, 0.00016, 0.00001];
    encodedFloats = '?]@^_@A';

    signedIntegers = [0, 15, -1, -16, 16, 1];
    encodedSignedIntegers = '?]@^_@A';

    unsignedIntegers = [0, 30, 1, 31, 32, 2, 174];
    encodedUnsignedIntegers = '?]@^_@AmD';
  })

  describe('encodeFlatCoordinates', function() {
    it('returns expected value', function() {
      var encodeFlatCoordinates = ol.parser.polyline.encodeFlatCoordinates;

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(encodeFlatCoordinates(
          flatPoints).toEqual('_p~iF~ps|U_ulLnnqC_mqNvxq`@');
    });
  });

  describe('decodeFlatCoordinates', function() {
    it('returns expected value', function() {
      var decodeFlatCoordinates = ol.parser.polyline.decodeFlatCoordinates;

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(decodeFlatCoordinates(
          '_p~iF~ps|U_ulLnnqC_mqNvxq`@')).toEqual(flatPoints);
    });
  });

  describe('encodeDeltas', function() {
    it('returns expected value', function() {
      var encodeDeltas = ol.parser.polyline.encodeDeltas;

      expect(encodeDeltas(
          flatPoints, 2)).toEqual('_p~iF~ps|U_ulLnnqC_mqNvxq`@');
    });
  });

  describe('decodeDeltas', function() {
    it('returns expected value', function() {
      var decodeDeltas = ol.parser.polyline.decodeDeltas;

      expect(decodeDeltas(
          '_p~iF~ps|U_ulLnnqC_mqNvxq`@', 2)).toEqual(flatPoints);
    });
  });

  describe('encodeFloats', function() {
    it('returns expected value', function() {
      var encodeFloats = ol.parser.polyline.encodeFloats;

      expect(encodeFloats(smallFloats).toEqual(encodedFloats);
      expect(encodeFloats(smallFloats, 1e5)).toEqual(encodedFloats);
      expect(encodeFloats(floats, 1e2)).toEqual(encodedFloats);
    });
  });

  describe('decodeFloats', function() {
    it('returns expected value', function() {
      var decodeFloats = ol.parser.polyline.decodeFloats;

      expect(decodeFloats(encodedFloats)).toEqual(smallFloats);
      expect(decodeFloats(encodedFloats, 1e5)).toEqual(smallFloats);
      expect(decodeFloats(encodedFloats, 1e2)).toEqual(floats);
    });
  });

  describe('encodeSignedIntegers', function() {
    it('returns expected value', function() {
      var encodeSignedIntegers = ol.parser.polyline.encodeSignedIntegers;

      expect(encodeSignedIntegers(
          signedIntegers).toEqual(encodedSignedIntegers);
    });
  });

  describe('decodeSignedIntegers', function() {
    it('returns expected value', function() {
      var decodeSignedIntegers = ol.parser.polyline.decodeSignedIntegers;

      expect(decodeSignedIntegers(
          encodedSignedIntegers)).toEqual(signedIntegers);
    });
  });

 describe('encodeUnsignedIntegers', function() {
    it('returns expected value', function() {
      var encodeUnsignedIntegers = ol.parser.polyline.encodeUnsignedIntegers;

      expect(encodeUnsignedIntegers(
          unsignedIntegers)).toEqual(encodedUnsignedIntegers);
    });
  });

  describe('decodeUnsignedIntegers', function() {
    it('returns expected value', function() {
      var decodeUnsignedIntegers = ol.parser.polyline.decodeUnsignedIntegers;

      expect(decodeUnsignedIntegers(
          encodedUnsignedIntegers)).toEqual(unsignedIntegers);
    });
  });

  describe('encodeFloat', function() {
    it('returns expected value', function() {
      var encodeFloat = ol.parser.polyline.encodeFloat;

      expect(encodeFloat(0.00000)).toEqual('?');
      expect(encodeFloat(-0.00001)).toEqual('@');
      expect(encodeFloat(0.00001)).toEqual('A');
      expect(encodeFloat(-0.00002)).toEqual('B');
      expect(encodeFloat(0.00002)).toEqual('C');
      expect(encodeFloat(0.00015)).toEqual(']');
      expect(encodeFloat(-0.00016)).toEqual('^');

      expect(encodeFloat(-0.1, 10)).toEqual('@');
      expect(encodeFloat(0.1, 10)).toEqual('A');

      expect(encodeFloat(16 * 32 / 1e5)).toEqual('__@');
      expect(encodeFloat(16 * 32 * 32 / 1e5)).toEqual('___@');

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(encodeFloat(-179.9832104)).toEqual('`~oia@');
    });
  });

  describe('decodeFloat', function() {
    it('returns expected value', function() {
      var decodeFloat = ol.parser.polyline.decodeFloat;

      expect(decodeFloat('?')).toEqual(0.00000);
      expect(decodeFloat('@')).toEqual(-0.00001);
      expect(decodeFloat('A')).toEqual(0.00001);
      expect(decodeFloat('B')).toEqual(-0.00002);
      expect(decodeFloat('C')).toEqual(0.00002);
      expect(decodeFloat(']')).toEqual(0.00015);
      expect(decodeFloat('^')).toEqual(-0.00016);

      expect(decodeFloat('@', 10)).toEqual(-0.1);
      expect(decodeFloat('A', 10)).toEqual(0.1);

      expect(decodeFloat('__@')).toEqual(16 * 32 / 1e5);
      expect(decodeFloat('___@')).toEqual(16 * 32 * 32 / 1e5);

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(decodeFloat('`~oia@')).toEqual(-179.98321);
    });
  });

  describe('encodeSignedInteger', function() {
    it('returns expected value', function() {
      var encodeSignedInteger = ol.parser.polyline.encodeSignedInteger;

      expect(encodeSignedInteger(0)).toEqual('?');
      expect(encodeSignedInteger(-1)).toEqual('@');
      expect(encodeSignedInteger(1)).toEqual('A');
      expect(encodeSignedInteger(-2)).toEqual('B');
      expect(encodeSignedInteger(2)).toEqual('C');
      expect(encodeSignedInteger(15)).toEqual(']');
      expect(encodeSignedInteger(-16)).toEqual('^');

      expect(encodeSignedInteger(16)).toEqual('_@');
      expect(encodeSignedInteger(16 * 32)).toEqual('__@');
      expect(encodeSignedInteger(16 * 32 * 32)).toEqual('___@');
    });
  });

  describe('decodeSignedInteger', function() {
    it('returns expected value', function() {
      var decodeSignedInteger = ol.parser.polyline.decodeSignedInteger;

      expect(decodeSignedInteger('?')).toEqual(0);
      expect(decodeSignedInteger('@')).toEqual(-1);
      expect(decodeSignedInteger('A')).toEqual(1);
      expect(decodeSignedInteger('B')).toEqual(-2);
      expect(decodeSignedInteger('C')).toEqual(2);
      expect(decodeSignedInteger(']')).toEqual(15);
      expect(decodeSignedInteger('^')).toEqual(-16);

      expect(decodeSignedInteger('_@')).toEqual(16);
      expect(decodeSignedInteger('__@')).toEqual(16 * 32);
      expect(decodeSignedInteger('___@')).toEqual(16 * 32 * 32);
    });
  });

  describe('encodeUnsignedInteger', function() {
    it('returns expected value', function() {
      var encodeUnsignedInteger = ol.parser.polyline.encodeUnsignedInteger;

      expect(encodeUnsignedInteger(0)).toEqual('?');
      expect(encodeUnsignedInteger(1)).toEqual('@');
      expect(encodeUnsignedInteger(2)).toEqual('A');
      expect(encodeUnsignedInteger(30)).toEqual(']');
      expect(encodeUnsignedInteger(31)).toEqual('^');
      expect(encodeUnsignedInteger(32)).toEqual('_@');

      expect(encodeUnsignedInteger(32 * 32)).toEqual('__@');
      expect(encodeUnsignedInteger(5 * 32 * 32)).toEqual('__D');
      expect(encodeUnsignedInteger(32 * 32 * 32)).toEqual('___@');

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(encodeUnsignedInteger(174)).toEqual('mD');
    });
  });

  describe('decodeUnsignedInteger', function() {
    it('returns expected value', function() {
      var decodeUnsignedInteger = ol.parser.polyline.decodeUnsignedInteger;

      expect(decodeUnsignedInteger('?')).toEqual(0);
      expect(decodeUnsignedInteger('@')).toEqual(1);
      expect(decodeUnsignedInteger('A')).toEqual(2);
      expect(decodeUnsignedInteger(']')).toEqual(30);
      expect(decodeUnsignedInteger('^')).toEqual(31);
      expect(decodeUnsignedInteger('_@')).toEqual(32);

      expect(decodeUnsignedInteger('__@')).toEqual(32 * 32);
      expect(decodeUnsignedInteger('__D')).toEqual(5 * 32 * 32);
      expect(decodeUnsignedInteger('___@')).toEqual(32 * 32 * 32);

      // from the "Encoded Polyline Algorithm Format" page at Google
      expect(decodeUnsignedInteger('mD')).toEqual(174);
    });
  });
});

goog.require('ol.parser.polyline');
<|endoftext|>


<commit_before>import {getValues, batchGetValues, batchUpdateValues} from './sheets.js'
import {formatAsHyperlink, formatDate} from './utils'
import {sheets} from './constants'

export async function storeOrder(order, items) {
  const sheet = order.isCompany ? sheets.companyOrders : sheets.personalOrders

  const newRowIndex = await getNextEmptyRowIndex(sheet)

  const user = order.user
  const userJiraId = await getUserJiraId(user.id, user.name)

  const date = new Date()

  const itemToSheetData = order.isCompany
    ? mapCompanyOrderItemToSheetData
    : mapPersonalOrderItemToSheetData

  const data = items.map((item, index) => {
    return itemToSheetData(
      item,
      newRowIndex + index,
      order,
      sheet.name,
      userJiraId,
      date,
    )
  })

  await batchUpdateValues(sheet, {data, valueInputOption: 'USER_ENTERED'})
}

async function getNextEmptyRowIndex(sheet) {
  const values = await getValues(sheet.idRange)
  let firstEmpty = values.findIndex((v) => v.every((c) => !c))

  if (firstEmpty === -1) {
    firstEmpty = values.length
  }

  return firstEmpty + sheet.rowOffset + 1
}

async function getUserJiraId(slackId, name) {
  const sheetData = await batchGetValues([
    sheets.settings.jiraIdRange,
    sheets.settings.slackIdRange,
  ])
  const values = sheetData.map((vr) => vr.values)

  const jiraIds = values[0]
  const slackIds = values[1]
  const rowIndex = slackIds.findIndex((i) => {
    return i.length === 1 && i[0] === slackId
  })

  if (rowIndex === -1) {
    return `${name}:${slackId}`
  }

  return jiraIds[rowIndex][0]
}

function mapPersonalOrderItemToSheetData(
  item,
  rowIndex,
  order,
  sheetName,
  userJiraId,
  date,
) {
  return [
    {
      range: `${sheetName}!A${rowIndex}:L${rowIndex}`,
      values: [
        [
          item.dbId,
          formatAsHyperlink(item.url, item.name),
          item.price,
          null,
          order.office,
          'requested',
          null,
          null,
          null,
          userJiraId,
          null,
          formatDate(date),
        ],
      ],
    },
  ]
}

function mapCompanyOrderItemToSheetData(
  item,
  rowIndex,
  order,
  sheetName,
  userJiraId,
  date,
) {
  return [
    {
      range: `${sheetName}!A${rowIndex}:N${rowIndex}`,
      values: [
        [
          item.dbId,
          formatAsHyperlink(item.url, item.name),
          item.price,
          null,
          null,
          order.office,
          order.reason,
          'requested',
          null,
          null,
          null,
          userJiraId,
          null,
          formatDate(date),
        ],
      ],
    },
  ]
}
<commit_msg>Store products with quantity > 1 as separate rows<commit_after>import {getValues, batchGetValues, batchUpdateValues} from './sheets.js'
import {formatAsHyperlink, formatDate} from './utils'
import {sheets} from './constants'

export async function storeOrder(order, items) {
  const sheet = order.isCompany ? sheets.companyOrders : sheets.personalOrders

  const newRowIndex = await getNextEmptyRowIndex(sheet)

  const user = order.user
  const userJiraId = await getUserJiraId(user.id, user.name)

  const date = new Date()

  const itemToSheetData = order.isCompany
    ? mapCompanyOrderItemToSheetData
    : mapPersonalOrderItemToSheetData

  const data = []

  for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
    const item = items[itemIndex]

    for (let i = 0; i < item.count; i++) {
      data.push(itemToSheetData(
        item,
        newRowIndex + itemIndex + i,
        order,
        sheet.name,
        userJiraId,
        date,
      ))
    }
  }

  await batchUpdateValues(sheet, {data, valueInputOption: 'USER_ENTERED'})
}

async function getNextEmptyRowIndex(sheet) {
  const values = await getValues(sheet.idRange)
  let firstEmpty = values.findIndex((v) => v.every((c) => !c))

  if (firstEmpty === -1) {
    firstEmpty = values.length
  }

  return firstEmpty + sheet.rowOffset + 1
}

async function getUserJiraId(slackId, name) {
  const sheetData = await batchGetValues([
    sheets.settings.jiraIdRange,
    sheets.settings.slackIdRange,
  ])
  const values = sheetData.map((vr) => vr.values)

  const jiraIds = values[0]
  const slackIds = values[1]
  const rowIndex = slackIds.findIndex((i) => {
    return i.length === 1 && i[0] === slackId
  })

  if (rowIndex === -1) {
    return `${name}:${slackId}`
  }

  return jiraIds[rowIndex][0]
}

function mapPersonalOrderItemToSheetData(
  item,
  rowIndex,
  order,
  sheetName,
  userJiraId,
  date,
) {
  return [
    {
      range: `${sheetName}!A${rowIndex}:L${rowIndex}`,
      values: [
        [
          item.dbId,
          formatAsHyperlink(item.url, item.name),
          item.price,
          null,
          order.office,
          'requested',
          null,
          null,
          null,
          userJiraId,
          null,
          formatDate(date),
        ],
      ],
    },
  ]
}

function mapCompanyOrderItemToSheetData(
  item,
  rowIndex,
  order,
  sheetName,
  userJiraId,
  date,
) {
  return [
    {
      range: `${sheetName}!A${rowIndex}:N${rowIndex}`,
      values: [
        [
          item.dbId,
          formatAsHyperlink(item.url, item.name),
          item.price,
          null,
          null,
          order.office,
          order.reason,
          'requested',
          null,
          null,
          null,
          userJiraId,
          null,
          formatDate(date),
        ],
      ],
    },
  ]
}
<|endoftext|>


<commit_before>'use strict';

var bind = require('./helpers/bind');

// utils is a library of generic helper functions non-specific to axios

var toString = Object.prototype.toString;

// eslint-disable-next-line func-names
var kindOf = (function(cache) {
  // eslint-disable-next-line func-names
  return function(thing) {
    var str = toString.call(thing);
    return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
  };
})(Object.create(null));

function kindOfTest(type) {
  type = type.toLowerCase();
  return function isKindOf(thing) {
    return kindOf(thing) === type;
  };
}

/**
 * Determine if a value is an Array
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an Array, otherwise false
 */
function isArray(val) {
  return Array.isArray(val);
}

/**
 * Determine if a value is undefined
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if the value is undefined, otherwise false
 */
function isUndefined(val) {
  return typeof val === 'undefined';
}

/**
 * Determine if a value is a Buffer
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Buffer, otherwise false
 */
function isBuffer(val) {
  return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
    && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
}

/**
 * Determine if a value is an ArrayBuffer
 *
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an ArrayBuffer, otherwise false
 */
var isArrayBuffer = kindOfTest('ArrayBuffer');

/**
 * Determine if a value is a view on an ArrayBuffer
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
 */
function isArrayBufferView(val) {
  var result;
  if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
    result = ArrayBuffer.isView(val);
  } else {
    result = (val) && (val.buffer) && (isArrayBuffer(val.buffer));
  }
  return result;
}

/**
 * Determine if a value is a String
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a String, otherwise false
 */
function isString(val) {
  return typeof val === 'string';
}

/**
 * Determine if a value is a Number
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Number, otherwise false
 */
function isNumber(val) {
  return typeof val === 'number';
}

/**
 * Determine if a value is an Object
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an Object, otherwise false
 */
function isObject(val) {
  return val !== null && typeof val === 'object';
}

/**
 * Determine if a value is a plain Object
 *
 * @param {Object} val The value to test
 * @return {boolean} True if value is a plain Object, otherwise false
 */
function isPlainObject(val) {
  if (kindOf(val) !== 'object') {
    return false;
  }

  var prototype = Object.getPrototypeOf(val);
  return prototype === null || prototype === Object.prototype;
}

/**
 * Determine if a value is a Date
 *
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Date, otherwise false
 */
var isDate = kindOfTest('Date');

/**
 * Determine if a value is a File
 *
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a File, otherwise false
 */
var isFile = kindOfTest('File');

/**
 * Determine if a value is a Blob
 *
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Blob, otherwise false
 */
var isBlob = kindOfTest('Blob');

/**
 * Determine if a value is a FileList
 *
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a File, otherwise false
 */
var isFileList = kindOfTest('FileList');

/**
 * Determine if a value is a Function
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Function, otherwise false
 */
function isFunction(val) {
  return toString.call(val) === '[object Function]';
}

/**
 * Determine if a value is a Stream
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Stream, otherwise false
 */
function isStream(val) {
  return isObject(val) && isFunction(val.pipe);
}

/**
 * Determine if a value is a FormData
 *
 * @param {Object} thing The value to test
 * @returns {boolean} True if value is an FormData, otherwise false
 */
function isFormData(thing) {
  var pattern = '[object FormData]';
  return thing && (
    (typeof FormData === 'function' && thing instanceof FormData) ||
    toString.call(thing) === pattern ||
    (isFunction(thing.toString) && thing.toString() === pattern)
  );
}

/**
 * Determine if a value is a URLSearchParams object
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a URLSearchParams object, otherwise false
 */
var isURLSearchParams = kindOfTest('URLSearchParams');

/**
 * Trim excess whitespace off the beginning and end of a string
 *
 * @param {String} str The String to trim
 * @returns {String} The String freed of excess whitespace
 */
function trim(str) {
  return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
}

/**
 * Determine if we're running in a standard browser environment
 *
 * This allows axios to run in a web worker, and react-native.
 * Both environments support XMLHttpRequest, but not fully standard globals.
 *
 * web workers:
 *  typeof window -> undefined
 *  typeof document -> undefined
 *
 * react-native:
 *  navigator.product -> 'ReactNative'
 * nativescript
 *  navigator.product -> 'NativeScript' or 'NS'
 */
function isStandardBrowserEnv() {
  if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||
                                           navigator.product === 'NativeScript' ||
                                           navigator.product === 'NS')) {
    return false;
  }
  return (
    typeof window !== 'undefined' &&
    typeof document !== 'undefined'
  );
}

/**
 * Iterate over an Array or an Object invoking a function for each item.
 *
 * If `obj` is an Array callback will be called passing
 * the value, index, and complete array for each item.
 *
 * If 'obj' is an Object callback will be called passing
 * the value, key, and complete object for each property.
 *
 * @param {Object|Array} obj The object to iterate
 * @param {Function} fn The callback to invoke for each item
 */
function forEach(obj, fn) {
  // Don't bother if no value provided
  if (obj === null || typeof obj === 'undefined') {
    return;
  }

  // Force an array if not already something iterable
  if (typeof obj !== 'object') {
    /*eslint no-param-reassign:0*/
    obj = [obj];
  }

  if (isArray(obj)) {
    // Iterate over array values
    for (var i = 0, l = obj.length; i < l; i++) {
      fn.call(null, obj[i], i, obj);
    }
  } else {
    // Iterate over object keys
    for (var key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        fn.call(null, obj[key], key, obj);
      }
    }
  }
}

/**
 * Accepts varargs expecting each argument to be an object, then
 * immutably merges the properties of each object and returns result.
 *
 * When multiple objects contain the same key the later object in
 * the arguments list will take precedence.
 *
 * Example:
 *
 * ```js
 * var result = merge({foo: 123}, {foo: 456});
 * console.log(result.foo); // outputs 456
 * ```
 *
 * @param {Object} obj1 Object to merge
 * @returns {Object} Result of all merge properties
 */
function merge(/* obj1, obj2, obj3, ... */) {
  var result = {};
  function assignValue(val, key) {
    if (isPlainObject(result[key]) && isPlainObject(val)) {
      result[key] = merge(result[key], val);
    } else if (isPlainObject(val)) {
      result[key] = merge({}, val);
    } else if (isArray(val)) {
      result[key] = val.slice();
    } else {
      result[key] = val;
    }
  }

  for (var i = 0, l = arguments.length; i < l; i++) {
    forEach(arguments[i], assignValue);
  }
  return result;
}

/**
 * Extends object a by mutably adding to it the properties of object b.
 *
 * @param {Object} a The object to be extended
 * @param {Object} b The object to copy properties from
 * @param {Object} thisArg The object to bind function to
 * @return {Object} The resulting value of object a
 */
function extend(a, b, thisArg) {
  forEach(b, function assignValue(val, key) {
    if (thisArg && typeof val === 'function') {
      a[key] = bind(val, thisArg);
    } else {
      a[key] = val;
    }
  });
  return a;
}

/**
 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
 *
 * @param {string} content with BOM
 * @return {string} content value without BOM
 */
function stripBOM(content) {
  if (content.charCodeAt(0) === 0xFEFF) {
    content = content.slice(1);
  }
  return content;
}

/**
 * Inherit the prototype methods from one constructor into another
 * @param {function} constructor
 * @param {function} superConstructor
 * @param {object} [props]
 * @param {object} [descriptors]
 */

function inherits(constructor, superConstructor, props, descriptors) {
  constructor.prototype = Object.create(superConstructor.prototype, descriptors);
  constructor.prototype.constructor = constructor;
  props && Object.assign(constructor.prototype, props);
}

/**
 * Resolve object with deep prototype chain to a flat object
 * @param {Object} sourceObj source object
 * @param {Object} [destObj]
 * @param {Function} [filter]
 * @returns {Object}
 */

function toFlatObject(sourceObj, destObj, filter) {
  var props;
  var i;
  var prop;
  var merged = {};

  destObj = destObj || {};

  do {
    props = Object.getOwnPropertyNames(sourceObj);
    i = props.length;
    while (i-- > 0) {
      prop = props[i];
      if (!merged[prop]) {
        destObj[prop] = sourceObj[prop];
        merged[prop] = true;
      }
    }
    sourceObj = Object.getPrototypeOf(sourceObj);
  } while (sourceObj && (!filter || filter(sourceObj, destObj)) && sourceObj !== Object.prototype);

  return destObj;
}

/*
 * determines whether a string ends with the characters of a specified string
 * @param {String} str
 * @param {String} searchString
 * @param {Number} [position= 0]
 * @returns {boolean}
 */
function endsWith(str, searchString, position) {
  str = String(str);
  if (position === undefined || position > str.length) {
    position = str.length;
  }
  position -= searchString.length;
  var lastIndex = str.indexOf(searchString, position);
  return lastIndex !== -1 && lastIndex === position;
}

/**
 * Returns new array from array like object
 * @param {*} [thing]
 * @returns {Array}
 */
function toArray(thing) {
  if (!thing) return null;
  var i = thing.length;
  if (isUndefined(i)) return null;
  var arr = new Array(i);
  while (i-- > 0) {
    arr[i] = thing[i];
  }
  return arr;
}

// eslint-disable-next-line func-names
var isTypedArray = (function(TypedArray) {
  // eslint-disable-next-line func-names
  return function(thing) {
    return TypedArray && thing instanceof TypedArray;
  };
})(typeof Uint8Array !== 'undefined' && Object.getPrototypeOf(Uint8Array));

module.exports = {
  isArray: isArray,
  isArrayBuffer: isArrayBuffer,
  isBuffer: isBuffer,
  isFormData: isFormData,
  isArrayBufferView: isArrayBufferView,
  isString: isString,
  isNumber: isNumber,
  isObject: isObject,
  isPlainObject: isPlainObject,
  isUndefined: isUndefined,
  isDate: isDate,
  isFile: isFile,
  isBlob: isBlob,
  isFunction: isFunction,
  isStream: isStream,
  isURLSearchParams: isURLSearchParams,
  isStandardBrowserEnv: isStandardBrowserEnv,
  forEach: forEach,
  merge: merge,
  extend: extend,
  trim: trim,
  stripBOM: stripBOM,
  inherits: inherits,
  toFlatObject: toFlatObject,
  kindOf: kindOf,
  kindOfTest: kindOfTest,
  endsWith: endsWith,
  toArray: toArray,
  isTypedArray: isTypedArray,
  isFileList: isFileList
};
<commit_msg>Updating trim() polyfill. According to mdn: https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md (#4072)<commit_after>'use strict';

var bind = require('./helpers/bind');

// utils is a library of generic helper functions non-specific to axios

var toString = Object.prototype.toString;

// eslint-disable-next-line func-names
var kindOf = (function(cache) {
  // eslint-disable-next-line func-names
  return function(thing) {
    var str = toString.call(thing);
    return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
  };
})(Object.create(null));

function kindOfTest(type) {
  type = type.toLowerCase();
  return function isKindOf(thing) {
    return kindOf(thing) === type;
  };
}

/**
 * Determine if a value is an Array
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an Array, otherwise false
 */
function isArray(val) {
  return Array.isArray(val);
}

/**
 * Determine if a value is undefined
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if the value is undefined, otherwise false
 */
function isUndefined(val) {
  return typeof val === 'undefined';
}

/**
 * Determine if a value is a Buffer
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Buffer, otherwise false
 */
function isBuffer(val) {
  return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
    && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
}

/**
 * Determine if a value is an ArrayBuffer
 *
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an ArrayBuffer, otherwise false
 */
var isArrayBuffer = kindOfTest('ArrayBuffer');

/**
 * Determine if a value is a view on an ArrayBuffer
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
 */
function isArrayBufferView(val) {
  var result;
  if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
    result = ArrayBuffer.isView(val);
  } else {
    result = (val) && (val.buffer) && (isArrayBuffer(val.buffer));
  }
  return result;
}

/**
 * Determine if a value is a String
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a String, otherwise false
 */
function isString(val) {
  return typeof val === 'string';
}

/**
 * Determine if a value is a Number
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Number, otherwise false
 */
function isNumber(val) {
  return typeof val === 'number';
}

/**
 * Determine if a value is an Object
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an Object, otherwise false
 */
function isObject(val) {
  return val !== null && typeof val === 'object';
}

/**
 * Determine if a value is a plain Object
 *
 * @param {Object} val The value to test
 * @return {boolean} True if value is a plain Object, otherwise false
 */
function isPlainObject(val) {
  if (kindOf(val) !== 'object') {
    return false;
  }

  var prototype = Object.getPrototypeOf(val);
  return prototype === null || prototype === Object.prototype;
}

/**
 * Determine if a value is a Date
 *
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Date, otherwise false
 */
var isDate = kindOfTest('Date');

/**
 * Determine if a value is a File
 *
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a File, otherwise false
 */
var isFile = kindOfTest('File');

/**
 * Determine if a value is a Blob
 *
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Blob, otherwise false
 */
var isBlob = kindOfTest('Blob');

/**
 * Determine if a value is a FileList
 *
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a File, otherwise false
 */
var isFileList = kindOfTest('FileList');

/**
 * Determine if a value is a Function
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Function, otherwise false
 */
function isFunction(val) {
  return toString.call(val) === '[object Function]';
}

/**
 * Determine if a value is a Stream
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Stream, otherwise false
 */
function isStream(val) {
  return isObject(val) && isFunction(val.pipe);
}

/**
 * Determine if a value is a FormData
 *
 * @param {Object} thing The value to test
 * @returns {boolean} True if value is an FormData, otherwise false
 */
function isFormData(thing) {
  var pattern = '[object FormData]';
  return thing && (
    (typeof FormData === 'function' && thing instanceof FormData) ||
    toString.call(thing) === pattern ||
    (isFunction(thing.toString) && thing.toString() === pattern)
  );
}

/**
 * Determine if a value is a URLSearchParams object
 * @function
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a URLSearchParams object, otherwise false
 */
var isURLSearchParams = kindOfTest('URLSearchParams');

/**
 * Trim excess whitespace off the beginning and end of a string
 *
 * @param {String} str The String to trim
 * @returns {String} The String freed of excess whitespace
 */
function trim(str) {
  return str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}

/**
 * Determine if we're running in a standard browser environment
 *
 * This allows axios to run in a web worker, and react-native.
 * Both environments support XMLHttpRequest, but not fully standard globals.
 *
 * web workers:
 *  typeof window -> undefined
 *  typeof document -> undefined
 *
 * react-native:
 *  navigator.product -> 'ReactNative'
 * nativescript
 *  navigator.product -> 'NativeScript' or 'NS'
 */
function isStandardBrowserEnv() {
  if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||
                                           navigator.product === 'NativeScript' ||
                                           navigator.product === 'NS')) {
    return false;
  }
  return (
    typeof window !== 'undefined' &&
    typeof document !== 'undefined'
  );
}

/**
 * Iterate over an Array or an Object invoking a function for each item.
 *
 * If `obj` is an Array callback will be called passing
 * the value, index, and complete array for each item.
 *
 * If 'obj' is an Object callback will be called passing
 * the value, key, and complete object for each property.
 *
 * @param {Object|Array} obj The object to iterate
 * @param {Function} fn The callback to invoke for each item
 */
function forEach(obj, fn) {
  // Don't bother if no value provided
  if (obj === null || typeof obj === 'undefined') {
    return;
  }

  // Force an array if not already something iterable
  if (typeof obj !== 'object') {
    /*eslint no-param-reassign:0*/
    obj = [obj];
  }

  if (isArray(obj)) {
    // Iterate over array values
    for (var i = 0, l = obj.length; i < l; i++) {
      fn.call(null, obj[i], i, obj);
    }
  } else {
    // Iterate over object keys
    for (var key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        fn.call(null, obj[key], key, obj);
      }
    }
  }
}

/**
 * Accepts varargs expecting each argument to be an object, then
 * immutably merges the properties of each object and returns result.
 *
 * When multiple objects contain the same key the later object in
 * the arguments list will take precedence.
 *
 * Example:
 *
 * ```js
 * var result = merge({foo: 123}, {foo: 456});
 * console.log(result.foo); // outputs 456
 * ```
 *
 * @param {Object} obj1 Object to merge
 * @returns {Object} Result of all merge properties
 */
function merge(/* obj1, obj2, obj3, ... */) {
  var result = {};
  function assignValue(val, key) {
    if (isPlainObject(result[key]) && isPlainObject(val)) {
      result[key] = merge(result[key], val);
    } else if (isPlainObject(val)) {
      result[key] = merge({}, val);
    } else if (isArray(val)) {
      result[key] = val.slice();
    } else {
      result[key] = val;
    }
  }

  for (var i = 0, l = arguments.length; i < l; i++) {
    forEach(arguments[i], assignValue);
  }
  return result;
}

/**
 * Extends object a by mutably adding to it the properties of object b.
 *
 * @param {Object} a The object to be extended
 * @param {Object} b The object to copy properties from
 * @param {Object} thisArg The object to bind function to
 * @return {Object} The resulting value of object a
 */
function extend(a, b, thisArg) {
  forEach(b, function assignValue(val, key) {
    if (thisArg && typeof val === 'function') {
      a[key] = bind(val, thisArg);
    } else {
      a[key] = val;
    }
  });
  return a;
}

/**
 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
 *
 * @param {string} content with BOM
 * @return {string} content value without BOM
 */
function stripBOM(content) {
  if (content.charCodeAt(0) === 0xFEFF) {
    content = content.slice(1);
  }
  return content;
}

/**
 * Inherit the prototype methods from one constructor into another
 * @param {function} constructor
 * @param {function} superConstructor
 * @param {object} [props]
 * @param {object} [descriptors]
 */

function inherits(constructor, superConstructor, props, descriptors) {
  constructor.prototype = Object.create(superConstructor.prototype, descriptors);
  constructor.prototype.constructor = constructor;
  props && Object.assign(constructor.prototype, props);
}

/**
 * Resolve object with deep prototype chain to a flat object
 * @param {Object} sourceObj source object
 * @param {Object} [destObj]
 * @param {Function} [filter]
 * @returns {Object}
 */

function toFlatObject(sourceObj, destObj, filter) {
  var props;
  var i;
  var prop;
  var merged = {};

  destObj = destObj || {};

  do {
    props = Object.getOwnPropertyNames(sourceObj);
    i = props.length;
    while (i-- > 0) {
      prop = props[i];
      if (!merged[prop]) {
        destObj[prop] = sourceObj[prop];
        merged[prop] = true;
      }
    }
    sourceObj = Object.getPrototypeOf(sourceObj);
  } while (sourceObj && (!filter || filter(sourceObj, destObj)) && sourceObj !== Object.prototype);

  return destObj;
}

/*
 * determines whether a string ends with the characters of a specified string
 * @param {String} str
 * @param {String} searchString
 * @param {Number} [position= 0]
 * @returns {boolean}
 */
function endsWith(str, searchString, position) {
  str = String(str);
  if (position === undefined || position > str.length) {
    position = str.length;
  }
  position -= searchString.length;
  var lastIndex = str.indexOf(searchString, position);
  return lastIndex !== -1 && lastIndex === position;
}

/**
 * Returns new array from array like object
 * @param {*} [thing]
 * @returns {Array}
 */
function toArray(thing) {
  if (!thing) return null;
  var i = thing.length;
  if (isUndefined(i)) return null;
  var arr = new Array(i);
  while (i-- > 0) {
    arr[i] = thing[i];
  }
  return arr;
}

// eslint-disable-next-line func-names
var isTypedArray = (function(TypedArray) {
  // eslint-disable-next-line func-names
  return function(thing) {
    return TypedArray && thing instanceof TypedArray;
  };
})(typeof Uint8Array !== 'undefined' && Object.getPrototypeOf(Uint8Array));

module.exports = {
  isArray: isArray,
  isArrayBuffer: isArrayBuffer,
  isBuffer: isBuffer,
  isFormData: isFormData,
  isArrayBufferView: isArrayBufferView,
  isString: isString,
  isNumber: isNumber,
  isObject: isObject,
  isPlainObject: isPlainObject,
  isUndefined: isUndefined,
  isDate: isDate,
  isFile: isFile,
  isBlob: isBlob,
  isFunction: isFunction,
  isStream: isStream,
  isURLSearchParams: isURLSearchParams,
  isStandardBrowserEnv: isStandardBrowserEnv,
  forEach: forEach,
  merge: merge,
  extend: extend,
  trim: trim,
  stripBOM: stripBOM,
  inherits: inherits,
  toFlatObject: toFlatObject,
  kindOf: kindOf,
  kindOfTest: kindOfTest,
  endsWith: endsWith,
  toArray: toArray,
  isTypedArray: isTypedArray,
  isFileList: isFileList
};
<|endoftext|>


<commit_before>"use strict";

function type(V) {
    if (V === null) {
        return "Null";
    }
    switch (typeof V) {
        case "undefined":
            return "Undefined";
        case "boolean":
            return "Boolean";
        case "number":
            return "Number";
        case "string":
            return "String";
        case "symbol":
            return "Symbol";
        case "object":
            // Falls through
        case "function":
            // Falls through
        default:
            // Per ES spec, typeof returns an implemention-defined value that is not any of the existing ones for
            // uncallable non-standard exotic objects. Yet Type() which the Web IDL spec depends on returns Object for
            // such cases. So treat the default case as an object.
            return "Object";
    }
}

function sign(x) {
    return x < 0 ? -1 : 1;
}

function evenRound(x) {
    // Round x to the nearest integer, choosing the even integer if it lies halfway between two.
    if ((x % 1) === 0.5 && (x & 1) === 0) { // [even number].5; round down (i.e. floor)
        return Math.floor(x);
    }

    return Math.round(x);
}

function createNumberConversion(bitLength, typeOpts) {
    if (!typeOpts.unsigned) {
        --bitLength;
    }
    const lowerBound = typeOpts.unsigned ? 0 : -Math.pow(2, bitLength);
    const upperBound = Math.pow(2, bitLength) - 1;

    const moduloVal = typeOpts.moduloBitLength ? Math.pow(2, typeOpts.moduloBitLength) : Math.pow(2, bitLength);
    const moduloBound = typeOpts.moduloBitLength ? Math.pow(2, typeOpts.moduloBitLength - 1) :
        Math.pow(2, bitLength - 1);

    return (V, opts) => {
        if (opts === undefined) {
            opts = {};
        }

        let x = +V;

        if (opts.enforceRange) {
            if (!Number.isFinite(x)) {
                throw new TypeError("Argument is not a finite number");
            }

            x = sign(x) * Math.floor(Math.abs(x));
            if (x < lowerBound || x > upperBound) {
                throw new TypeError("Argument is not in byte range");
            }

            return x;
        }

        if (!isNaN(x) && opts.clamp) {
            x = evenRound(x);

            if (x < lowerBound) {
                x = lowerBound;
            }
            if (x > upperBound) {
                x = upperBound;
            }
            return x;
        }

        if (!Number.isFinite(x) || x === 0) {
            return 0;
        }

        x = sign(x) * Math.floor(Math.abs(x));
        x %= moduloVal;

        if (!typeOpts.unsigned && x >= moduloBound) {
            return x - moduloVal;
        } else if (typeOpts.unsigned) {
            if (x < 0) {
                x += moduloVal;
            } else if (Object.is(x, -0)) { // Don't return negative zero
                return 0;
            }
        }

        return x;
    };
}

exports.any = V => {
    return V;
};

exports.void = function () {
    return undefined;
};

exports.boolean = function (val) {
    return !!val;
};

exports.byte = createNumberConversion(8, { unsigned: false });
exports.octet = createNumberConversion(8, { unsigned: true });

exports.short = createNumberConversion(16, { unsigned: false });
exports["unsigned short"] = createNumberConversion(16, { unsigned: true });

exports.long = createNumberConversion(32, { unsigned: false });
exports["unsigned long"] = createNumberConversion(32, { unsigned: true });

exports["long long"] = createNumberConversion(32, { unsigned: false, moduloBitLength: 64 });
exports["unsigned long long"] = createNumberConversion(32, { unsigned: true, moduloBitLength: 64 });

exports.double = V => {
    const x = +V;

    if (!Number.isFinite(x)) {
        throw new TypeError("Argument is not a finite floating-point value");
    }

    return x;
};

exports["unrestricted double"] = V => {
    const x = +V;

    return x;
};

exports.float = V => {
    const x = +V;

    if (!Number.isFinite(x)) {
        throw new TypeError("Argument is not a finite floating-point value");
    }

    if (Object.is(x, -0)) {
        return x;
    }

    const array = new Float32Array(1);
    array[0] = x;
    const y = array[0];

    if (!Number.isFinite(y)) {
        throw new TypeError("Argument is not within the range of a single-precision floating-point value");
    }

    return y;
};

exports["unrestricted float"] = V => {
    const x = +V;

    if (isNaN(x)) {
        return x;
    }

    if (Object.is(x, -0)) {
        return x;
    }

    const array = new Float32Array(1);
    array[0] = x;
    return array[0];
};

exports.DOMString = function (V, opts) {
    if (opts === undefined) {
        opts = {};
    }

    if (opts.treatNullAsEmptyString && V === null) {
        return "";
    }

    return String(V);
};

exports.ByteString = V => {
    const x = String(V);
    let c;
    for (let i = 0; (c = x.codePointAt(i)) !== undefined; ++i) {
        if (c > 255) {
            throw new TypeError("Argument is not a valid bytestring");
        }
    }

    return x;
};

exports.USVString = V => {
    const S = String(V);
    const n = S.length;
    const U = [];
    for (let i = 0; i < n; ++i) {
        const c = S.charCodeAt(i);
        if (c < 0xD800 || c > 0xDFFF) {
            U.push(String.fromCodePoint(c));
        } else if (0xDC00 <= c && c <= 0xDFFF) {
            U.push(String.fromCodePoint(0xFFFD));
        } else if (i === n - 1) {
            U.push(String.fromCodePoint(0xFFFD));
        } else {
            const d = S.charCodeAt(i + 1);
            if (0xDC00 <= d && d <= 0xDFFF) {
                const a = c & 0x3FF;
                const b = d & 0x3FF;
                U.push(String.fromCodePoint((2 << 15) + ((2 << 9) * a) + b));
                ++i;
            } else {
                U.push(String.fromCodePoint(0xFFFD));
            }
        }
    }

    return U.join("");
};

exports.object = V => {
    if (type(V) !== "Object") {
        throw new TypeError("Argument is not an object");
    }

    return V;
};

// Not exported, but used in Function and VoidFunction.

// Neither Function nor VoidFunction is defined with [TreatNonObjectAsNull], so
// handling for that is omitted.
function convertCallbackFunction(V) {
    if (typeof V !== "function") {
        throw new TypeError("Argument is not a function");
    }
    return V;
}

[
    Error,
    ArrayBuffer, // The IsDetachedBuffer abstract operation is not exposed in JS
    DataView, Int8Array, Int16Array, Int32Array, Uint8Array,
    Uint16Array, Uint32Array, Uint8ClampedArray, Float32Array, Float64Array
].forEach(func => {
    const name = func.name;
    const article = /^[AEIOU]/.test(name) ? "an" : "a";
    exports[name] = V => {
        if (!(V instanceof func)) {
            throw new TypeError(`Argument is not ${article} ${name} object`);
        }

        return V;
    };
});

// Common definitions

exports.ArrayBufferView = V => {
    if (!ArrayBuffer.isView(V)) {
        throw new TypeError("Argument is not a view on an ArrayBuffer object");
    }

    return V;
};

exports.BufferSource = V => {
    if (!(ArrayBuffer.isView(V) || V instanceof ArrayBuffer)) {
        throw new TypeError("Argument is not an ArrayBuffer object or a view on one");
    }

    return V;
};

exports.DOMTimeStamp = exports["unsigned long long"];

exports.Function = convertCallbackFunction;

exports.VoidFunction = convertCallbackFunction;
<commit_msg>Fix DOMString conversion for symbols<commit_after>"use strict";

function type(V) {
    if (V === null) {
        return "Null";
    }
    switch (typeof V) {
        case "undefined":
            return "Undefined";
        case "boolean":
            return "Boolean";
        case "number":
            return "Number";
        case "string":
            return "String";
        case "symbol":
            return "Symbol";
        case "object":
            // Falls through
        case "function":
            // Falls through
        default:
            // Per ES spec, typeof returns an implemention-defined value that is not any of the existing ones for
            // uncallable non-standard exotic objects. Yet Type() which the Web IDL spec depends on returns Object for
            // such cases. So treat the default case as an object.
            return "Object";
    }
}

function sign(x) {
    return x < 0 ? -1 : 1;
}

function evenRound(x) {
    // Round x to the nearest integer, choosing the even integer if it lies halfway between two.
    if ((x % 1) === 0.5 && (x & 1) === 0) { // [even number].5; round down (i.e. floor)
        return Math.floor(x);
    }

    return Math.round(x);
}

function createNumberConversion(bitLength, typeOpts) {
    if (!typeOpts.unsigned) {
        --bitLength;
    }
    const lowerBound = typeOpts.unsigned ? 0 : -Math.pow(2, bitLength);
    const upperBound = Math.pow(2, bitLength) - 1;

    const moduloVal = typeOpts.moduloBitLength ? Math.pow(2, typeOpts.moduloBitLength) : Math.pow(2, bitLength);
    const moduloBound = typeOpts.moduloBitLength ? Math.pow(2, typeOpts.moduloBitLength - 1) :
        Math.pow(2, bitLength - 1);

    return (V, opts) => {
        if (opts === undefined) {
            opts = {};
        }

        let x = +V;

        if (opts.enforceRange) {
            if (!Number.isFinite(x)) {
                throw new TypeError("Argument is not a finite number");
            }

            x = sign(x) * Math.floor(Math.abs(x));
            if (x < lowerBound || x > upperBound) {
                throw new TypeError("Argument is not in byte range");
            }

            return x;
        }

        if (!isNaN(x) && opts.clamp) {
            x = evenRound(x);

            if (x < lowerBound) {
                x = lowerBound;
            }
            if (x > upperBound) {
                x = upperBound;
            }
            return x;
        }

        if (!Number.isFinite(x) || x === 0) {
            return 0;
        }

        x = sign(x) * Math.floor(Math.abs(x));
        x %= moduloVal;

        if (!typeOpts.unsigned && x >= moduloBound) {
            return x - moduloVal;
        } else if (typeOpts.unsigned) {
            if (x < 0) {
                x += moduloVal;
            } else if (Object.is(x, -0)) { // Don't return negative zero
                return 0;
            }
        }

        return x;
    };
}

exports.any = V => {
    return V;
};

exports.void = function () {
    return undefined;
};

exports.boolean = function (val) {
    return !!val;
};

exports.byte = createNumberConversion(8, { unsigned: false });
exports.octet = createNumberConversion(8, { unsigned: true });

exports.short = createNumberConversion(16, { unsigned: false });
exports["unsigned short"] = createNumberConversion(16, { unsigned: true });

exports.long = createNumberConversion(32, { unsigned: false });
exports["unsigned long"] = createNumberConversion(32, { unsigned: true });

exports["long long"] = createNumberConversion(32, { unsigned: false, moduloBitLength: 64 });
exports["unsigned long long"] = createNumberConversion(32, { unsigned: true, moduloBitLength: 64 });

exports.double = V => {
    const x = +V;

    if (!Number.isFinite(x)) {
        throw new TypeError("Argument is not a finite floating-point value");
    }

    return x;
};

exports["unrestricted double"] = V => {
    const x = +V;

    return x;
};

exports.float = V => {
    const x = +V;

    if (!Number.isFinite(x)) {
        throw new TypeError("Argument is not a finite floating-point value");
    }

    if (Object.is(x, -0)) {
        return x;
    }

    const array = new Float32Array(1);
    array[0] = x;
    const y = array[0];

    if (!Number.isFinite(y)) {
        throw new TypeError("Argument is not within the range of a single-precision floating-point value");
    }

    return y;
};

exports["unrestricted float"] = V => {
    const x = +V;

    if (isNaN(x)) {
        return x;
    }

    if (Object.is(x, -0)) {
        return x;
    }

    const array = new Float32Array(1);
    array[0] = x;
    return array[0];
};

exports.DOMString = function (V, opts) {
    if (opts === undefined) {
        opts = {};
    }

    if (opts.treatNullAsEmptyString && V === null) {
        return "";
    }

    if (typeof V === "symbol") {
        throw new TypeError("Argument is a symbol, which cannot be converted to a string");
    }

    return String(V);
};

exports.ByteString = V => {
    const x = String(V);
    let c;
    for (let i = 0; (c = x.codePointAt(i)) !== undefined; ++i) {
        if (c > 255) {
            throw new TypeError("Argument is not a valid bytestring");
        }
    }

    return x;
};

exports.USVString = V => {
    const S = String(V);
    const n = S.length;
    const U = [];
    for (let i = 0; i < n; ++i) {
        const c = S.charCodeAt(i);
        if (c < 0xD800 || c > 0xDFFF) {
            U.push(String.fromCodePoint(c));
        } else if (0xDC00 <= c && c <= 0xDFFF) {
            U.push(String.fromCodePoint(0xFFFD));
        } else if (i === n - 1) {
            U.push(String.fromCodePoint(0xFFFD));
        } else {
            const d = S.charCodeAt(i + 1);
            if (0xDC00 <= d && d <= 0xDFFF) {
                const a = c & 0x3FF;
                const b = d & 0x3FF;
                U.push(String.fromCodePoint((2 << 15) + ((2 << 9) * a) + b));
                ++i;
            } else {
                U.push(String.fromCodePoint(0xFFFD));
            }
        }
    }

    return U.join("");
};

exports.object = V => {
    if (type(V) !== "Object") {
        throw new TypeError("Argument is not an object");
    }

    return V;
};

// Not exported, but used in Function and VoidFunction.

// Neither Function nor VoidFunction is defined with [TreatNonObjectAsNull], so
// handling for that is omitted.
function convertCallbackFunction(V) {
    if (typeof V !== "function") {
        throw new TypeError("Argument is not a function");
    }
    return V;
}

[
    Error,
    ArrayBuffer, // The IsDetachedBuffer abstract operation is not exposed in JS
    DataView, Int8Array, Int16Array, Int32Array, Uint8Array,
    Uint16Array, Uint32Array, Uint8ClampedArray, Float32Array, Float64Array
].forEach(func => {
    const name = func.name;
    const article = /^[AEIOU]/.test(name) ? "an" : "a";
    exports[name] = V => {
        if (!(V instanceof func)) {
            throw new TypeError(`Argument is not ${article} ${name} object`);
        }

        return V;
    };
});

// Common definitions

exports.ArrayBufferView = V => {
    if (!ArrayBuffer.isView(V)) {
        throw new TypeError("Argument is not a view on an ArrayBuffer object");
    }

    return V;
};

exports.BufferSource = V => {
    if (!(ArrayBuffer.isView(V) || V instanceof ArrayBuffer)) {
        throw new TypeError("Argument is not an ArrayBuffer object or a view on one");
    }

    return V;
};

exports.DOMTimeStamp = exports["unsigned long long"];

exports.Function = convertCallbackFunction;

exports.VoidFunction = convertCallbackFunction;
<|endoftext|>


<commit_before>import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, setupOnerror } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('Integration | Component | o-s-s/button', function (hooks) {
  setupRenderingTest(hooks);

  test('it fails if label and icon are missing', async function (assert) {
    setupOnerror((err) => {
      assert.equal(err.message, '[component][OSS::Button] You must pass either a @label or an @icon argument.');
    });

    await render(hbs`<OSS::Button />`);
  });

  test('it renders the icon when present', async function (assert) {
    await render(hbs`<OSS::Button @icon="fab fa-facebook" />`);

    assert.dom('.upf-btn i').hasClass('fa-facebook');
  });

  test('it renders the icon and label when present', async function (assert) {
    await render(hbs`<OSS::Button @icon="fab fa-facebook" @label="Label" />`);

    assert.dom('.upf-btn i').hasClass('fa-facebook');
    assert.dom('.upf-btn span').hasClass('margin-left-xxx-sm');
    assert.dom('.upf-btn span').hasText('Label');
  });

  test('it renders default button', async function (assert) {
    await render(hbs`<OSS::Button @label="Test" />`);

    assert.dom('.upf-btn').exists({ count: 1 });
    assert.dom('.upf-btn').hasClass('upf-btn--default');
    assert.dom('.upf-btn').hasText('Test');
  });

  module('it render with right skin', function () {
    test('when using unknown skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="unknown" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--default');
    });

    test('when using primary skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="primary" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--primary');
    });

    test('when using secondary skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="secondary" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--secondary');
    });

    test('when using destructive skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="destructive" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--destructive');
    });

    test('when using social-instagram skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="instagram" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--social-instagram');
    });

    test('when using social-facebook skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="facebook" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--social-facebook');
    });

    test('when using social-youtube skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="youtube" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--social-youtube');
    });
  });

  module('it render with right size', function () {
    test('when using small', async function (assert) {
      await render(hbs`<OSS::Button @size="s" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--small');
    });

    test('when using x-small', async function (assert) {
      await render(hbs`<OSS::Button @size="xs" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--x-small');
    });
  });

  module('it render with loading state', function () {
    test('when using default loading', async function (assert) {
      await render(hbs`<OSS::Button @loading="true" @label="Test" />`);
      const btn = document.querySelector('.upf-btn');

      assert.equal(btn.children[0].className, 'fas fa-circle-notch fa-spin');
    });
  });

  module('it renders with the right theme', function () {
    test('it adds the right class for usage on dark theme', async function (assert) {
      await render(hbs`<OSS::Button @label="Test" @theme="dark" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--dark-bg');
    });
  });
});
<commit_msg>Added: oss::button new tests<commit_after>import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, setupOnerror } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('Integration | Component | o-s-s/button', function (hooks) {
  setupRenderingTest(hooks);

  test('it fails if label and icon are missing', async function (assert) {
    setupOnerror((err) => {
      assert.equal(err.message, '[component][OSS::Button] You must pass either a @label or an @icon argument.');
    });

    await render(hbs`<OSS::Button />`);
  });

  test('it renders the icon when present', async function (assert) {
    await render(hbs`<OSS::Button @icon="fab fa-facebook" />`);

    assert.dom('.upf-btn i').hasClass('fa-facebook');
  });

  test('it renders the icon and label when present', async function (assert) {
    await render(hbs`<OSS::Button @icon="fab fa-facebook" @label="Label" />`);

    assert.dom('.upf-btn i').hasClass('fa-facebook');
    assert.dom('.upf-btn span').hasClass('margin-left-xxx-sm');
    assert.dom('.upf-btn span').hasText('Label');
  });

  test('it renders the default button', async function (assert) {
    await render(hbs`<OSS::Button @label="Test" />`);

    assert.dom('.upf-btn').exists({ count: 1 });
    assert.dom('.upf-btn').hasClass('upf-btn--default');
    assert.dom('.upf-btn').hasText('Test');
  });

  module('it render with the correct skin', function () {
    test('when using an unknown skin, it is set to default', async function (assert) {
      await render(hbs`<OSS::Button @skin="unknown" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--default');
    });

    test('when using primary skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="primary" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--primary');
    });

    test('when using secondary skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="secondary" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--secondary');
    });

    test('when using destructive skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="destructive" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--destructive');
    });

    test('when using alert skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="alert" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--alert');
    });

    test('when using success skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="success" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--success');
    });

    test('when using social-instagram skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="instagram" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--social-instagram');
    });

    test('when using social-facebook skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="facebook" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--social-facebook');
    });

    test('when using social-youtube skin', async function (assert) {
      await render(hbs`<OSS::Button @skin="youtube" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--social-youtube');
    });
  });

  module('it render with the right size', function () {
    test('when using xs', async function (assert) {
      await render(hbs`<OSS::Button @size="xs" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--xs');
    });

    test('when using sm', async function (assert) {
      await render(hbs`<OSS::Button @size="sm" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--sm');
    });

    test('when using md', async function (assert) {
      await render(hbs`<OSS::Button @size="md" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--md');
    });

    test('when using lg', async function (assert) {
      await render(hbs`<OSS::Button @size="lg" @label="Test" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--lg');
    });
  });

  module('it renders with loading state', function () {
    test('when using default loading', async function (assert) {
      await render(hbs`<OSS::Button @size="sm" @loading="true" @label="Test" />`);
      const btn = document.querySelector('.upf-btn');

      assert.equal(btn.children[0].className, 'fas fa-circle-notch fa-spin');
    });
  });

  module('it renders a square button', function () {
    test('when setting the square parameter to true', async function (assert) {
      await render(hbs`<OSS::Button @square="true" @label="Test" />`);

      assert.dom('.upf-square-btn').exists();
    });
  });

  module('it renders with the right theme', function () {
    test('it adds the right class for usage on dark theme', async function (assert) {
      await render(hbs`<OSS::Button @label="Test" @theme="dark" />`);

      assert.dom('.upf-btn').hasClass('upf-btn--dark-bg');
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

/**
 * Currently we are using gulp as only build system, but I wouldn't want to
 * hang to it too much (maybe own client would be better to have...)
 *
 * Biggest problem is that when writing dodo plugins which registers their
 * own tasks, it would be nasty to have to write support for multiple build
 * systems there... so lets try to write all real build code platform
 * independently and then gulp-base just wraps common implementations to gulp...
 * (if one likes to use gulp in their common implementation that is fine too
 * not my problem)
 */

var _ = require('lodash')
 , fs = require('fs')
 , path = require('path')
 , plugins = require('../app/plugins')
 ;

module.exports = {
  /**
   * TODO: read also configuration to be able to tell which plugins /
   *       plugin tasks are available for each service
   */
  scanServices: function (serviceDir, selectedConfiguration) {
    if (!serviceDir.startsWith('/')) {
      throw new Error(`serviceDir (${serviceDir}) must be absolute path.`);
    }

    // Create services folder if it doesn't exist.
    if (!fs.existsSync(serviceDir)) {
      fs.mkdirSync(serviceDir);
    }

    // Names of all the services (must have directory serviceName/config)
    var serviceNames = _.filter(fs.readdirSync(serviceDir), function(fileName) {
      var serviceConfigPath = path.join(serviceDir, fileName, 'config');
      try {
        return fs.lstatSync(serviceConfigPath).isDirectory();
      } catch (err) {
        return false;
      }
    });

    return _.map(serviceNames, function (serviceName) {
      var servicePath = path.join(serviceDir, serviceName);
      var configurationPath = path.join(servicePath, 'config', selectedConfiguration);
      var configuration = null;

      if (!configurationPath.startsWith('/')) {
        configurationPath = './' + configurationPath;
      }

      try {
        configuration = require(configurationPath);
        configuration = configuration.default || configuration;

        // pass directories etc. to configuration
        if (_.isFunction(configuration)) {
          configuration = configuration({
            servicePath: servicePath
          });
        }
      } catch (err) {
        // could not read configuration, maybe given profile was not defined for
        // the service, which is totally cool... no prob bro!
      }

      var tasks = [];
      if (configuration) {
        // TODO: move these tasks to separate file and test that they do what they should
        tasks.push({
          name: 'serve',
          run: function () {
            var app = require('../app/express/main');
            return app.start(configuration);
          },
          description: 'Start the express app of the service.'
        });

        // add tasks declared by features to build
        var features = plugins.loadFeatures(configuration);
        _.each(features, function(featureDescription) {
          _.each(featureDescription.tasks, function (task) {
            // wrap task runner function to closure which passes required parameters  to call, which takes only global configuration as parameter and pass feature config implicitly
            tasks.push({
              name: task.name,
              description: task.description,
              run: function () {
                return task.run(featureDescription.featureDef.config, configuration, servicePath);
              }
            });
          });
        });
      }

      return {
        name: serviceName,
        configPath: configurationPath,
        config: configuration,
        tasks: tasks
      };
    });
  }
};
<commit_msg>Improve configuration error handling<commit_after>'use strict';

/**
 * Currently we are using gulp as only build system, but I wouldn't want to
 * hang to it too much (maybe own client would be better to have...)
 *
 * Biggest problem is that when writing dodo plugins which registers their
 * own tasks, it would be nasty to have to write support for multiple build
 * systems there... so lets try to write all real build code platform
 * independently and then gulp-base just wraps common implementations to gulp...
 * (if one likes to use gulp in their common implementation that is fine too
 * not my problem)
 */

var _ = require('lodash')
 , fs = require('fs')
 , path = require('path')
 , plugins = require('../app/plugins')
 ;

module.exports = {
  /**
   * TODO: read also configuration to be able to tell which plugins /
   *       plugin tasks are available for each service
   */
  scanServices: function (serviceDir, selectedConfiguration) {
    if (!serviceDir.startsWith('/')) {
      throw new Error(`serviceDir (${serviceDir}) must be absolute path.`);
    }

    // Create services folder if it doesn't exist.
    if (!fs.existsSync(serviceDir)) {
      fs.mkdirSync(serviceDir);
    }

    // Names of all the services (must have directory serviceName/config)
    var serviceNames = _.filter(fs.readdirSync(serviceDir), function(fileName) {
      var serviceConfigPath = path.join(serviceDir, fileName, 'config');
      try {
        return fs.lstatSync(serviceConfigPath).isDirectory();
      } catch (err) {
        return false;
      }
    });

    return _.map(serviceNames, function (serviceName) {
      var servicePath = path.join(serviceDir, serviceName);
      var configurationPath = path.join(servicePath, 'config', selectedConfiguration);
      var configuration = null;

      if (!configurationPath.startsWith('/')) {
        configurationPath = './' + configurationPath;
      }

      try {
        configuration = require(configurationPath);
        configuration = configuration.default || configuration;

        // pass directories etc. to configuration
        if (_.isFunction(configuration)) {
          configuration = configuration({
            servicePath: servicePath
          });
        }
      } catch (err) {
        // rethrow if the error was not about missing module
        if ( err.code !== 'MODULE_NOT_FOUND' ) {
            throw err;
        }
        // ignore missing module errors silently, maybe given profile was not defined for
        // the service, which is totally cool... no prob bro!
      }

      var tasks = [];
      if (configuration) {
        // TODO: move these tasks to separate file and test that they do what they should
        tasks.push({
          name: 'serve',
          run: function () {
            var app = require('../app/express/main');
            return app.start(configuration);
          },
          description: 'Start the express app of the service.'
        });

        // add tasks declared by features to build
        var features = plugins.loadFeatures(configuration);
        _.each(features, function(featureDescription) {
          _.each(featureDescription.tasks, function (task) {
            // wrap task runner function to closure which passes required parameters  to call, which takes only global configuration as parameter and pass feature config implicitly
            tasks.push({
              name: task.name,
              description: task.description,
              run: function () {
                return task.run(featureDescription.featureDef.config, configuration, servicePath);
              }
            });
          });
        });
      }

      return {
        name: serviceName,
        configPath: configurationPath,
        config: configuration,
        tasks: tasks
      };
    });
  }
};
<|endoftext|>


<commit_before>
Calendar.prototype.isEventInstanceGroupAllowed = function(eventInstanceGroup) {
	var eventDef = eventInstanceGroup.getEventDef();
	var eventFootprints = this.eventRangesToEventFootprints(eventInstanceGroup.getAllEventRanges());
	var i;

	var peerEventInstances = this.eventManager.getEventInstancesWithoutId(eventDef.id);
	var peerEventRanges = eventInstancesToEventRanges(peerEventInstances);
	var peerEventFootprints = this.eventRangesToEventFootprints(peerEventRanges);

	var constraintVal = eventDef.getConstraint();
	var overlapVal = eventDef.getOverlap();

	var eventAllowFunc = this.opt('eventAllow');

	for (i = 0; i < eventFootprints.length; i++) {
		if (
			!this.isFootprintAllowed(
				eventFootprints[i].componentFootprint,
				peerEventFootprints,
				constraintVal,
				overlapVal,
				eventFootprints[i].eventInstance
			)
		) {
			return false;
		}
	}

	if (eventAllowFunc) {
		for (i = 0; i < eventFootprints.length; i++) {
			if (
				eventAllowFunc(
					convertFootprintToLegacySelection(eventFootprints[i].componentFootprint, this),
					eventFootprints[i].toLegacy()
				) === false
			) {
				return false;
			}
		}
	}

	return true;
};

Calendar.prototype.isSelectionFootprintAllowed = function(componentFootprint) {
	var peerEventInstances = this.eventManager.getEventInstances();
	var peerEventRanges = eventInstancesToEventRanges(peerEventInstances);
	var peerEventFootprints = this.eventRangesToEventFootprints(peerEventRanges);

	var selectAllowFunc;

	if (
		this.isFootprintAllowed(
			componentFootprint,
			peerEventFootprints,
			this.opt('selectConstraint'),
			this.opt('selectOverlap')
		)
	) {
		selectAllowFunc = this.opt('selectAllow');

		if (selectAllowFunc) {
			return selectAllowFunc(componentFootprint.toLegacy()) !== false;
		}
		else {
			return true;
		}
	}

	return false;
};

Calendar.prototype.isFootprintAllowed = function(
	componentFootprint,
	peerEventFootprints,
	constraintVal,
	overlapVal,
	subjectEventInstance // optional
) {
	var constraintFootprints; // ComponentFootprint[]
	var overlapEventFootprints; // EventFootprint[]

	if (constraintVal != null) {
		constraintFootprints = this.constraintValToFootprints(constraintVal, componentFootprint.isAllDay);

		if (!this.isFootprintWithinConstraints(componentFootprint, constraintFootprints)) {
			return false;
		}
	}

	overlapEventFootprints = this.collectOverlapEventFootprints(peerEventFootprints, componentFootprint);

	if (overlapVal === false) {
		if (overlapEventFootprints.length) {
			return false;
		}
	}
	else if (typeof overlapVal === 'function') {
		if (!isOverlapsAllowedByFunc(overlapEventFootprints, overlapVal, subjectEventInstance)) {
			return false;
		}
	}

	if (subjectEventInstance) {
		if (!isOverlapEventInstancesAllowed(overlapEventFootprints, subjectEventInstance)) {
			return false;
		}
	}

	return true;
};

// Constraint
// ------------------------------------------------------------------------------------------------

Calendar.prototype.isFootprintWithinConstraints = function(componentFootprint, constraintFootprints) {
	var i;

	for (i = 0; i < constraintFootprints.length; i++) {
		if (this.footprintContainsFootprint(constraintFootprints[i], componentFootprint)) {
			return true;
		}
	}

	return false;
};

Calendar.prototype.constraintValToFootprints = function(constraintVal, isAllDay) {
	if (constraintVal === 'businessHours') {
		return this.buildCurrentBusinessFootprints(isAllDay);
	}
	else if (typeof constraintVal === 'object') {
		return this.eventInstancesToFootprints(
			this.parseEventDefToInstances(constraintVal)
		);
	}
	else if (constraintVal != null) { // an ID
		return this.eventInstancesToFootprints(
			this.eventManager.getEventInstancesWithId(constraintVal)
		);
	}
};

// conversion util
Calendar.prototype.eventInstancesToFootprints = function(eventInstances) {
	return eventFootprintsToComponentFootprints(
		this.eventRangesToEventFootprints(
			eventInstancesToEventRanges(eventInstances)
		)
	);
};

// Overlap
// ------------------------------------------------------------------------------------------------

Calendar.prototype.collectOverlapEventFootprints = function(peerEventFootprints, targetFootprint) {
	var overlapEventFootprints = [];
	var i;

	for (i = 0; i < peerEventFootprints.length; i++) {
		if (
			this.footprintsIntersect(
				targetFootprint,
				peerEventFootprints[i].componentFootprint
			)
		) {
			overlapEventFootprints.push(peerEventFootprints[i]);
		}
	}

	return overlapEventFootprints;
};

// optional subjectEventInstance
function isOverlapsAllowedByFunc(overlapEventFootprints, overlapFunc, subjectEventInstance) {
	var i;

	for (i = 0; i < overlapEventFootprints.length; i++) {
		if (
			!overlapFunc(
				overlapEventFootprints[i].eventInstance.toLegacy(),
				subjectEventInstance ? subjectEventInstance.toLegacy() : null
			)
		) {
			return false;
		}
	}

	return true;
}

function isOverlapEventInstancesAllowed(overlapEventFootprints, subjectEventInstance) {
	var subjectLegacyInstance = subjectEventInstance.toLegacy();
	var i;
	var overlapEventInstance;
	var overlapEventDef;
	var overlapVal;

	for (i = 0; i < overlapEventFootprints.length; i++) {
		overlapEventInstance = overlapEventFootprints[i].eventInstance;
		overlapEventDef = overlapEventInstance.def;

		// don't need to pass in calendar, because don't want to consider global eventOverlap property,
		// because we already considered that earlier in the process.
		overlapVal = overlapEventDef.getOverlap();

		if (overlapVal === false) {
			return false;
		}
		else if (typeof overlapVal === 'function') {
			if (
				!overlapVal(
					overlapEventInstance.toLegacy(),
					subjectLegacyInstance
				)
			) {
				return false;
			}
		}
	}

	return true;
}

// Conversion: eventDefs -> eventInstances -> eventRanges -> eventFootprints -> componentFootprints
// ------------------------------------------------------------------------------------------------
// NOTE: this might seem like repetitive code with the Grid class, however, this code is related to
// constraints whereas the Grid code is related to rendering. Each approach might want to convert
// eventRanges -> eventFootprints in a different way. Regardless, there are opportunities to make
// this more DRY.

Calendar.prototype.parseEventDefToInstances = function(eventInput) {
	var eventPeriod = this.eventManager.currentPeriod;
	var eventDef = EventDefParser.parse(eventInput, new EventSource(this));

	if (eventPeriod && eventDef) {
		return eventDef.buildInstances(eventPeriod.start, eventPeriod.end);
	}
	else {
		return [];
	}
};

Calendar.prototype.eventRangesToEventFootprints = function(eventRanges) {
	var i;
	var eventFootprints = [];

	for (i = 0; i < eventRanges.length; i++) {
		eventFootprints.push.apply(eventFootprints, // append
			this.eventRangeToEventFootprints(eventRanges[i])
		);
	}

	return eventFootprints;
};

Calendar.prototype.eventRangeToEventFootprints = function(eventRange) {
	return [
		new EventFootprint(
			new ComponentFootprint(
				eventRange.unzonedRange,
				eventRange.eventDef.isAllDay()
			),
			eventRange.eventDef,
			eventRange.eventInstance // might not exist
		)
	];
};

// Footprint Utils
// ----------------------------------------------------------------------------------------

Calendar.prototype.footprintContainsFootprint = function(outerFootprint, innerFootprint) {
	// TODO: use date range utils
	return innerFootprint.unzonedRange.startMs >= outerFootprint.unzonedRange.startMs &&
		innerFootprint.unzonedRange.endMs <= outerFootprint.unzonedRange.endMs;
};

Calendar.prototype.footprintsIntersect = function(footprint0, footprint1) {
	// TODO: use date range utils
	return footprint0.unzonedRange.startMs < footprint1.unzonedRange.endMs &&
		footprint0.unzonedRange.endMs > footprint1.unzonedRange.startMs;
};
<commit_msg>Calendar::getPeerEventInstances<commit_after>
Calendar.prototype.isEventInstanceGroupAllowed = function(eventInstanceGroup) {
	var eventDef = eventInstanceGroup.getEventDef();
	var eventFootprints = this.eventRangesToEventFootprints(eventInstanceGroup.getAllEventRanges());
	var i;

	var peerEventInstances = this.getPeerEventInstances(eventDef);
	var peerEventRanges = eventInstancesToEventRanges(peerEventInstances);
	var peerEventFootprints = this.eventRangesToEventFootprints(peerEventRanges);

	var constraintVal = eventDef.getConstraint();
	var overlapVal = eventDef.getOverlap();

	var eventAllowFunc = this.opt('eventAllow');

	for (i = 0; i < eventFootprints.length; i++) {
		if (
			!this.isFootprintAllowed(
				eventFootprints[i].componentFootprint,
				peerEventFootprints,
				constraintVal,
				overlapVal,
				eventFootprints[i].eventInstance
			)
		) {
			return false;
		}
	}

	if (eventAllowFunc) {
		for (i = 0; i < eventFootprints.length; i++) {
			if (
				eventAllowFunc(
					convertFootprintToLegacySelection(eventFootprints[i].componentFootprint, this),
					eventFootprints[i].toLegacy()
				) === false
			) {
				return false;
			}
		}
	}

	return true;
};

Calendar.prototype.getPeerEventInstances = function(eventDef) {
	return this.eventManager.getEventInstancesWithoutId(eventDef.id);
};

Calendar.prototype.isSelectionFootprintAllowed = function(componentFootprint) {
	var peerEventInstances = this.eventManager.getEventInstances();
	var peerEventRanges = eventInstancesToEventRanges(peerEventInstances);
	var peerEventFootprints = this.eventRangesToEventFootprints(peerEventRanges);

	var selectAllowFunc;

	if (
		this.isFootprintAllowed(
			componentFootprint,
			peerEventFootprints,
			this.opt('selectConstraint'),
			this.opt('selectOverlap')
		)
	) {
		selectAllowFunc = this.opt('selectAllow');

		if (selectAllowFunc) {
			return selectAllowFunc(componentFootprint.toLegacy()) !== false;
		}
		else {
			return true;
		}
	}

	return false;
};

Calendar.prototype.isFootprintAllowed = function(
	componentFootprint,
	peerEventFootprints,
	constraintVal,
	overlapVal,
	subjectEventInstance // optional
) {
	var constraintFootprints; // ComponentFootprint[]
	var overlapEventFootprints; // EventFootprint[]

	if (constraintVal != null) {
		constraintFootprints = this.constraintValToFootprints(constraintVal, componentFootprint.isAllDay);

		if (!this.isFootprintWithinConstraints(componentFootprint, constraintFootprints)) {
			return false;
		}
	}

	overlapEventFootprints = this.collectOverlapEventFootprints(peerEventFootprints, componentFootprint);

	if (overlapVal === false) {
		if (overlapEventFootprints.length) {
			return false;
		}
	}
	else if (typeof overlapVal === 'function') {
		if (!isOverlapsAllowedByFunc(overlapEventFootprints, overlapVal, subjectEventInstance)) {
			return false;
		}
	}

	if (subjectEventInstance) {
		if (!isOverlapEventInstancesAllowed(overlapEventFootprints, subjectEventInstance)) {
			return false;
		}
	}

	return true;
};

// Constraint
// ------------------------------------------------------------------------------------------------

Calendar.prototype.isFootprintWithinConstraints = function(componentFootprint, constraintFootprints) {
	var i;

	for (i = 0; i < constraintFootprints.length; i++) {
		if (this.footprintContainsFootprint(constraintFootprints[i], componentFootprint)) {
			return true;
		}
	}

	return false;
};

Calendar.prototype.constraintValToFootprints = function(constraintVal, isAllDay) {
	if (constraintVal === 'businessHours') {
		return this.buildCurrentBusinessFootprints(isAllDay);
	}
	else if (typeof constraintVal === 'object') {
		return this.eventInstancesToFootprints(
			this.parseEventDefToInstances(constraintVal)
		);
	}
	else if (constraintVal != null) { // an ID
		return this.eventInstancesToFootprints(
			this.eventManager.getEventInstancesWithId(constraintVal)
		);
	}
};

// conversion util
Calendar.prototype.eventInstancesToFootprints = function(eventInstances) {
	return eventFootprintsToComponentFootprints(
		this.eventRangesToEventFootprints(
			eventInstancesToEventRanges(eventInstances)
		)
	);
};

// Overlap
// ------------------------------------------------------------------------------------------------

Calendar.prototype.collectOverlapEventFootprints = function(peerEventFootprints, targetFootprint) {
	var overlapEventFootprints = [];
	var i;

	for (i = 0; i < peerEventFootprints.length; i++) {
		if (
			this.footprintsIntersect(
				targetFootprint,
				peerEventFootprints[i].componentFootprint
			)
		) {
			overlapEventFootprints.push(peerEventFootprints[i]);
		}
	}

	return overlapEventFootprints;
};

// optional subjectEventInstance
function isOverlapsAllowedByFunc(overlapEventFootprints, overlapFunc, subjectEventInstance) {
	var i;

	for (i = 0; i < overlapEventFootprints.length; i++) {
		if (
			!overlapFunc(
				overlapEventFootprints[i].eventInstance.toLegacy(),
				subjectEventInstance ? subjectEventInstance.toLegacy() : null
			)
		) {
			return false;
		}
	}

	return true;
}

function isOverlapEventInstancesAllowed(overlapEventFootprints, subjectEventInstance) {
	var subjectLegacyInstance = subjectEventInstance.toLegacy();
	var i;
	var overlapEventInstance;
	var overlapEventDef;
	var overlapVal;

	for (i = 0; i < overlapEventFootprints.length; i++) {
		overlapEventInstance = overlapEventFootprints[i].eventInstance;
		overlapEventDef = overlapEventInstance.def;

		// don't need to pass in calendar, because don't want to consider global eventOverlap property,
		// because we already considered that earlier in the process.
		overlapVal = overlapEventDef.getOverlap();

		if (overlapVal === false) {
			return false;
		}
		else if (typeof overlapVal === 'function') {
			if (
				!overlapVal(
					overlapEventInstance.toLegacy(),
					subjectLegacyInstance
				)
			) {
				return false;
			}
		}
	}

	return true;
}

// Conversion: eventDefs -> eventInstances -> eventRanges -> eventFootprints -> componentFootprints
// ------------------------------------------------------------------------------------------------
// NOTE: this might seem like repetitive code with the Grid class, however, this code is related to
// constraints whereas the Grid code is related to rendering. Each approach might want to convert
// eventRanges -> eventFootprints in a different way. Regardless, there are opportunities to make
// this more DRY.

Calendar.prototype.parseEventDefToInstances = function(eventInput) {
	var eventPeriod = this.eventManager.currentPeriod;
	var eventDef = EventDefParser.parse(eventInput, new EventSource(this));

	if (eventPeriod && eventDef) {
		return eventDef.buildInstances(eventPeriod.start, eventPeriod.end);
	}
	else {
		return [];
	}
};

Calendar.prototype.eventRangesToEventFootprints = function(eventRanges) {
	var i;
	var eventFootprints = [];

	for (i = 0; i < eventRanges.length; i++) {
		eventFootprints.push.apply(eventFootprints, // append
			this.eventRangeToEventFootprints(eventRanges[i])
		);
	}

	return eventFootprints;
};

Calendar.prototype.eventRangeToEventFootprints = function(eventRange) {
	return [
		new EventFootprint(
			new ComponentFootprint(
				eventRange.unzonedRange,
				eventRange.eventDef.isAllDay()
			),
			eventRange.eventDef,
			eventRange.eventInstance // might not exist
		)
	];
};

// Footprint Utils
// ----------------------------------------------------------------------------------------

Calendar.prototype.footprintContainsFootprint = function(outerFootprint, innerFootprint) {
	// TODO: use date range utils
	return innerFootprint.unzonedRange.startMs >= outerFootprint.unzonedRange.startMs &&
		innerFootprint.unzonedRange.endMs <= outerFootprint.unzonedRange.endMs;
};

Calendar.prototype.footprintsIntersect = function(footprint0, footprint1) {
	// TODO: use date range utils
	return footprint0.unzonedRange.startMs < footprint1.unzonedRange.endMs &&
		footprint0.unzonedRange.endMs > footprint1.unzonedRange.startMs;
};
<|endoftext|>