<commit_before>import * as TripUtils  from './trip-utils.js';

const mockCurUserEmail = 'cur.user@email.com';
const USER_A_EMAIL = 'apple@email.com';
const USER_A_EMAIL = 'banana@email.com';
const USER_Z_EMAIL = 'zamboni@email.com';
jest.mock('../AuthUtils', () => ({
    getCurUserEmail: () => mockCurUserEmail,
    getUserUidArrFromUserEmailArr: (userEmailArr) => {
          return new Promise(resolve => {
            resolve(userEmailArr.map(userEmail => '_' + userEmail + '_'));
          });
        },
}));

describe('getCleanedTextInput tests', () => {
  test('No input entered in form (empty string)', () => {
    const testDefaultValue = 'Untitled Trip';
    const testRawName = '';
    const expectedTripName = testDefaultValue;

    const testTripName = TripUtils.getCleanedTextInput(testRawName,
                                                       testDefaultValue);

    expect(testTripName).toEqual(expectedTripName);
  });

  test('Input entered into form', () => {
    const testDefaultValue = 'Untitled Trip';
    const testRawName = 'Trip to No Man\'s Land';
    const expectedTripName = testRawName;

    const testTripName = TripUtils.getCleanedTextInput(testRawName,
                                                       testDefaultValue);

    expect(testTripName).toEqual(expectedTripName);
  });
});

describe('getCollaboratorUidArray tests', () => {
  test('No collaborators entered', async () => {
    const expectedUidArr = [`_${mockCurUserEmail}_`];
    // This is the list that is created when there are no collaborators added
    // (automatically one empty string from the constructor created ref).
    const testEmailArr = [''];

    const testUidArr = await TripUtils.getCollaboratorUidArray(testEmailArr);

    expect(testUidArr).toEqual(expectedUidArr);
  });

  test('Some added collaborators', async () => {
    const expectedUidArr = [`_${mockCurUserEmail}_`,
                            `_${USER_A_EMAIL}_`,
                            `_${USER_Z_EMAIL}_`];
    const testEmailArr = [USER_A_EMAIL, USER_Z_EMAIL];

    const testUidArr = await TripUtils.getCollaboratorUidArray(testEmailArr);

    expect(testUidArr).toEqual(expectedUidArr);
  });

  test('Some added collaborators and some blank entries', async () => {
    const expectedUidArr = [`_${mockCurUserEmail}_`,
                            `_${USER_A_EMAIL}_`,
                            `_${USER_Z_EMAIL}_`];
    const testEmailArr = ['', USER_A_EMAIL, '', USER_Z_EMAIL, ''];

    const testUidArr = await TripUtils.getCollaboratorUidArray(testEmailArr);

    expect(testUidArr).toEqual(expectedUidArr);
  });
});

describe('moveCurUserEmailToFront tests', () => {
  test('No users (error is caught in getUserEmailArrFromUserUidArr)', () => {
    const expectedEmailArr = [mockCurUserEmail];
    const testEmailInputArr = [];

    const testEmailOutputArr = TripUtils.moveCurUserEmailToFront(testEmailInputArr);

    expect(testEmailOutputArr).toEqual(expectedEmailArr);
  });

  test('Only the current user', () => {
    const expectedEmailArr = [mockCurUserEmail];
    const testEmailInputArr = [mockCurUserEmail];

    const testEmailOutputArr = TripUtils.moveCurUserEmailToFront(testEmailInputArr);

    expect(testEmailOutputArr).toEqual(expectedEmailArr);
  });

  test('Current user between two others (alphabetical order)', () => {
    const expectedEmailArr = [mockCurUserEmail, USER_A_EMAIL, USER_Z_EMAIL];
    const testEmailInputArr = [USER_A_EMAIL, mockCurUserEmail, USER_Z_EMAIL];

    const testEmailOutputArr = TripUtils.moveCurUserEmailToFront(testEmailInputArr);

    expect(testEmailOutputArr).toEqual(expectedEmailArr);
  });
});
<commit_msg>Fix duplicate declaration bug.<commit_after>import * as TripUtils  from './trip-utils.js';

const mockCurUserEmail = 'cur.user@email.com';
const USER_A_EMAIL = 'apple@email.com';
const USER_Z_EMAIL = 'zamboni@email.com';
jest.mock('../AuthUtils', () => ({
    getCurUserEmail: () => mockCurUserEmail,
    getUserUidArrFromUserEmailArr: (userEmailArr) => {
          return new Promise(resolve => {
            resolve(userEmailArr.map(userEmail => '_' + userEmail + '_'));
          });
        },
}));

describe('getCleanedTextInput tests', () => {
  test('No input entered in form (empty string)', () => {
    const testDefaultValue = 'Untitled Trip';
    const testRawName = '';
    const expectedTripName = testDefaultValue;

    const testTripName = TripUtils.getCleanedTextInput(testRawName,
                                                       testDefaultValue);

    expect(testTripName).toEqual(expectedTripName);
  });

  test('Input entered into form', () => {
    const testDefaultValue = 'Untitled Trip';
    const testRawName = 'Trip to No Man\'s Land';
    const expectedTripName = testRawName;

    const testTripName = TripUtils.getCleanedTextInput(testRawName,
                                                       testDefaultValue);

    expect(testTripName).toEqual(expectedTripName);
  });
});

describe('getCollaboratorUidArray tests', () => {
  test('No collaborators entered', async () => {
    const expectedUidArr = [`_${mockCurUserEmail}_`];
    // This is the list that is created when there are no collaborators added
    // (automatically one empty string from the constructor created ref).
    const testEmailArr = [''];

    const testUidArr = await TripUtils.getCollaboratorUidArray(testEmailArr);

    expect(testUidArr).toEqual(expectedUidArr);
  });

  test('Some added collaborators', async () => {
    const expectedUidArr = [`_${mockCurUserEmail}_`,
                            `_${USER_A_EMAIL}_`,
                            `_${USER_Z_EMAIL}_`];
    const testEmailArr = [USER_A_EMAIL, USER_Z_EMAIL];

    const testUidArr = await TripUtils.getCollaboratorUidArray(testEmailArr);

    expect(testUidArr).toEqual(expectedUidArr);
  });

  test('Some added collaborators and some blank entries', async () => {
    const expectedUidArr = [`_${mockCurUserEmail}_`,
                            `_${USER_A_EMAIL}_`,
                            `_${USER_Z_EMAIL}_`];
    const testEmailArr = ['', USER_A_EMAIL, '', USER_Z_EMAIL, ''];

    const testUidArr = await TripUtils.getCollaboratorUidArray(testEmailArr);

    expect(testUidArr).toEqual(expectedUidArr);
  });
});

describe('moveCurUserEmailToFront tests', () => {
  test('No users (error is caught in getUserEmailArrFromUserUidArr)', () => {
    const expectedEmailArr = [mockCurUserEmail];
    const testEmailInputArr = [];

    const testEmailOutputArr = TripUtils.moveCurUserEmailToFront(testEmailInputArr);

    expect(testEmailOutputArr).toEqual(expectedEmailArr);
  });

  test('Only the current user', () => {
    const expectedEmailArr = [mockCurUserEmail];
    const testEmailInputArr = [mockCurUserEmail];

    const testEmailOutputArr = TripUtils.moveCurUserEmailToFront(testEmailInputArr);

    expect(testEmailOutputArr).toEqual(expectedEmailArr);
  });

  test('Current user between two others (alphabetical order)', () => {
    const expectedEmailArr = [mockCurUserEmail, USER_A_EMAIL, USER_Z_EMAIL];
    const testEmailInputArr = [USER_A_EMAIL, mockCurUserEmail, USER_Z_EMAIL];

    const testEmailOutputArr = TripUtils.moveCurUserEmailToFront(testEmailInputArr);

    expect(testEmailOutputArr).toEqual(expectedEmailArr);
  });
});
<|endoftext|>


<commit_before>var getContext = function(display, infos) {
   var localLanguageStrings = {
      fr: {
         label: {
            fillCanvas: "remplir avec",
            setFill: "définir la couleur de fond à",
            setStroke: "définir le contour à",
            drawRectangle: "dessiner un rectangle",
            drawEllipse: "dessiner une ellipse"
         },
         code: {
            fillCanvas: "remplirToile",
            setFill: "definirFond",
            setStroke: "definirContour",
            drawRectangle: "dessinerRectangle",
            drawEllipse: "dessinerEllipse"
         },
         description: {
         },
         startingBlockName: "Programme",
         messages: {
         }
      },
      none: {
         comment: {}
      }
   }

   window.stringsLanguage = window.stringsLanguage || "fr";
   window.languageStrings = window.languageStrings || {};

   if (typeof window.languageStrings != "object") {
      console.error("window.languageStrings is not an object");
   }
   else { // merge translations
      $.extend(true, window.languageStrings, localLanguageStrings[window.stringsLanguage]);
   }
   var strings = window.languageStrings;

   var cells = [];
   var texts = [];
   var scale = 1;

   var context = {
      display: display,
      infos: infos,
      strings: strings,
      localLanguageStrings: localLanguageStrings,
      processing: {
         ops: []
      }
   };

   context.changeDelay = function(newDelay) {
      infos.actionDelay = newDelay;
   };

   context.waitDelay = function(callback, value) {
      context.runner.waitDelay(callback, value, infos.actionDelay);
   };

   context.callCallback = function(callback, value) { // Default implementation
      context.runner.noDelay(callback, value);
   };

   context.nbRobots = 1;

   context.getRobotItem = function(iRobot) {
      var items = context.getItems(undefined, undefined, {category: "robot"});
      return items[iRobot];
   };

   context.program_end = function(callback) {
      var curRobot = context.curRobot;
      if (!context.programEnded[curRobot]) {
         context.programEnded[curRobot] = true;
         infos.checkEndCondition(context, true);
      }
      context.waitDelay(callback);
   };

   context.reset = function(taskInfos) {
      if (context.display) {
         context.resetDisplay();
      }
   };

   function sketchProc(processing) {
      processing.setup = function() {
         processing.size(300, 300);
         processing.frameRate(10);
         processing.background(255);
      };

      processing.draw = function() {
         for (var iOp = 0; iOp < context.processing.ops.length; iOp++) {
            var op = context.processing.ops[iOp];
            processing[op.func].apply(null, op.args);
         }
      };
   }

   context.resetDisplay = function() {
      var canvas = $('<canvas>').css('border', '1px solid black');
      $('#grid').empty().append(canvas);

      var processingInstance = new Processing(canvas.get(0), sketchProc);

      context.blocklyHelper.updateSize();
      context.updateScale();
   };

   context.unload = function() {
      if (context.display) {
      }
   };

   context.processing.fillCanvas = function(color, callback) {
      context.processing.ops.push({ func: 'background', args: [color] });
      context.waitDelay(callback);
   };

   context.processing.setFill = function(color, callback) {
      context.processing.ops.push({ func: 'fill', args: [color] });
      context.waitDelay(callback);
   };

   context.processing.setStroke = function(color, width, callback) {
      context.processing.ops.push({ func: 'stroke', args: [color] });
      context.processing.ops.push({ func: 'strokeWeight', args: [width] });
      context.waitDelay(callback);
   };

   context.processing.drawRectangle = function(p1, p2, callback) {
      context.processing.ops.push({ func: 'rect', args: [p1.x, p1.y, p2.x, p2.y] });
      context.waitDelay(callback);
   };

   context.processing.drawEllipse = function(center, rads, callback) {
      context.processing.ops.push({ func: 'ellipse', args: [center.x, center.y, rads.x, rads.y] });
      context.waitDelay(callback);
   };

   context.debug_alert = function(message, callback) {
      message = message ? message.toString() : '';
      if (context.display) {
         alert(message);
      }
      context.callCallback(callback);
   };

   context.customBlocks = {
      processing: {
         draw: [
            { name: "fillCanvas", params: [null] },
            { name: "setFill", params: [null] },
            { name: "setStroke", params: [null, null] },
            { name: "drawRectangle", params: [null, null] },
            { name: "drawEllipse", params: [null, null] }
         ]
      }
   };

   context.getItems = function(row, col, filters) {
      var listItems = [];
      for (var iItem = 0; iItem < context.items.length; iItem++) {
         var item = context.items[iItem];
         var itemType = infos.itemTypes[item.type];
         if ((row == undefined) || ((item.row == row) && (item.col == col))) {
            var accepted = true;
            for (var property in filters) {
               var value = filters[property];
               if ((itemType[property] == undefined) && (value != undefined)) {
                  accepted = false;
                  break;
               }
               if ((itemType[property] != undefined) && (itemType[property] != value)) {
                  accepted = false;
                  break;
               }
            }
            if (accepted) {
               item.index = iItem;
               listItems.push(item);
            }
         }
      }
      return listItems;
   };

   context.updateScale = function() {
      if (!context.display) {
         return;
      }
   };

   return context;
}
<commit_msg>blocklyProcessing: fix arguments order<commit_after>var getContext = function(display, infos) {
   var localLanguageStrings = {
      fr: {
         label: {
            fillCanvas: "remplir avec",
            setFill: "définir la couleur de fond à",
            setStroke: "définir le contour à",
            drawRectangle: "dessiner un rectangle",
            drawEllipse: "dessiner une ellipse"
         },
         code: {
            fillCanvas: "remplirToile",
            setFill: "definirFond",
            setStroke: "definirContour",
            drawRectangle: "dessinerRectangle",
            drawEllipse: "dessinerEllipse"
         },
         description: {
         },
         startingBlockName: "Programme",
         messages: {
         }
      },
      none: {
         comment: {}
      }
   }

   window.stringsLanguage = window.stringsLanguage || "fr";
   window.languageStrings = window.languageStrings || {};

   if (typeof window.languageStrings != "object") {
      console.error("window.languageStrings is not an object");
   }
   else { // merge translations
      $.extend(true, window.languageStrings, localLanguageStrings[window.stringsLanguage]);
   }
   var strings = window.languageStrings;

   var cells = [];
   var texts = [];
   var scale = 1;

   var context = {
      display: display,
      infos: infos,
      strings: strings,
      localLanguageStrings: localLanguageStrings,
      processing: {
         ops: []
      }
   };

   context.changeDelay = function(newDelay) {
      infos.actionDelay = newDelay;
   };

   context.waitDelay = function(callback, value) {
      context.runner.waitDelay(callback, value, infos.actionDelay);
   };

   context.callCallback = function(callback, value) { // Default implementation
      context.runner.noDelay(callback, value);
   };

   context.nbRobots = 1;

   context.getRobotItem = function(iRobot) {
      var items = context.getItems(undefined, undefined, {category: "robot"});
      return items[iRobot];
   };

   context.program_end = function(callback) {
      var curRobot = context.curRobot;
      if (!context.programEnded[curRobot]) {
         context.programEnded[curRobot] = true;
         infos.checkEndCondition(context, true);
      }
      context.waitDelay(callback);
   };

   context.reset = function(taskInfos) {
      if (context.display) {
         context.resetDisplay();
      }
   };

   function sketchProc(processing) {
      processing.setup = function() {
         processing.size(300, 300);
         processing.frameRate(10);
         processing.background(255);
      };

      processing.draw = function() {
         for (var iOp = 0; iOp < context.processing.ops.length; iOp++) {
            var op = context.processing.ops[iOp];
            processing[op.func].apply(null, op.args);
         }
      };
   }

   context.resetDisplay = function() {
      var canvas = $('<canvas>').css('border', '1px solid black');
      $('#grid').empty().append(canvas);

      var processingInstance = new Processing(canvas.get(0), sketchProc);

      context.blocklyHelper.updateSize();
      context.updateScale();
   };

   context.unload = function() {
      if (context.display) {
      }
   };

   context.processing.fillCanvas = function(callback, color) {
      context.processing.ops.push({ func: 'background', args: [color] });
      context.waitDelay(callback);
   };

   context.processing.setFill = function(callback, color) {
      context.processing.ops.push({ func: 'fill', args: [color] });
      context.waitDelay(callback);
   };

   context.processing.setStroke = function(callback, color, width) {
      context.processing.ops.push({ func: 'stroke', args: [color] });
      context.processing.ops.push({ func: 'strokeWeight', args: [width] });
      context.waitDelay(callback);
   };

   context.processing.drawRectangle = function(callback, p1, p2) {
      context.processing.ops.push({ func: 'rect', args: [p1.x, p1.y, p2.x, p2.y] });
      context.waitDelay(callback);
   };

   context.processing.drawEllipse = function(callback, center, rads) {
      context.processing.ops.push({ func: 'ellipse', args: [center.x, center.y, rads.x, rads.y] });
      context.waitDelay(callback);
   };

   context.debug_alert = function(message, callback) {
      message = message ? message.toString() : '';
      if (context.display) {
         alert(message);
      }
      context.callCallback(callback);
   };

   context.customBlocks = {
      processing: {
         draw: [
            { name: "fillCanvas", params: [null] },
            { name: "setFill", params: [null] },
            { name: "setStroke", params: [null, null] },
            { name: "drawRectangle", params: [null, null] },
            { name: "drawEllipse", params: [null, null] }
         ]
      }
   };

   context.getItems = function(row, col, filters) {
      var listItems = [];
      for (var iItem = 0; iItem < context.items.length; iItem++) {
         var item = context.items[iItem];
         var itemType = infos.itemTypes[item.type];
         if ((row == undefined) || ((item.row == row) && (item.col == col))) {
            var accepted = true;
            for (var property in filters) {
               var value = filters[property];
               if ((itemType[property] == undefined) && (value != undefined)) {
                  accepted = false;
                  break;
               }
               if ((itemType[property] != undefined) && (itemType[property] != value)) {
                  accepted = false;
                  break;
               }
            }
            if (accepted) {
               item.index = iItem;
               listItems.push(item);
            }
         }
      }
      return listItems;
   };

   context.updateScale = function() {
      if (!context.display) {
         return;
      }
   };

   return context;
}
<|endoftext|>


<commit_before>/**
 * @class CM_FormField_Set
 * @extends CM_FormField_Abstract
 */
var CM_FormField_Set = CM_FormField_Abstract.extend({
  _class: 'CM_FormField_Set',

  events: {
    'change input': function() {
      this.trigger('change');
    }
  },

  getInput: function() {
    return this.$(':checkbox[name="' + this.options.params.name + '[]"]');
  },

  getValue: function() {
    var array = this.getInput().filter(':not([disabled])').map(function() {
      var $this = $(this);
      if (!$this.is(':checkbox') || $this.is(':checked')) {
        return $(this).val();
      }
      return null;
    }).get();
    return _.compact(array);
  },

  /**
   * @param {Array} values
   */
  setValue: function(values) {
    this.getInput().removeAttr('checked');
    _.each(values, function(value) {
      this.getInputByValue(value).prop('checked', 'checked');
    }, this);
  },

  /**
   * @param {*} value
   * @returns {jQuery}
   */
  getInputByValue: function(value) {
    var $input = this.getInput().filter('[value=' + value + ']');
    if (!$input.length) {
      throw new CM_Exception('Invalid value `' + value + '` for `' + this.getName() + '` form field');
    }
    return $input;
  }
});
<commit_msg>Simplify CM_FormField_Set.getValue()<commit_after>/**
 * @class CM_FormField_Set
 * @extends CM_FormField_Abstract
 */
var CM_FormField_Set = CM_FormField_Abstract.extend({
  _class: 'CM_FormField_Set',

  events: {
    'change input': function() {
      this.trigger('change');
    }
  },

  getInput: function() {
    return this.$(':checkbox[name="' + this.options.params.name + '[]"]');
  },

  getValue: function() {
    return this.getInput().filter(':enabled:checked').get().map(function(element) {
      return $(element).val();
    });
  },

  /**
   * @param {Array} values
   */
  setValue: function(values) {
    this.getInput().removeAttr('checked');
    _.each(values, function(value) {
      this.getInputByValue(value).prop('checked', 'checked');
    }, this);
  },

  /**
   * @param {*} value
   * @returns {jQuery}
   */
  getInputByValue: function(value) {
    var $input = this.getInput().filter('[value=' + value + ']');
    if (!$input.length) {
      throw new CM_Exception('Invalid value `' + value + '` for `' + this.getName() + '` form field');
    }
    return $input;
  }
});
<|endoftext|>


<commit_before>Meteor.methods({
  loginWithLdap: function (request) {
    if(!Meteor.settings.ldap) {
      throw new Error("LDAP settings missing.");
    }
    console.log ('LDAP authentication for ' + request.username);
    var ldap = Npm.require('ldapjs');
    var Future = Npm.require('fibers/future');
    var assert = Npm.require('assert');
    var client = ldap.createClient({
      url: Meteor.settings.ldap.server_url
    });

    var userDN = request.username+'@'+Meteor.settings.ldap.server_dn;
    var bindFuture = new Future();

    //Bind to the LDAP server.
    client.bind(userDN, request.password, function (err) {
      console.log ('Callback from binding LDAP:');
      if (err) {
        console.log('LDAP bind failed with error');
        console.log({dn: err.dn, code: err.code, name: err.name, message: err.message});
        bindFuture.return(false);
      } else {
        bindFuture.return(true);
      }
    });

    var success = bindFuture.wait();
    if (!success || request.password === '') {
      throw new Meteor.Error(403, "Invalid credentials");
    }

    var opts = {
      filter: '(&(cn='+request.username+')(objectClass=user))',
      scope: 'sub'
    };
    var searchFuture = new Future();
    var userObj = {};

    //Searching to retrieve the other fields for our user.
    client.search(Meteor.settings.ldap.server_dc, opts, function(err, res) {
      assert.ifError(err);
      res.on('searchEntry', function(entry) {
        userObj = _.extend({username: request.username},_.pick(entry.object, Meteor.settings.ldap.whiteListedFields));
        searchFuture.return(true);
      });

      res.on('error', function(err) {
        console.error('error: ' + err.message);
        searchFuture.return(false);
      });

      res.on('end', function(result) {
        console.log('status: ' + result.status);
      });
    });

    //Add the user to users, or update the user if it already exists.
    var userId;
    var user = Meteor.users.findOne({username: request.username});

    if (user) {
      userId = user._id;
      Meteor.users.update(userId, {$set: userObj});
    } else {
      userId = Meteor.users.insert(userObj);
    }
    var stampedToken = Accounts._generateStampedLoginToken();
    Meteor.users.update(userId,
      {$push: {'services.resume.loginTokens': stampedToken}}
    );

    this.setUserId(userId);

    return {
      id: userId,
      token: stampedToken.token,
      tokenExpires: Accounts._tokenExpiration(stampedToken.when)
    };
  }
});

<commit_msg>Fixed a bug where the user would insert before the LDAP search finished.<commit_after>Meteor.methods({
  loginWithLdap: function (request) {
    if(!Meteor.settings.ldap) {
      throw new Error("LDAP settings missing.");
    }
    console.log ('LDAP authentication for ' + request.username);
    var ldap = Npm.require('ldapjs');
    var Future = Npm.require('fibers/future');
    var assert = Npm.require('assert');
    var client = ldap.createClient({
      url: Meteor.settings.ldap.server_url
    });

    var userDN = request.username+'@'+Meteor.settings.ldap.server_dn;
    var bindFuture = new Future();

    //Bind to the LDAP server.
    client.bind(userDN, request.password, function (err) {
      console.log ('Callback from binding LDAP:');
      if (err) {
        console.log('LDAP bind failed with error');
        console.log({dn: err.dn, code: err.code, name: err.name, message: err.message});
        bindFuture.return(false);
      } else {
        bindFuture.return(true);
      }
    });

    var success = bindFuture.wait();
    if (!success || request.password === '') {
      throw new Meteor.Error(403, "Invalid credentials");
    }

    var opts = {
      filter: '(&(cn='+request.username+')(objectClass=user))',
      scope: 'sub'
    };
    var searchFuture = new Future();
    var userObj = {};

    //Searching to retrieve the other fields for our user.
    client.search(Meteor.settings.ldap.server_dc, opts, function(err, res) {
      assert.ifError(err);
      res.on('searchEntry', function(entry) {
        userObj = _.extend({username: request.username},_.pick(entry.object, Meteor.settings.ldap.whiteListedFields));
        searchFuture.return(true);
      });

      res.on('error', function(err) {
        console.error('error: ' + err.message);
        searchFuture.return(false);
      });

      res.on('end', function(result) {
        console.log('status: ' + result.status);
      });
    });
    var searchSuccess = searchFuture.wait();
    //Add the user to users, or update the user if it already exists.
    var userId;
    var user = Meteor.users.findOne({username: request.username});
    if (user) {
      userId = user._id;
      Meteor.users.update(userId, {$set: userObj});
    } else {
      userId = Meteor.users.insert(userObj);
    }
    var stampedToken = Accounts._generateStampedLoginToken();
    Meteor.users.update(userId,
      {$push: {'services.resume.loginTokens': stampedToken}}
    );

    this.setUserId(userId);

    return {
      id: userId,
      token: stampedToken.token,
      tokenExpires: Accounts._tokenExpiration(stampedToken.when)
    };
  }
});

<|endoftext|>


<commit_before>/*jshint boss: true */

var leap = require('leapjs');
var _ = require('underscore');
var direction = require('curtsy');
var EventEmitter = require('events').EventEmitter;

var emitter = new EventEmitter();
module.exports = emitter;

var controller;

// make sure we only send the takeoff and land commands once
var flying = false;

// calibrate the 1st time there is a hand
var calibration = null;

function checkGesture(gesture) {
  return gesture.progress > 1.9;
}

// get only the circle gesture from the gestures array
function getGesture(gestures, type) {
  if (!gestures.length) return;
  var types = _.pluck(gestures, 'type');
  var index = types.indexOf(type);
  if (index > -1) return gestures[index];
}

function takeoffOrLand(gesture) {
  var dir = direction(gesture).type;
  if (dir === 'clockwise' && !flying) {
    emitter.emit('takeoff');
    flying = !flying;
  } else if (dir === 'counter-clockwise' && flying) {
    emitter.emit('land');
    flying = !flying;
  }
}

function calibrate(frame) {
  if (frame.hands.length !== 1) return;
  if (!calibrate._first) return (calibrate._first = frame);
  if ((frame.id - calibrate._first.id) < 150) return;
  var hand = frame.hands[0];

  calibration = {
    position: hand.palmPosition,
    palm: hand.palmNormal
  };
  console.log('calibrated!', calibration);
}

function control(hand) {
  // hand#palmNormal
  // array of 3 numbers
  // 1.
  //  * 0.0 < 0.2 -> normal
  //  * 0.2 - 0.8 -> left
  //  * 0.0 < -0.6 -> right
  // 2. ?
  // 3.
  //  * -0.19 - 0.19 -> normal
  //  * 0.2 - 0.7 -> forward
  //  * -0.2 - -0.9 -> back
  //console.log('hand#palmNormal', hand.palmNormal);

  // palmPosition Y -> mm of height
  //console.log('hand#palmPosition', hand.palmPosition);
}

function processFrame(frame) {
  if (!frame.valid) return;
  if (!calibration) return calibrate(frame);
  var circleGest = getGesture(frame.gestures, 'circle');
  if (circleGest && checkGesture(circleGest)) takeoffOrLand(circleGest);

  var hand = frame.hands[0];
  if (hand) control(hand);
}

function start() {
  controller = new leap.Controller({
    frameEventName: 'deviceFrame',
    enableGestures: true
  });
  controller.on('frame', processFrame);
  controller.connect();
}

emitter._getGesture = getGesture;
emitter.start = start;
<commit_msg>normalise calibration<commit_after>/*jshint boss: true */

var leap = require('leapjs');
var _ = require('underscore');
var direction = require('curtsy');
var EventEmitter = require('events').EventEmitter;

var emitter = new EventEmitter();
module.exports = emitter;

var controller;

// make sure we only send the takeoff and land commands once
var flying = false;

// calibrate the 1st time there is a hand
var calibration = null;

function checkGesture(gesture) {
  return gesture.progress > 1.9;
}

// get only the circle gesture from the gestures array
function getGesture(gestures, type) {
  if (!gestures.length) return;
  var types = _.pluck(gestures, 'type');
  var index = types.indexOf(type);
  if (index > -1) return gestures[index];
}

function takeoffOrLand(gesture) {
  var dir = direction(gesture).type;
  if (dir === 'clockwise' && !flying) {
    emitter.emit('takeoff');
    flying = !flying;
  } else if (dir === 'counter-clockwise' && flying) {
    emitter.emit('land');
    flying = !flying;
  }
}

function calibrate(frame) {
  if (frame.hands.length !== 1) return;
  if (!calibrate._first) return (calibrate._first = frame);
  if ((frame.id - calibrate._first.id) < 150) return;
  var hand = frame.hands[0];

  calibration = {
    ver: normalisePP(hand.palmPosition[1]),
    hor: normalisePP(hand.palmPosition[0]),

    lat: normalise(hand.palmNormal[0]),
    lon: normalise(hand.palmNormal[1])
  };
  console.log('calibrated!', calibration);
}

// make sure the function is not called continuously
var hover = _.throttle(function hover () {
  emitter.emit('stop');
  console.log('HOVER');
}, 500);

function getFrontBack() {
}

function getLeftRight() {
}

function getUpDown() {
}

function getTurn() {
}

var control = _.throttle(function control(hand) {
  // hand#palmNormal
  // array of 3 numbers
  // X.
  //  * 0.0 < 0.2 -> normal
  //  * 0.2 - 0.8 -> left
  //  * 0.0 < -0.6 -> right
  // Y. N/A
  // Z.
  //  * -0.19 - 0.19 -> normal
  //  * 0.2 - 0.7 -> forward
  //  * -0.2 - -0.9 -> back
  //console.log('hand#palmNormal', hand.palmNormal);

  // palmPosition.X -> negative == counterClockwise
  // palmPosition.X -> positive == clockwise
  // palmPosition Y -> mm of height
  //console.log('hand#palmPosition', hand.palmPosition);
}, 30);

function processFrame(frame) {
  if (!frame.valid) return;
  if (!calibration) return calibrate(frame);
  var circleGest = getGesture(frame.gestures, 'circle');
  if (circleGest && checkGesture(circleGest)) takeoffOrLand(circleGest);

  var hand = frame.hands[0];
  if (hand) control(hand);
  else if (controller.frame(5).hands.length < 1) hover();
}

function start() {
  controller = new leap.Controller({
    frameEventName: 'deviceFrame',
    enableGestures: true
  });
  controller.on('frame', processFrame);
  controller.connect();
}

emitter._getGesture = getGesture;
emitter.start = start;

function isSimilar(value, compare, tolerance) {
  tolerance = tolerance || 10;
  return (Math.abs(value - compare) <= tolerance);
}

function isMiddle(value) {
  return (value > 450 && value < 573);
}

function normalisePP(value) {
  return parseInt(value / 10, 10);
}

function normalise(value) {
  return parseInt(1000 * value, 10);
}

function scale(value) {
  if (isMiddle(value)) return 0;
  if (value <= 450) return (value - 450) / 450;
  if (value >= 573) return (value - 573) / 450;
}
<|endoftext|>


<commit_before>'use strict';
var async = require('async');
var _ = require('lodash');

exports.traverse = function (mongoose, cb) {
    var Schema = mongoose.Schema;
    var ObjectId = Schema.Types.ObjectId;
    var Publisher = mongoose.model('Publisher', new Schema({
        name: String,
        chapters: [
            {type: ObjectId}
        ]
    }, { collection: 'publishers' }));
    var Chapter = mongoose.model('Chapter', new Schema({
        name: String,
        topics: [
            {type: ObjectId}
        ],
        icon: String
    }));
    var Topic = mongoose.model('Topic', new Schema({
        name: String,
        tasks: [
            {type: ObjectId}
        ]
    }));
    var Task = mongoose.model('Task', new Schema({
        type: String,
        activities: [
            {type: ObjectId}
        ]
    }));
    var Activity = mongoose.model('Activity', new Schema({
        name: String,
        videos: [
            {type: ObjectId}
        ]
    }));
    var Video = mongoose.model('Video', new Schema({
        type: String,
        url: String
    }));

    var target = [];
    Publisher.findOne({name: '人民教育出版社'}, function (err, publisher) {
        Chapter.find({_id: {$in: publisher.chapters}}, function (err, chapters) {
            async.each(chapters, function (chapter, callback) {
                Video.find({_id: chapter._doc.guideVideo}, function(err, gv){
                    if (gv[0]){
                        target.push({
                            chapter: chapter.name,
                            topic: "章节引入",
                            topicIcon: "guide.png",
                            task: "guide",
                            activity: chapter.name + '的引入',
                            actThumbnail: chapter.icon,
                            video: gv[0].url
                        });
                    }
                });
                Topic.find({_id: {$in: chapter.topics}}, function (err, topics) {
                    async.each(topics, function (topic, callback) {
                        Task.find({_id: {$in: topic.tasks}}, function (err, tasks) {
                            async.each(tasks, function (task, callback) {
                                Activity.find({_id: {$in: task.activities}}, function (err, activities) {
                                    async.each(activities, function (activity, callback) {
                                        Video.find({_id: {$in: activity.videos}, type: 'main'}, function (err, videos) {
                                            _.forEach(videos, function (video) {
                                                target.push({chapter: chapter.name, topic: topic.name, topicIcon: topic._doc.icon, task: task.type,
                                                    activity: activity.name, actThumbnail: activity._doc.thumbnail, video: video.url});
                                            });
                                            callback();
                                        });
                                    }, callback);
                                });
                            }, callback);
                        });
                    }, callback);
                });
            }, function () {
                cb(target);
            });
        });
    });
}
<commit_msg>[ronfe] update chapter schema to include guide videos<commit_after>'use strict';
var async = require('async');
var _ = require('lodash');

exports.traverse = function (mongoose, cb) {
    var Schema = mongoose.Schema;
    var ObjectId = Schema.Types.ObjectId;
    var Publisher = mongoose.model('Publisher', new Schema({
        name: String,
        chapters: [
            {type: ObjectId}
        ]
    }, { collection: 'publishers' }));
    var Chapter = mongoose.model('Chapter', new Schema({
        name: String,
        topics: [
            {type: ObjectId}
        ],
        icon: String,
        guideVideo: String
    }));
    var Topic = mongoose.model('Topic', new Schema({
        name: String,
        tasks: [
            {type: ObjectId}
        ]
    }));
    var Task = mongoose.model('Task', new Schema({
        type: String,
        activities: [
            {type: ObjectId}
        ]
    }));
    var Activity = mongoose.model('Activity', new Schema({
        name: String,
        videos: [
            {type: ObjectId}
        ]
    }));
    var Video = mongoose.model('Video', new Schema({
        type: String,
        url: String
    }));

    var target = [];
    Publisher.findOne({name: '人民教育出版社'}, function (err, publisher) {
        Chapter.find({_id: {$in: publisher.chapters}}, function (err, chapters) {
            async.each(chapters, function (chapter, callback) {
                Video.find({_id: chapter.guideVideo}, function(err, gv){
                    if (gv[0]){
                    //In case of undefined or null guide videos
                        target.push({
                            chapter: chapter.name,
                            topic: "章节引入",
                            topicIcon: "guide.png",
                            task: "guide",
                            activity: chapter.name + '的引入',
                            actThumbnail: chapter.icon,
                            video: gv[0].url
                        });
                    }
                });
                Topic.find({_id: {$in: chapter.topics}}, function (err, topics) {
                    async.each(topics, function (topic, callback) {
                        Task.find({_id: {$in: topic.tasks}}, function (err, tasks) {
                            async.each(tasks, function (task, callback) {
                                Activity.find({_id: {$in: task.activities}}, function (err, activities) {
                                    async.each(activities, function (activity, callback) {
                                        Video.find({_id: {$in: activity.videos}, type: 'main'}, function (err, videos) {
                                            _.forEach(videos, function (video) {
                                                target.push({chapter: chapter.name, topic: topic.name, topicIcon: topic._doc.icon, task: task.type,
                                                    activity: activity.name, actThumbnail: activity._doc.thumbnail, video: video.url});
                                            });
                                            callback();
                                        });
                                    }, callback);
                                });
                            }, callback);
                        });
                    }, callback);
                });
            }, function () {
                cb(target);
            });
        });
    });
}
<|endoftext|>


<commit_before>function getMonzo() {
  return {
    // general
    name: "Monzo Account",
    provider: "monzo",

    // auth
    authType: "oauth2",
    accessToken: null,
    accessExpires: -1,
    // variables below used while auth is being negotiated
    clientId: null,
    clientSecret: null,
    state: null,
    redirectUri: null,

    // other variables
    accountId: null,
    cardId: null
  };
}

// auth functions
function monzoAuth(monzo) {
  if (monzo.clientId == null) {
    throw new Error("clientId isn't set and yet auth was called!");
  }
  if (monzo.clientSecret == null) {
    throw new Error("clientSecret isn't set and yet auth was called!");
  }
  if (monzo.state == null) {
    throw new Error("state isn't set and yet auth was called!");
  }
  if (monzo.redirectUri == null) {
    throw new Error("redirectUri isn't set and yet auth was called!");
  }
  window.location = "https://auth.getmondo.co.uk/?client_id=" + monzo.clientId + "&redirect_uri=" + monzo.redirectUri + "&response_type=code&state=" + monzo.state;
}

function monzoCallback(monzo, authCode) {
  if (monzo.clientId == null) {
    throw new Error("clientId isn't set and yet callback was called!");
  }
  if (monzo.clientSecret == null) {
    throw new Error("clientSecret isn't set and yet callback was called!");
  }
  if (monzo.state == null) {
    throw new Error("state isn't set and yet callback was called!");
  }
  if (monzo.redirectUri == null) {
    throw new Error("redirectUri isn't set and yet callback was called!");
  }

  var request = new XMLHttpRequest();
  request.open("POST", "https://api.monzo.com/oauth2/token", false);
  request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  request.send("grant_type=authorization_code&client_id=" + monzo.clientId + "&client_secret=" + monzo.clientSecret + "&redirect_uri=" + monzo.redirectUri + "&code=" + authCode);
  if (request.status == 200) {
    monzo.accessToken = JSON.parse(request.responseText).access_token;
    monzo.accessExpires = Date.now() + JSON.parse(request.responseText).expires_in * 1000;
    // clear the variables we were using while getting an access token
    delete monzo["clientId"];
    delete monzo["clientSecret"];
    delete monzo["redirectUri"];
    delete monzo["state"];
    // get the account id
    var request = new XMLHttpRequest();
    request.open("GET", "https://api.monzo.com/accounts", false);
    request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
    request.send();
    monzo.accountId = JSON.parse(request.responseText).accounts[0].id;
    monzo.cardId = monzoGetCards(monzo).id;
  }
}

// HTML functions
function monzoTransactionHTML(monzo, id) {
  // alasql
  try {
    alasql("drop table transactions")
  } catch(err) {
    console.log(err.message);
  }
  alasql('CREATE TABLE transactions (' +
    'id STRING PRIMARY KEY,' +
    'created Date,' +
    'settled Date,' +
    'is_load BOOLEAN,' +
    'merchant_id STRING,' +
    'description STRING,' +
    'currency STRING,' +
    'amount INT,' +
    'account_balance INT,' +
    'category STRING,' +
    'notes STRING);');
  alasql('CREATE TABLE merchants (' +
    'id STRING PRIMARY KEY,' +
    'name STRING,' +
    'address STRING,' +
    'city STRING,' +
    'region STRING,' +
    'country STRING,' +
    'postcode STRING,' +
    'latitude DOUBLE PRECISION,' +
    'longitude DOUBLE PRECISION,' +
    'zoom_level INT,' +
    'created Date,' +
    'group_id STRING,' +
    'logo STRING,' +
    'emoji STRING,' +
    'category STRING);');
  // create a fake merchant for top-ups
  alasql('INSERT INTO merchants VALUES("merch_monzo", "Monzo", null, null, null, null, null, null, null, null, null, null, null, null, null);');

  transactions = monzoGetTransactions(monzo, true);
  for (var i = 0; i < transactions.length; i++) {
    var merchantId;
    if (transactions[i].merchant == null) {
      merchantId = null;
      if (transactions[i].is_load) {
        merchantId = "merch_monzo";
      }
    } else {
      merchantId = transactions[i].merchant.id;
      if (alasql('SELECT id from merchants where id="' + merchantId + '"').length == 0) {
        // add merchant to alasql
        alasql('INSERT INTO merchants VALUES(' +
          '"' + transactions[i].merchant.id + '"' + ',' +
          '"' + transactions[i].merchant.name + '"' + ',' +
          '"' + transactions[i].merchant.address.address + '"' + ',' +
          '"' + transactions[i].merchant.address.city + '"' + ',' +
          '"' + transactions[i].merchant.address.region + '"' + ',' +
          '"' + transactions[i].merchant.address.country + '"' + ',' +
          '"' + transactions[i].merchant.address.postcode + '"' + ',' +
          transactions[i].merchant.address.latitude + ',' +
          transactions[i].merchant.address.longitude + ',' +
          transactions[i].merchant.address.zoom_level + ',' +
          '"' + transactions[i].merchant.created + '"' + ',' +
          '"' + transactions[i].merchant.group_id + '"' + ',' +
          '"' + transactions[i].merchant.logo + '"' + ',' +
          '"' + transactions[i].merchant.emoji + '"' + ',' +
          '"' + transactions[i].merchant.category + '");');
      }
    }
    alasql('INSERT INTO transactions VALUES(' +
      '"' + transactions[i].id + '"' + ',' +
      '"' + transactions[i].created + '"' + ',' +
      '"' + transactions[i].settled + '"' + ',' +
      transactions[i].is_load + ',' +
      '"' + merchantId + '"' + ',' +
      '"' + transactions[i].description + '"' + ',' +
      '"' + transactions[i].currency + '"' + ',' +
      transactions[i].amount + ',' +
      transactions[i].account_balance + ',' +
      '"' + transactions[i].category + '"' + ',' +
      '"' + transactions[i].notes + '"' + ');');
  }

  var HTML = "";
  HTML += "
" + monzo.name + "
";
  HTML += '<textarea id="sql"></textarea><button type="button" onclick="javascript:monzoSql(document.getElementById(\'sql\').value)" id="searchButton">Search!</button>';
  HTML += '

';
  return HTML;
}

function monzoDetailsHTML(monzo, id) {
  var HTML = "";
  var balance = monzoGetBalance(monzo);
  var card = monzoGetCards(monzo);
  HTML += "
Welcome, " + card.name + "!
";
  HTML += "
Balance: £" + (balance.balance*0.01).toFixed(2) + "
";
  HTML += "
You spent £" + (balance.spend_today*0.01).toFixed(2) + " today.
";
  if (card.status == "ACTIVE") {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") is currently active.
";
    HTML += 'Freeze your card
';
  } else if (card.status == "INACTIVE") {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") is currently frozen.
";
    HTML += 'Defrost your card
';
  } else {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") has unknown status!
";
  }
  return HTML;
}

// other functions
function monzoGetBalance(monzo) {
  var request = new XMLHttpRequest();
  request.open("GET", "https://api.monzo.com/balance?account_id=" + monzo.accountId, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText);
}

function monzoGetCards(monzo) {
  var request = new XMLHttpRequest();
  request.open("GET", "https://api.monzo.com/card/list?account_id=" + monzo.accountId, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText).cards[0];
}

function monzoGetTransactions(monzo, expand) {
  if (typeof expand === "undefined") { expand = false; }
  var request = new XMLHttpRequest();
  if (expand) {
    request.open("GET", "https://api.monzo.com/transactions?account_id=" + monzo.accountId + "&expand[]=merchant", false);
  } else {
    request.open("GET", "https://api.monzo.com/transactions?account_id=" + monzo.accountId, false);
  }
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText).transactions;
}

function monzoFreezeCard(monzo, targetStatus, id) {
  var request = new XMLHttpRequest();
  request.open("PUT", "https://api.monzo.com/card/toggle?card_id=" + monzo.cardId + "&status=" + targetStatus, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  document.getElementById("accountDetails").innerHTML = monzoDetailsHTML(monzo, id)
}

function monzoSql(query) {
  alasql(query.replace(' from ', ' INTO HTML("#transactions", {headers:true}) from '));
}<commit_msg>Removed merchant created from merchants table<commit_after>function getMonzo() {
  return {
    // general
    name: "Monzo Account",
    provider: "monzo",

    // auth
    authType: "oauth2",
    accessToken: null,
    accessExpires: -1,
    // variables below used while auth is being negotiated
    clientId: null,
    clientSecret: null,
    state: null,
    redirectUri: null,

    // other variables
    accountId: null,
    cardId: null
  };
}

// auth functions
function monzoAuth(monzo) {
  if (monzo.clientId == null) {
    throw new Error("clientId isn't set and yet auth was called!");
  }
  if (monzo.clientSecret == null) {
    throw new Error("clientSecret isn't set and yet auth was called!");
  }
  if (monzo.state == null) {
    throw new Error("state isn't set and yet auth was called!");
  }
  if (monzo.redirectUri == null) {
    throw new Error("redirectUri isn't set and yet auth was called!");
  }
  window.location = "https://auth.getmondo.co.uk/?client_id=" + monzo.clientId + "&redirect_uri=" + monzo.redirectUri + "&response_type=code&state=" + monzo.state;
}

function monzoCallback(monzo, authCode) {
  if (monzo.clientId == null) {
    throw new Error("clientId isn't set and yet callback was called!");
  }
  if (monzo.clientSecret == null) {
    throw new Error("clientSecret isn't set and yet callback was called!");
  }
  if (monzo.state == null) {
    throw new Error("state isn't set and yet callback was called!");
  }
  if (monzo.redirectUri == null) {
    throw new Error("redirectUri isn't set and yet callback was called!");
  }

  var request = new XMLHttpRequest();
  request.open("POST", "https://api.monzo.com/oauth2/token", false);
  request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  request.send("grant_type=authorization_code&client_id=" + monzo.clientId + "&client_secret=" + monzo.clientSecret + "&redirect_uri=" + monzo.redirectUri + "&code=" + authCode);
  if (request.status == 200) {
    monzo.accessToken = JSON.parse(request.responseText).access_token;
    monzo.accessExpires = Date.now() + JSON.parse(request.responseText).expires_in * 1000;
    // clear the variables we were using while getting an access token
    delete monzo["clientId"];
    delete monzo["clientSecret"];
    delete monzo["redirectUri"];
    delete monzo["state"];
    // get the account id
    var request = new XMLHttpRequest();
    request.open("GET", "https://api.monzo.com/accounts", false);
    request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
    request.send();
    monzo.accountId = JSON.parse(request.responseText).accounts[0].id;
    monzo.cardId = monzoGetCards(monzo).id;
  }
}

// HTML functions
function monzoTransactionHTML(monzo, id) {
  // alasql
  try {
    alasql("drop table transactions")
  } catch(err) {
    console.log(err.message);
  }
  alasql('CREATE TABLE transactions (' +
    'id STRING PRIMARY KEY,' +
    'created Date,' +
    'settled Date,' +
    'is_load BOOLEAN,' +
    'merchant_id STRING,' +
    'description STRING,' +
    'currency STRING,' +
    'amount INT,' +
    'account_balance INT,' +
    'category STRING,' +
    'notes STRING);');
  alasql('CREATE TABLE merchants (' +
    'id STRING PRIMARY KEY,' +
    'name STRING,' +
    'address STRING,' +
    'city STRING,' +
    'region STRING,' +
    'country STRING,' +
    'postcode STRING,' +
    'latitude DOUBLE PRECISION,' +
    'longitude DOUBLE PRECISION,' +
    'zoom_level INT,' +
    'group_id STRING,' +
    'logo STRING,' +
    'emoji STRING,' +
    'category STRING);');
  // create a fake merchant for top-ups
  alasql('INSERT INTO merchants VALUES("merch_monzo", "Monzo", null, null, null, null, null, null, null, null, null, null, null, null);');

  transactions = monzoGetTransactions(monzo, true);
  for (var i = 0; i < transactions.length; i++) {
    var merchantId;
    if (transactions[i].merchant == null) {
      merchantId = null;
      if (transactions[i].is_load) {
        merchantId = "merch_monzo";
      }
    } else {
      merchantId = transactions[i].merchant.id;
      if (alasql('SELECT id from merchants where id="' + merchantId + '"').length == 0) {
        // add merchant to alasql
        alasql('INSERT INTO merchants VALUES(' +
          '"' + transactions[i].merchant.id + '"' + ',' +
          '"' + transactions[i].merchant.name + '"' + ',' +
          '"' + transactions[i].merchant.address.address + '"' + ',' +
          '"' + transactions[i].merchant.address.city + '"' + ',' +
          '"' + transactions[i].merchant.address.region + '"' + ',' +
          '"' + transactions[i].merchant.address.country + '"' + ',' +
          '"' + transactions[i].merchant.address.postcode + '"' + ',' +
          transactions[i].merchant.address.latitude + ',' +
          transactions[i].merchant.address.longitude + ',' +
          transactions[i].merchant.address.zoom_level + ',' +
          '"' + transactions[i].merchant.group_id + '"' + ',' +
          '"' + transactions[i].merchant.logo + '"' + ',' +
          '"' + transactions[i].merchant.emoji + '"' + ',' +
          '"' + transactions[i].merchant.category + '");');
      }
    }
    alasql('INSERT INTO transactions VALUES(' +
      '"' + transactions[i].id + '"' + ',' +
      '"' + transactions[i].created + '"' + ',' +
      '"' + transactions[i].settled + '"' + ',' +
      transactions[i].is_load + ',' +
      '"' + merchantId + '"' + ',' +
      '"' + transactions[i].description + '"' + ',' +
      '"' + transactions[i].currency + '"' + ',' +
      transactions[i].amount + ',' +
      transactions[i].account_balance + ',' +
      '"' + transactions[i].category + '"' + ',' +
      '"' + transactions[i].notes + '"' + ');');
  }

  var HTML = "";
  HTML += "
" + monzo.name + "
";
  HTML += '<textarea id="sql"></textarea><button type="button" onclick="javascript:monzoSql(document.getElementById(\'sql\').value)" id="searchButton">Search!</button>';
  HTML += '

';
  return HTML;
}

function monzoDetailsHTML(monzo, id) {
  var HTML = "";
  var balance = monzoGetBalance(monzo);
  var card = monzoGetCards(monzo);
  HTML += "
Welcome, " + card.name + "!
";
  HTML += "
Balance: £" + (balance.balance*0.01).toFixed(2) + "
";
  HTML += "
You spent £" + (balance.spend_today*0.01).toFixed(2) + " today.
";
  if (card.status == "ACTIVE") {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") is currently active.
";
    HTML += 'Freeze your card
';
  } else if (card.status == "INACTIVE") {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") is currently frozen.
";
    HTML += 'Defrost your card
';
  } else {
    HTML += "
Your card (last 4 digits " + card.last_digits + ") has unknown status!
";
  }
  return HTML;
}

// other functions
function monzoGetBalance(monzo) {
  var request = new XMLHttpRequest();
  request.open("GET", "https://api.monzo.com/balance?account_id=" + monzo.accountId, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText);
}

function monzoGetCards(monzo) {
  var request = new XMLHttpRequest();
  request.open("GET", "https://api.monzo.com/card/list?account_id=" + monzo.accountId, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText).cards[0];
}

function monzoGetTransactions(monzo, expand) {
  if (typeof expand === "undefined") { expand = false; }
  var request = new XMLHttpRequest();
  if (expand) {
    request.open("GET", "https://api.monzo.com/transactions?account_id=" + monzo.accountId + "&expand[]=merchant", false);
  } else {
    request.open("GET", "https://api.monzo.com/transactions?account_id=" + monzo.accountId, false);
  }
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  return JSON.parse(request.responseText).transactions;
}

function monzoFreezeCard(monzo, targetStatus, id) {
  var request = new XMLHttpRequest();
  request.open("PUT", "https://api.monzo.com/card/toggle?card_id=" + monzo.cardId + "&status=" + targetStatus, false);
  request.setRequestHeader("Authorization", "Bearer " + monzo.accessToken);
  request.send();
  document.getElementById("accountDetails").innerHTML = monzoDetailsHTML(monzo, id)
}

function monzoSql(query) {
  alasql(query.replace(' from ', ' INTO HTML("#transactions", {headers:true}) from '));
}<|endoftext|>


<commit_before>//Required modules
var sparkFunctions = require('./spark-functions.js');

//Morse code array
var charCodes=new Array(37);

charCodes["a"]="._";
charCodes["b"]="_...";
charCodes["c"]="_._.";
charCodes["d"]="_..";
charCodes["e"]=".";
charCodes["f"]=".._.";
charCodes["g"]="__.";
charCodes["h"]="....";
charCodes["i"]="..";
charCodes["j"]=".___";
charCodes["k"]="_._";
charCodes["l"]="._..";
charCodes["m"]="__";
charCodes["n"]="_.";
charCodes["o"]="___";
charCodes["p"]=".__.";
charCodes["q"]="__._";
charCodes["r"]="._.";
charCodes["s"]="...";
charCodes["t"]="_";
charCodes["u"]=".._";
charCodes["v"]="..._";
charCodes["w"]=".__";
charCodes["x"]="_.._";
charCodes["y"]="_.__";
charCodes["z"]="__..";
charCodes["1"]=".____";
charCodes["2"]="..___";
charCodes["3"]="...__";
charCodes["4"]="...._";
charCodes["5"]=".....";
charCodes["6"]="_....";
charCodes["7"]="__...";
charCodes["8"]="___..";
charCodes["9"]="____.";
charCodes["0"]="_____";
charCodes[" "]= " ";

function convertToMorse(text) {
	console.log("In convertToMorse");
	for(it in text) {
		sendToSpark(charCodes[it]);

	}

}

function sendToSpark(letter) {
	//Call dot(), dash() or space() depending on sequence of characters

	for(i = 0; i < letter.length(); i++) {
		console.log("Char: " + letter[i]);
		if(letter[i] == ".")
			sparkFunctions.dot();
		else if(letter[i] == "_")
			sparkFunctions.dash();
		else if(letter[i] == " ")
			sparkFunctions.space();
	}
}

exports.convertToMorse = convertToMorse;<commit_msg>Testing morse conversion<commit_after>//Required modules
var sparkFunctions = require('./spark-functions.js');

//Morse code array
var charCodes=new Array(37);

charCodes["a"]="._";
charCodes["b"]="_...";
charCodes["c"]="_._.";
charCodes["d"]="_..";
charCodes["e"]=".";
charCodes["f"]=".._.";
charCodes["g"]="__.";
charCodes["h"]="....";
charCodes["i"]="..";
charCodes["j"]=".___";
charCodes["k"]="_._";
charCodes["l"]="._..";
charCodes["m"]="__";
charCodes["n"]="_.";
charCodes["o"]="___";
charCodes["p"]=".__.";
charCodes["q"]="__._";
charCodes["r"]="._.";
charCodes["s"]="...";
charCodes["t"]="_";
charCodes["u"]=".._";
charCodes["v"]="..._";
charCodes["w"]=".__";
charCodes["x"]="_.._";
charCodes["y"]="_.__";
charCodes["z"]="__..";
charCodes["1"]=".____";
charCodes["2"]="..___";
charCodes["3"]="...__";
charCodes["4"]="...._";
charCodes["5"]=".....";
charCodes["6"]="_....";
charCodes["7"]="__...";
charCodes["8"]="___..";
charCodes["9"]="____.";
charCodes["0"]="_____";
charCodes[" "]= " ";

function convertToMorse(text) {
	console.log("In convertToMorse");
	for(it in text) {
		sendToSpark(charCodes[it]);

	}

}

function sendToSpark(letter) {
	//Call dot(), dash() or space() depending on sequence of characters

	for(i = 0; i < letter.length(); i++) {
		console.log("Char: " + letter.charAt(i));
		if(letter.charAt(i) == ".")
			sparkFunctions.dot();
		else if(letter.charAt(i) == "_")
			sparkFunctions.dash();
		else if(letter.charAt(i) == " ")
			sparkFunctions.space();
	}
}

exports.convertToMorse = convertToMorse;<|endoftext|>


<commit_before>//Required modules
var sparkFunctions = require('./spark-functions.js');

//Morse code array
var charCodes=new Array(37);

charCodes["a"]="._";
charCodes["b"]="_...";
charCodes["c"]="_._.";
charCodes["d"]="_..";
charCodes["e"]=".";
charCodes["f"]=".._.";
charCodes["g"]="__.";
charCodes["h"]="....";
charCodes["i"]="..";
charCodes["j"]=".___";
charCodes["k"]="_._";
charCodes["l"]="._..";
charCodes["m"]="__";
charCodes["n"]="_.";
charCodes["o"]="___";
charCodes["p"]=".__.";
charCodes["q"]="__._";
charCodes["r"]="._.";
charCodes["s"]="...";
charCodes["t"]="_";
charCodes["u"]=".._";
charCodes["v"]="..._";
charCodes["w"]=".__";
charCodes["x"]="_.._";
charCodes["y"]="_.__";
charCodes["z"]="__..";
charCodes["1"]=".____";
charCodes["2"]="..___";
charCodes["3"]="...__";
charCodes["4"]="...._";
charCodes["5"]=".....";
charCodes["6"]="_....";
charCodes["7"]="__...";
charCodes["8"]="___..";
charCodes["9"]="____.";
charCodes["0"]="_____";
charCodes[" "]= " ";

function convertToMorse(text) {
	console.log("In convertToMorse");
	for(it in text) {
		console.log("convertToMorse: " + String(charCodes[it]));

		//Call dot(), dash() or space() depending on sequence of characters

		for(var i in charCodes[it]) {
			if(i == ".")
				sparkFunctions.dot();
			else if(i == "_")
				sparkFunctions.dash();
			else if(i == " ")
				sparkFunctions.space();
		}

	}

}

exports.convertToMorse = convertToMorse;<commit_msg>Testing morse conversion<commit_after>//Required modules
var sparkFunctions = require('./spark-functions.js');

//Morse code array
var charCodes=new Array(37);

charCodes["a"]="._";
charCodes["b"]="_...";
charCodes["c"]="_._.";
charCodes["d"]="_..";
charCodes["e"]=".";
charCodes["f"]=".._.";
charCodes["g"]="__.";
charCodes["h"]="....";
charCodes["i"]="..";
charCodes["j"]=".___";
charCodes["k"]="_._";
charCodes["l"]="._..";
charCodes["m"]="__";
charCodes["n"]="_.";
charCodes["o"]="___";
charCodes["p"]=".__.";
charCodes["q"]="__._";
charCodes["r"]="._.";
charCodes["s"]="...";
charCodes["t"]="_";
charCodes["u"]=".._";
charCodes["v"]="..._";
charCodes["w"]=".__";
charCodes["x"]="_.._";
charCodes["y"]="_.__";
charCodes["z"]="__..";
charCodes["1"]=".____";
charCodes["2"]="..___";
charCodes["3"]="...__";
charCodes["4"]="...._";
charCodes["5"]=".....";
charCodes["6"]="_....";
charCodes["7"]="__...";
charCodes["8"]="___..";
charCodes["9"]="____.";
charCodes["0"]="_____";
charCodes[" "]= " ";

function convertToMorse(text) {
	console.log("In convertToMorse");
	for(it in text) {
		console.log(it);
		console.log("convertToMorse: " + String(charCodes[it]));

		//Call dot(), dash() or space() depending on sequence of characters

		for(var i in charCodes[it]) {
			if(i == ".")
				sparkFunctions.dot();
			else if(i == "_")
				sparkFunctions.dash();
			else if(i == " ")
				sparkFunctions.space();
		}

	}

}

exports.convertToMorse = convertToMorse;<|endoftext|>


<commit_before>const debug = require('debug')('Actions');
const merge = require('lodash.merge');
const Storage = require('./Storage');
const actionsFactory = require('./actions/actionsFactory');
const wait = require('./tools/wait');

class Actions {
    /**
     * @param {Object} options
     * @param {AbstractEnvironment} options.environment
     * @param {Parser} options.parser
     * @param {Storage} options.storage
     */
    constructor(options) {
        this._env = options.environment;
        this._parser = options.parser;
        this._storage = options.storage || new Storage();
    }

    /**
     * Perform parsing rule
     * @param {Rule} rule
     * @param {string?} parentSelector
     * @returns {Promise}
     */
    async performForRule(rule, parentSelector) {
        const actions = rule.actions;
        const possibleErrors = rule.catchError || {};

        if (!actions) {
            return Promise.resolve();
        }

        try {
            return this.performActions(actions, parentSelector);
        } catch (e) {
            debug('Catching possible errors %o', possibleErrors);
            if (!(e instanceof Error) || !possibleErrors[e.name]) {
                debug('Handler for %o not found', e);
                throw e;
            }

            return this._handleError(possibleErrors[e.name], args);
        }
    }

    /**
     * Handle action error
     * @param {object} handlerOptions
     * @param {number} handlerOptions.handler Handler name
     * @param {number} handlerOptions.attempts Number of attempts before cancel with error
     * @param {number} handlerOptions.__attempt Current attempt number
     * @param actionArgs
     * @return {Promise}
     * @private
     */
    async _handleError(handlerOptions, actionArgs) {
        debug('Handle error with rules %o', handlerOptions);
        switch (handlerOptions.handler) {
            case 'repeat':
                handlerOptions.__attempt = handlerOptions.__attempt || 0;
                if (++handlerOptions.__attempt > handlerOptions.attempts) {
                    throw new Error('Max attempts limit exceeded');
                }
                const result = await this.performForRule(...actionArgs);
                delete handlerOptions.__attempt;
                return result;

            default:
                throw new Error('Unknown handler ' + handlerOptions.handler);
        }
    }

    /**
     * Perform parsing rule
     * @param {Rule} rule
     * @param {string} parentSelector
     * @returns {Promise}
     */
    async performPostActionsForRule(rule, parentSelector) {
        const actions = rule.postActions;

        if (!actions) {
            return Promise.resolve();
        }

        return this.performActions(actions, parentSelector);
    }

    /**
     * Perform array of actions
     * @param {Array} actions
     * @param {string} [parentSelector]
     * @returns {Promise}
     */
    async performActions(actions, parentSelector) {
        if (!Array.isArray(actions)) {
            throw new Error('actions must be an Array');
        }

        debug('Perform actions %o', actions);

        if (!parentSelector) {
            parentSelector = 'body';
            debug('Parent scope switched to %s', parentSelector);
        }

        return actions.reduce(async (promise, action) => {
            if (action.once && action.__done) {
                return promise;
            }

            const prevResult = await promise;
            const result = await this.performAction(action, parentSelector, prevResult);
            action.__done = true;
            return result;
        }, Promise.resolve());
    }

    /**
     * @param {string} name
     * @returns {*}
     */
    get(name) {
        return this._storage.get(name);
    }

    /**
     * @param {string} name
     * @param {*} value
     * @returns {*}
     */
    set(name, value) {
        return this._storage.set(name, value);
    }

    /**
     * @param {string} name
     * @returns {*}
     */
    unset(name) {
        return this._storage.unset(name);
    }

    /**
     * @param {ActionOptions} action
     * @param {string} parentSelector
     * @param {?*} prevResult
     * @returns {Promise}
     */
    async performAction(action, parentSelector, prevResult) {
        const selector = (action.parentScope || parentSelector || '') + ' ' + (action.scope || '');
        debug('Perform action %o for generated selector %s', action, selector);

        if (action.waitForPage || action.type === 'back') {
            await this.performAction({
                type: 'waitForPage',
                timeout: action.waitForPageTimeout
            }, parentSelector, prevResult);
        }

        if (action.waitForQuery) {
            const waitAction = merge({}, action.waitForQuery, {
                type: this.TYPES.WAIT_FOR_QUERY
            });
            await this.performAction(waitAction, parentSelector, prevResult);
        }

        let casesResult;
        if (action.cases && action.type !== 'cases') {
            casesResult = await this._createInstance({
                    type: 'cases',
                    cases: action.cases
                },
                selector,
                parentSelector,
                prevResult
            ).perform();
        }

        // mutation for if-then-else action
        if (action.conditions) {
            action.type = 'condition';
        }

        const actionInstance = this._createInstance(action, selector, parentSelector, prevResult);

        if (!actionInstance) {
            Promise.reject(new Error('Unknown action type: ' + action.type));
            return;
        }

        let result = await actionInstance.perform();

        // mutation for transform action
        if (action.transform) {
            result = this._parser.transform(result, action.transform);
        }

        // mutation for set action
        if (action.set) {
            this.set(action.set, result);
        }

        return casesResult || result;
    }

    /**
     * @param action
     * @param selector
     * @param parentSelector
     * @param prevResult
     * @return {Action}
     * @private
     */
    _createInstance(action, selector, parentSelector, prevResult) {
        return actionsFactory.createAction({
            selector,
            actionOptions: action,
            parentSelector,
            prevResult,
            env: this._env,
            parser: this._parser,
            actions: this
        });
    }

    /**
     * Add custom action
     * @param {string} type
     * @param {Function} action
     */
    addAction(type, action) {
        actionsFactory.addAction(type, action);
    }

    /**
     * Wait until function evalFunction expected in checkerFunction result
     * @param {Function} evalFunction
     * @param {Function} [checkerFunction]
     * @param {Array} [args]
     * @param {number} [timeout]
     * @param {number} [interval]
     * @returns {Promise}
     */
    async wait(evalFunction, checkerFunction, args, timeout, interval) {
        return wait(this._env, evalFunction, checkerFunction, args, timeout, interval);
    }

    async click(selector) {
        return this.performAction({
            type: 'click',
            scope: selector
        }, '');
    }

    /**
     * Perform page scroll-down
     * @param {number} interval
     * @returns {Promise}
     */
    async scroll(interval) {
        debug('scroll %s px', interval);
        return this._env.evaluateJs(interval, /* @covignore */ function (interval) {
            document.body.scrollTop += interval;
        });
    }
}

module.exports = Actions;
<commit_msg>- fixed issue with click and wait for something - added unified waitFor property which can contain all waiting actions<commit_after>const debug = require('debug')('Actions');
const merge = require('lodash.merge');
const Storage = require('./Storage');
const actionsFactory = require('./actions/actionsFactory');
const wait = require('./tools/wait');

class Actions {
    /**
     * @param {Object} options
     * @param {AbstractEnvironment} options.environment
     * @param {Parser} options.parser
     * @param {Storage} options.storage
     */
    constructor(options) {
        this._env = options.environment;
        this._parser = options.parser;
        this._storage = options.storage || new Storage();
    }

    /**
     * Perform parsing rule
     * @param {Rule} rule
     * @param {string?} parentSelector
     * @returns {Promise}
     */
    async performForRule(rule, parentSelector) {
        const actions = rule.actions;
        const possibleErrors = rule.catchError || {};

        if (!actions) {
            return Promise.resolve();
        }

        try {
            return this.performActions(actions, parentSelector);
        } catch (e) {
            debug('Catching possible errors %o', possibleErrors);
            if (!(e instanceof Error) || !possibleErrors[e.name]) {
                debug('Handler for %o not found', e);
                throw e;
            }

            return this._handleError(possibleErrors[e.name], args);
        }
    }

    /**
     * Handle action error
     * @param {object} handlerOptions
     * @param {number} handlerOptions.handler Handler name
     * @param {number} handlerOptions.attempts Number of attempts before cancel with error
     * @param {number} handlerOptions.__attempt Current attempt number
     * @param actionArgs
     * @return {Promise}
     * @private
     */
    async _handleError(handlerOptions, actionArgs) {
        debug('Handle error with rules %o', handlerOptions);
        switch (handlerOptions.handler) {
            case 'repeat':
                handlerOptions.__attempt = handlerOptions.__attempt || 0;
                if (++handlerOptions.__attempt > handlerOptions.attempts) {
                    throw new Error('Max attempts limit exceeded');
                }
                const result = await this.performForRule(...actionArgs);
                delete handlerOptions.__attempt;
                return result;

            default:
                throw new Error('Unknown handler ' + handlerOptions.handler);
        }
    }

    /**
     * Perform parsing rule
     * @param {Rule} rule
     * @param {string} parentSelector
     * @returns {Promise}
     */
    async performPostActionsForRule(rule, parentSelector) {
        const actions = rule.postActions;

        if (!actions) {
            return Promise.resolve();
        }

        return this.performActions(actions, parentSelector);
    }

    /**
     * Perform array of actions
     * @param {Array} actions
     * @param {string} [parentSelector]
     * @returns {Promise}
     */
    async performActions(actions, parentSelector) {
        if (!Array.isArray(actions)) {
            throw new Error('actions must be an Array');
        }

        debug('Perform actions %o', actions);

        if (!parentSelector) {
            parentSelector = 'body';
            debug('Parent scope switched to %s', parentSelector);
        }

        return actions.reduce(async (promise, action) => {
            if (action.once && action.__done) {
                return promise;
            }

            const prevResult = await promise;
            const result = await this.performAction(action, parentSelector, prevResult);
            action.__done = true;
            return result;
        }, Promise.resolve());
    }

    /**
     * @param {string} name
     * @returns {*}
     */
    get(name) {
        return this._storage.get(name);
    }

    /**
     * @param {string} name
     * @param {*} value
     * @returns {*}
     */
    set(name, value) {
        return this._storage.set(name, value);
    }

    /**
     * @param {string} name
     * @returns {*}
     */
    unset(name) {
        return this._storage.unset(name);
    }

    /**
     * @param {ActionOptions} action
     * @param {string} parentSelector
     * @param {?*} prevResult
     * @returns {Promise}
     */
    async performAction(action, parentSelector, prevResult) {
        const selector = (action.parentScope || parentSelector || '') + ' ' + (action.scope || '');
        debug('Perform action %o for generated selector %s', action, selector);

        let waitForPromise = Promise.resolve();
        if (action.waitForPage || action.type === 'back') {
            waitForPromise = this.performAction({
                type: 'waitForPage',
                timeout: action.waitForPageTimeout
            }, parentSelector, prevResult);
        }

        if (action.waitForQuery) {
            const waitAction = merge({}, action.waitForQuery, {
                type: this.TYPES.WAIT_FOR_QUERY
            });
            waitForPromise = this.performAction(waitAction, parentSelector, prevResult);
        }

        if (action.waitFor) {
            let waitFor = typeof action.waitFor === 'string' ?
              { type: action.waitFor } : action.waitFor;
            waitFor = merge({}, waitFor, {
                type: `waitFor${waitFor.type.charAt(0).toUpperCase() + waitFor.type.slice(1)}`,
            });
            waitForPromise = this.performAction(waitFor, parentSelector, prevResult);
        }

        let casesResult;
        if (action.cases && action.type !== 'cases') {
            casesResult = await this._createInstance({
                    type: 'cases',
                    cases: action.cases
                },
                selector,
                parentSelector,
                prevResult
            ).perform();
        }

        // mutation for if-then-else action
        if (action.conditions) {
            action.type = 'condition';
        }

        const actionInstance = this._createInstance(action, selector, parentSelector, prevResult);

        if (!actionInstance) {
            Promise.reject(new Error('Unknown action type: ' + action.type));
            return;
        }

        let result = await actionInstance.perform();
        await waitForPromise;

        // mutation for transform action
        if (action.transform) {
            result = this._parser.transform(result, action.transform);
        }

        // mutation for set action
        if (action.set) {
            this.set(action.set, result);
        }

        return casesResult || result;
    }

    /**
     * @param action
     * @param selector
     * @param parentSelector
     * @param prevResult
     * @return {Action}
     * @private
     */
    _createInstance(action, selector, parentSelector, prevResult) {
        return actionsFactory.createAction({
            selector,
            actionOptions: action,
            parentSelector,
            prevResult,
            env: this._env,
            parser: this._parser,
            actions: this
        });
    }

    /**
     * Add custom action
     * @param {string} type
     * @param {Function} action
     */
    addAction(type, action) {
        actionsFactory.addAction(type, action);
    }

    /**
     * Wait until function evalFunction expected in checkerFunction result
     * @param {Function} evalFunction
     * @param {Function} [checkerFunction]
     * @param {Array} [args]
     * @param {number} [timeout]
     * @param {number} [interval]
     * @returns {Promise}
     */
    async wait(evalFunction, checkerFunction, args, timeout, interval) {
        return wait(this._env, evalFunction, checkerFunction, args, timeout, interval);
    }

    async click(selector) {
        return this.performAction({
            type: 'click',
            scope: selector
        }, '');
    }

    /**
     * Perform page scroll-down
     * @param {number} interval
     * @returns {Promise}
     */
    async scroll(interval) {
        debug('scroll %s px', interval);
        return this._env.evaluateJs(interval, /* @covignore */ function (interval) {
            document.body.scrollTop += interval;
        });
    }
}

module.exports = Actions;
<|endoftext|>


<commit_before>/**
 * Configuration and indexing functions of Algolia API
 */
const algoliasearch = require('algoliasearch');

/**
 * Algolia Class
 */
class Algolia {

  /**
   * Constructor
   * @param  {Object} config Configuration
   * @param  {String} index  Name of index
   * @return {void}
   */
  constructor (config, index) {
    let indexName = (config.indexPrefix || '') + index;

    this.client = algoliasearch(config.applicationId, config.apiKey);
    this.index = this.client.initIndex(indexName);
  }

  /**
   * Index any data for a specific type. Update and create.
   * @param  {Object}  data All elements that should be indexed
   * @return {Promise}
   */
  indexData (data) {
    return new Promise((resolve, reject) => {
      let promises = [];
      let newObjects = [];
      let existingObjects = [];

      data.forEach((element) => {
        promises.push(this.getElementPromise(element));
      });

      Promise.all(promises)
        .then((results) => {
          results.forEach((result) => {
            if (result.exists) {
              existingObjects.push(result.element);
            } else {
              newObjects.push(result.element);
            }
          });
        })
        .then(() => {
          this.indexObjects(newObjects, existingObjects, resolve, reject);
        })
        .catch(console.error);
    });
  }

  /**
   * For each element, that should be indexed, we need to know if it exists or not
   * @param  {Object}  element Element to index
   * @return {Promise}         Resolver holds the data of element and if it exists
   *                           or not
   */
  getElementPromise (element) {
    return new Promise((resolve, reject) => {
      this.getObjectById(element.id)
        .then(results => {
          if (results && results.hits && results.hits.length > 0) {
            element.objectID = results.hits[0].objectID;
            return resolve({
              exists: true,
              element
            });
          } else {
            return resolve({
              exists: false,
              element
            });
          }
        })
        .catch(console.error);
    });
  }

  /**
   * Index all objects in Algolia by updating and creating them
   * @param  {Array}    newObjects      Objects to create
   * @param  {Array}    existingObjects Objects to update
   * @param  {Function} resolve         Resolve when ready
   * @param  {Function} reject          Reject
   * @return {Promise}
   */
  indexObjects (newObjects, existingObjects, resolve, reject) {
    return Promise.all([
      this.addObjects(newObjects),
      this.updateObjects(existingObjects)
    ]).then((data) => {
      let objects = this.getMergedObjects(data);

      resolve(objects);
    })
    .catch(reject);
  }

  /**
   * Merge two object arrays
   * @param  {Array} data Data that should be merged
   * @return {Array}      Data that is merged
   */
  getMergedObjects (data) {
    let objects = [];

    data.forEach((element) => {
      objects = objects.concat(element.objectIDs);
    });

    return objects;
  }

  /**
   * Get an object from the index by its id attribute
   * @param  {String}  id Id of a given object
   * @return {Promise}    Resolves with the found elements
   */
  getObjectById (id) {
    return new Promise((resolve, reject) => {
      this.index.search({
        query: `${id}`,
        restrictSearchableAttributes: ['id']
      }, (error, results) => {
        if (error) {
          return reject(error);
        }

        return resolve(results);
      });
    });
  }

  /**
   * Add new objects to the index
   * @param  {Array}   objects Objects to index
   * @return {Promise}         Resolves with indexed objects
   */
  addObjects (objects) {
    return new Promise((resolve, reject) => {
      this.index.addObjects(objects, (error, content) => {
        if (error) {
          return reject(error);
        }

        return resolve(content);
      });
    });
  }

  /**
   * Update existing objects in the index
   * @param  {Array}   objects Objects to update
   * @return {Promise}         Resolves with indexed objects
   */
  updateObjects (objects) {
    return new Promise((resolve, reject) => {
      this.index.saveObjects(objects, (error, content) => {
        if (error) {
          return reject(error);
        }

        return resolve(content);
      });
    });
  }

}

/**
 * Exports
 * @type {Class}
 */
module.exports = Algolia;
<commit_msg>For Algolia indexing: Add locale string to search for exisiting elements<commit_after>/**
 * Configuration and indexing functions of Algolia API
 */
const algoliasearch = require('algoliasearch');

/**
 * Algolia Class
 */
class Algolia {

  /**
   * Constructor
   * @param  {Object} config Configuration
   * @param  {String} index  Name of index
   * @return {void}
   */
  constructor (config, index) {
    let indexName = (config.indexPrefix || '') + index;

    this.client = algoliasearch(config.applicationId, config.apiKey);
    this.index = this.client.initIndex(indexName);
  }

  /**
   * Index any data for a specific type. Update and create.
   * @param  {Object}  data All elements that should be indexed
   * @return {Promise}
   */
  indexData (data) {
    return new Promise((resolve, reject) => {
      let promises = [];
      let newObjects = [];
      let existingObjects = [];

      data.forEach((element) => {
        promises.push(this.getElementPromise(element));
      });

      Promise.all(promises)
        .then((results) => {
          results.forEach((result) => {
            if (result.exists) {
              existingObjects.push(result.element);
            } else {
              newObjects.push(result.element);
            }
          });
        })
        .then(() => {
          this.indexObjects(newObjects, existingObjects, resolve, reject);
        })
        .catch(console.error);
    });
  }

  /**
   * For each element, that should be indexed, we need to know if it exists or not
   * @param  {Object}  element Element to index
   * @return {Promise}         Resolver holds the data of element and if it exists
   *                           or not
   */
  getElementPromise (element) {
    return new Promise((resolve, reject) => {
      this.getObjectById(element.id, element.locale)
        .then(results => {
          if (results && results.hits && results.hits.length > 0) {
            element.objectID = results.hits[0].objectID;
            return resolve({
              exists: true,
              element
            });
          } else {
            return resolve({
              exists: false,
              element
            });
          }
        })
        .catch(console.error);
    });
  }

  /**
   * Index all objects in Algolia by updating and creating them
   * @param  {Array}    newObjects      Objects to create
   * @param  {Array}    existingObjects Objects to update
   * @param  {Function} resolve         Resolve when ready
   * @param  {Function} reject          Reject
   * @return {Promise}
   */
  indexObjects (newObjects, existingObjects, resolve, reject) {
    return Promise.all([
      this.addObjects(newObjects),
      this.updateObjects(existingObjects)
    ]).then((data) => {
      let objects = this.getMergedObjects(data);

      resolve(objects);
    })
    .catch(reject);
  }

  /**
   * Merge two object arrays
   * @param  {Array} data Data that should be merged
   * @return {Array}      Data that is merged
   */
  getMergedObjects (data) {
    let objects = [];

    data.forEach((element) => {
      objects = objects.concat(element.objectIDs);
    });

    return objects;
  }

  /**
   * Get an object from the index by its id attribute
   * @param  {String}  id     Id of a given object
   * @param  {String}  locale Locale to filter for
   * @return {Promise}        Resolves with the found elements
   */
  getObjectById (id, locale) {
    return new Promise((resolve, reject) => {
      this.index.search({
        query: `${id} ${locale}`,
        restrictSearchableAttributes: ['id', 'locale']
      }, (error, results) => {
        if (error) {
          return reject(error);
        }

        return resolve(results);
      });
    });
  }

  /**
   * Add new objects to the index
   * @param  {Array}   objects Objects to index
   * @return {Promise}         Resolves with indexed objects
   */
  addObjects (objects) {
    return new Promise((resolve, reject) => {
      this.index.addObjects(objects, (error, content) => {
        if (error) {
          return reject(error);
        }

        return resolve(content);
      });
    });
  }

  /**
   * Update existing objects in the index
   * @param  {Array}   objects Objects to update
   * @return {Promise}         Resolves with indexed objects
   */
  updateObjects (objects) {
    return new Promise((resolve, reject) => {
      this.index.saveObjects(objects, (error, content) => {
        if (error) {
          return reject(error);
        }

        return resolve(content);
      });
    });
  }

}

/**
 * Exports
 * @type {Class}
 */
module.exports = Algolia;
<|endoftext|>