<commit_before>// @flow

import type { PingRequest, PingResponse } from 'lib/types/ping-types';
import { defaultNumberPerThread } from 'lib/types/message-types';
import type { Viewer } from '../session/viewer';
import {
  serverRequestTypes,
  type ThreadPollPushInconsistencyClientResponse,
} from 'lib/types/request-types';
import { isDeviceType, assertDeviceType } from 'lib/types/device-types';
import { reportTypes } from 'lib/types/report-types';
import type { CalendarQuery } from 'lib/types/entry-types';

import t from 'tcomb';
import invariant from 'invariant';
import _isEqual from 'lodash/fp/isEqual';

import { ServerError } from 'lib/utils/errors';
import { mostRecentMessageTimestamp } from 'lib/shared/message-utils';
import { mostRecentUpdateTimestamp } from 'lib/shared/update-utils';
import { promiseAll } from 'lib/utils/promises';
import { values } from 'lib/utils/objects';

import {
  validateInput,
  tShape,
  tPlatform,
  tPlatformDetails,
} from '../utils/validation-utils';
import {
  entryQueryInputValidator,
  normalizeCalendarQuery,
  verifyCalendarQueryThreadIDs,
} from './entry-responders';
import { fetchMessageInfosSince } from '../fetchers/message-fetchers';
import { fetchThreadInfos } from '../fetchers/thread-fetchers';
import { fetchEntryInfos } from '../fetchers/entry-fetchers';
import {
  updateActivityTime,
  activityUpdater,
} from '../updaters/activity-updaters';
import { fetchCurrentUserInfo } from '../fetchers/user-fetchers';
import { fetchUpdateInfos } from '../fetchers/update-fetchers';
import {
  setNewSession,
  setCookiePlatform,
  setCookiePlatformDetails,
} from '../session/cookies';
import { deviceTokenUpdater } from '../updaters/device-token-updaters';
import createReport from '../creators/report-creator';
import {
  compareNewCalendarQuery,
  commitSessionUpdate,
} from '../updaters/session-updaters';
import {
  deleteUpdatesBeforeTimeTargettingCookie,
} from '../deleters/update-deleters';

const pingRequestInputValidator = tShape({
  calendarQuery: entryQueryInputValidator,
  lastPing: t.maybe(t.Number), // deprecated
  messagesCurrentAsOf: t.maybe(t.Number),
  updatesCurrentAsOf: t.maybe(t.Number),
  watchedIDs: t.list(t.String),
  clientResponses: t.maybe(t.list(t.union([
    tShape({
      type: t.irreducible(
        'serverRequestTypes.PLATFORM',
        x => x === serverRequestTypes.PLATFORM,
      ),
      platform: tPlatform,
    }),
    tShape({
      type: t.irreducible(
        'serverRequestTypes.DEVICE_TOKEN',
        x => x === serverRequestTypes.DEVICE_TOKEN,
      ),
      deviceToken: t.String,
    }),
    tShape({
      type: t.irreducible(
        'serverRequestTypes.THREAD_POLL_PUSH_INCONSISTENCY',
        x => x === serverRequestTypes.THREAD_POLL_PUSH_INCONSISTENCY,
      ),
      platformDetails: tPlatformDetails,
      beforeAction: t.Object,
      action: t.Object,
      pollResult: t.Object,
      pushResult: t.Object,
      lastActionTypes: t.maybe(t.list(t.String)),
      time: t.maybe(t.Number),
    }),
    tShape({
      type: t.irreducible(
        'serverRequestTypes.PLATFORM_DETAILS',
        x => x === serverRequestTypes.PLATFORM_DETAILS,
      ),
      platformDetails: tPlatformDetails,
    }),
    tShape({
      type: t.irreducible(
        'serverRequestTypes.INITIAL_ACTIVITY_UPDATE',
        x => x === serverRequestTypes.INITIAL_ACTIVITY_UPDATE,
      ),
      threadID: t.String,
    }),
  ]))),
});

async function pingResponder(
  viewer: Viewer,
  input: any,
): Promise<PingResponse> {
  const request: PingRequest = input;
  request.calendarQuery = normalizeCalendarQuery(request.calendarQuery);
  validateInput(pingRequestInputValidator, request);

  let clientMessagesCurrentAsOf;
  if (
    request.messagesCurrentAsOf !== null &&
    request.messagesCurrentAsOf !== undefined
  ) {
    clientMessagesCurrentAsOf = request.messagesCurrentAsOf;
  } else if (request.lastPing !== null && request.lastPing !== undefined) {
    clientMessagesCurrentAsOf = request.lastPing;
  }
  if (
    clientMessagesCurrentAsOf === null ||
    clientMessagesCurrentAsOf === undefined
  ) {
    throw new ServerError('invalid_parameters');
  }

  const { calendarQuery } = request;
  await verifyCalendarQueryThreadIDs(calendarQuery);

  const threadCursors = {};
  for (let watchedThreadID of request.watchedIDs) {
    threadCursors[watchedThreadID] = null;
  }
  const threadSelectionCriteria = { threadCursors, joinedThreads: true };

  let viewerMissingPlatform = !viewer.platform;
  const platformDetails = viewer.platformDetails;
  let viewerMissingPlatformDetails = !platformDetails ||
    (isDeviceType(viewer.platform) &&
      (platformDetails.codeVersion === null ||
        platformDetails.codeVersion === undefined ||
        platformDetails.stateVersion === null ||
        platformDetails.stateVersion === undefined));
  let viewerMissingDeviceToken =
    isDeviceType(viewer.platform) && viewer.loggedIn && !viewer.deviceToken;

  const { clientResponses } = request;
  const clientResponsePromises = [];
  if (clientResponses) {
    const clientSentPlatformDetails = clientResponses.some(
      response => response.type === serverRequestTypes.PLATFORM_DETAILS,
    );
    for (let clientResponse of clientResponses) {
      if (
        clientResponse.type === serverRequestTypes.PLATFORM &&
        !clientSentPlatformDetails
      ) {
        clientResponsePromises.push(setCookiePlatform(
          viewer.cookieID,
          clientResponse.platform,
        ));
        viewerMissingPlatform = false;
        if (!isDeviceType(clientResponse.platform)) {
          viewerMissingPlatformDetails = false;
        }
      } else if (clientResponse.type === serverRequestTypes.DEVICE_TOKEN) {
        clientResponsePromises.push(deviceTokenUpdater(
          viewer,
          {
            deviceToken: clientResponse.deviceToken,
            deviceType: assertDeviceType(viewer.platform),
          },
        ));
        viewerMissingDeviceToken = false;
      } else if (
        clientResponse.type ===
          serverRequestTypes.THREAD_POLL_PUSH_INCONSISTENCY
      ) {
        clientResponsePromises.push(recordThreadPollPushInconsistency(
          viewer,
          clientResponse,
        ));
      } else if (clientResponse.type === serverRequestTypes.PLATFORM_DETAILS) {
        clientResponsePromises.push(setCookiePlatformDetails(
          viewer.cookieID,
          clientResponse.platformDetails,
        ));
        viewerMissingPlatform = false;
        viewerMissingPlatformDetails = false;
      } else if (
        clientResponse.type === serverRequestTypes.INITIAL_ACTIVITY_UPDATE
      ) {
        clientResponsePromises.push(activityUpdater(
          viewer,
          { updates: [ { focus: true, threadID: clientResponse.threadID } ] },
        ));
      }
    }
  }
  if (clientResponsePromises.length > 0) {
    await Promise.all(clientResponsePromises);
  }

  const oldUpdatesCurrentAsOf = request.updatesCurrentAsOf;
  const [
    messagesResult,
    threadsResult,
    entriesResult,
    currentUserInfo,
  ] = await Promise.all([
    fetchMessageInfosSince(
      viewer,
      threadSelectionCriteria,
      clientMessagesCurrentAsOf,
      defaultNumberPerThread,
    ),
    fetchThreadInfos(viewer),
    fetchEntryInfos(viewer, calendarQuery),
    fetchCurrentUserInfo(viewer),
    initializeSession(viewer, calendarQuery, oldUpdatesCurrentAsOf),
  ]);

  const promises = {};
  promises.activityUpdate = updateActivityTime(viewer);
  if (
    viewer.loggedIn &&
    oldUpdatesCurrentAsOf !== null &&
    oldUpdatesCurrentAsOf !== undefined
  ) {
    promises.deleteExpiredUpdates = deleteUpdatesBeforeTimeTargettingCookie(
      viewer,
      oldUpdatesCurrentAsOf,
    );
    promises.fetchUpdateResult = fetchUpdateInfos(
      viewer,
      oldUpdatesCurrentAsOf,
      { ...threadsResult, calendarQuery },
    );
  }

  const { fetchUpdateResult } = await promiseAll(promises);

  let updateUserInfos = {}, updatesResult = null;
  if (fetchUpdateResult) {
    invariant(
      oldUpdatesCurrentAsOf !== null && oldUpdatesCurrentAsOf !== undefined,
      "should be set",
    );
    updateUserInfos = fetchUpdateResult.userInfos;
    const { updateInfos } = fetchUpdateResult;
    const newUpdatesCurrentAsOf = mostRecentUpdateTimestamp(
      [...updateInfos],
      oldUpdatesCurrentAsOf,
    );
    updatesResult = {
      newUpdates: updateInfos,
      currentAsOf: newUpdatesCurrentAsOf,
    };
  }

  const userInfos = values({
    ...messagesResult.userInfos,
    ...entriesResult.userInfos,
    ...threadsResult.userInfos,
    ...updateUserInfos,
  });

  const serverRequests = [];
  if (viewerMissingPlatform) {
    serverRequests.push({ type: serverRequestTypes.PLATFORM });
  }
  if (viewerMissingPlatformDetails) {
    serverRequests.push({ type: serverRequestTypes.PLATFORM_DETAILS });
  }
  if (viewerMissingDeviceToken) {
    serverRequests.push({ type: serverRequestTypes.DEVICE_TOKEN });
  }

  const messagesCurrentAsOf = mostRecentMessageTimestamp(
    messagesResult.rawMessageInfos,
    clientMessagesCurrentAsOf,
  );
  const response: PingResponse = {
    threadInfos: threadsResult.threadInfos,
    currentUserInfo,
    rawMessageInfos: messagesResult.rawMessageInfos,
    truncationStatuses: messagesResult.truncationStatuses,
    messagesCurrentAsOf,
    serverTime: messagesCurrentAsOf,
    rawEntryInfos: entriesResult.rawEntryInfos,
    userInfos,
    serverRequests,
  };
  if (updatesResult) {
    response.updatesResult = updatesResult;
  }

  return response;
}

async function recordThreadPollPushInconsistency(
  viewer: Viewer,
  response: ThreadPollPushInconsistencyClientResponse,
): Promise<void> {
  const { type, ...rest } = response;
  const reportCreationRequest = {
    ...rest,
    type: reportTypes.THREAD_POLL_PUSH_INCONSISTENCY,
  };
  await createReport(viewer, reportCreationRequest);
}

async function initializeSession(
  viewer: Viewer,
  calendarQuery: CalendarQuery,
  oldLastUpdate: ?number,
): Promise<void> {
  if (!viewer.loggedIn) {
    return;
  }

  let sessionInitialized = true, sessionUpdate = {};
  try {
    sessionUpdate = await compareNewCalendarQuery(viewer, calendarQuery);
  } catch (e) {
    if (e.message !== "unknown_error") {
      throw e;
    }
    sessionInitialized = false;
  }

  if (sessionInitialized) {
    if (oldLastUpdate !== null && oldLastUpdate !== undefined) {
      sessionUpdate.lastUpdate = oldLastUpdate;
    }
    await commitSessionUpdate(viewer, sessionUpdate);
  } else if (oldLastUpdate !== null && oldLastUpdate !== undefined) {
    // We're only able to create the session if we have oldLastUpdate
    await setNewSession(viewer, calendarQuery, oldLastUpdate);
  }
}

export {
  pingResponder,
};
<commit_msg>[server] Return sessionContinued from initializeSession<commit_after>// @flow

import type { PingRequest, PingResponse } from 'lib/types/ping-types';
import { defaultNumberPerThread } from 'lib/types/message-types';
import type { Viewer } from '../session/viewer';
import {
  serverRequestTypes,
  type ThreadPollPushInconsistencyClientResponse,
} from 'lib/types/request-types';
import { isDeviceType, assertDeviceType } from 'lib/types/device-types';
import { reportTypes } from 'lib/types/report-types';
import type { CalendarQuery } from 'lib/types/entry-types';

import t from 'tcomb';
import invariant from 'invariant';
import _isEqual from 'lodash/fp/isEqual';

import { ServerError } from 'lib/utils/errors';
import { mostRecentMessageTimestamp } from 'lib/shared/message-utils';
import { mostRecentUpdateTimestamp } from 'lib/shared/update-utils';
import { promiseAll } from 'lib/utils/promises';
import { values } from 'lib/utils/objects';

import {
  validateInput,
  tShape,
  tPlatform,
  tPlatformDetails,
} from '../utils/validation-utils';
import {
  entryQueryInputValidator,
  normalizeCalendarQuery,
  verifyCalendarQueryThreadIDs,
} from './entry-responders';
import { fetchMessageInfosSince } from '../fetchers/message-fetchers';
import { fetchThreadInfos } from '../fetchers/thread-fetchers';
import { fetchEntryInfos } from '../fetchers/entry-fetchers';
import {
  updateActivityTime,
  activityUpdater,
} from '../updaters/activity-updaters';
import { fetchCurrentUserInfo } from '../fetchers/user-fetchers';
import { fetchUpdateInfos } from '../fetchers/update-fetchers';
import {
  setNewSession,
  setCookiePlatform,
  setCookiePlatformDetails,
} from '../session/cookies';
import { deviceTokenUpdater } from '../updaters/device-token-updaters';
import createReport from '../creators/report-creator';
import {
  compareNewCalendarQuery,
  commitSessionUpdate,
} from '../updaters/session-updaters';
import {
  deleteUpdatesBeforeTimeTargettingCookie,
} from '../deleters/update-deleters';

const pingRequestInputValidator = tShape({
  calendarQuery: entryQueryInputValidator,
  lastPing: t.maybe(t.Number), // deprecated
  messagesCurrentAsOf: t.maybe(t.Number),
  updatesCurrentAsOf: t.maybe(t.Number),
  watchedIDs: t.list(t.String),
  clientResponses: t.maybe(t.list(t.union([
    tShape({
      type: t.irreducible(
        'serverRequestTypes.PLATFORM',
        x => x === serverRequestTypes.PLATFORM,
      ),
      platform: tPlatform,
    }),
    tShape({
      type: t.irreducible(
        'serverRequestTypes.DEVICE_TOKEN',
        x => x === serverRequestTypes.DEVICE_TOKEN,
      ),
      deviceToken: t.String,
    }),
    tShape({
      type: t.irreducible(
        'serverRequestTypes.THREAD_POLL_PUSH_INCONSISTENCY',
        x => x === serverRequestTypes.THREAD_POLL_PUSH_INCONSISTENCY,
      ),
      platformDetails: tPlatformDetails,
      beforeAction: t.Object,
      action: t.Object,
      pollResult: t.Object,
      pushResult: t.Object,
      lastActionTypes: t.maybe(t.list(t.String)),
      time: t.maybe(t.Number),
    }),
    tShape({
      type: t.irreducible(
        'serverRequestTypes.PLATFORM_DETAILS',
        x => x === serverRequestTypes.PLATFORM_DETAILS,
      ),
      platformDetails: tPlatformDetails,
    }),
    tShape({
      type: t.irreducible(
        'serverRequestTypes.INITIAL_ACTIVITY_UPDATE',
        x => x === serverRequestTypes.INITIAL_ACTIVITY_UPDATE,
      ),
      threadID: t.String,
    }),
  ]))),
});

async function pingResponder(
  viewer: Viewer,
  input: any,
): Promise<PingResponse> {
  const request: PingRequest = input;
  request.calendarQuery = normalizeCalendarQuery(request.calendarQuery);
  validateInput(pingRequestInputValidator, request);

  let clientMessagesCurrentAsOf;
  if (
    request.messagesCurrentAsOf !== null &&
    request.messagesCurrentAsOf !== undefined
  ) {
    clientMessagesCurrentAsOf = request.messagesCurrentAsOf;
  } else if (request.lastPing !== null && request.lastPing !== undefined) {
    clientMessagesCurrentAsOf = request.lastPing;
  }
  if (
    clientMessagesCurrentAsOf === null ||
    clientMessagesCurrentAsOf === undefined
  ) {
    throw new ServerError('invalid_parameters');
  }

  const { calendarQuery } = request;
  await verifyCalendarQueryThreadIDs(calendarQuery);

  const threadCursors = {};
  for (let watchedThreadID of request.watchedIDs) {
    threadCursors[watchedThreadID] = null;
  }
  const threadSelectionCriteria = { threadCursors, joinedThreads: true };

  let viewerMissingPlatform = !viewer.platform;
  const platformDetails = viewer.platformDetails;
  let viewerMissingPlatformDetails = !platformDetails ||
    (isDeviceType(viewer.platform) &&
      (platformDetails.codeVersion === null ||
        platformDetails.codeVersion === undefined ||
        platformDetails.stateVersion === null ||
        platformDetails.stateVersion === undefined));
  let viewerMissingDeviceToken =
    isDeviceType(viewer.platform) && viewer.loggedIn && !viewer.deviceToken;

  const { clientResponses } = request;
  const clientResponsePromises = [];
  if (clientResponses) {
    const clientSentPlatformDetails = clientResponses.some(
      response => response.type === serverRequestTypes.PLATFORM_DETAILS,
    );
    for (let clientResponse of clientResponses) {
      if (
        clientResponse.type === serverRequestTypes.PLATFORM &&
        !clientSentPlatformDetails
      ) {
        clientResponsePromises.push(setCookiePlatform(
          viewer.cookieID,
          clientResponse.platform,
        ));
        viewerMissingPlatform = false;
        if (!isDeviceType(clientResponse.platform)) {
          viewerMissingPlatformDetails = false;
        }
      } else if (clientResponse.type === serverRequestTypes.DEVICE_TOKEN) {
        clientResponsePromises.push(deviceTokenUpdater(
          viewer,
          {
            deviceToken: clientResponse.deviceToken,
            deviceType: assertDeviceType(viewer.platform),
          },
        ));
        viewerMissingDeviceToken = false;
      } else if (
        clientResponse.type ===
          serverRequestTypes.THREAD_POLL_PUSH_INCONSISTENCY
      ) {
        clientResponsePromises.push(recordThreadPollPushInconsistency(
          viewer,
          clientResponse,
        ));
      } else if (clientResponse.type === serverRequestTypes.PLATFORM_DETAILS) {
        clientResponsePromises.push(setCookiePlatformDetails(
          viewer.cookieID,
          clientResponse.platformDetails,
        ));
        viewerMissingPlatform = false;
        viewerMissingPlatformDetails = false;
      } else if (
        clientResponse.type === serverRequestTypes.INITIAL_ACTIVITY_UPDATE
      ) {
        clientResponsePromises.push(activityUpdater(
          viewer,
          { updates: [ { focus: true, threadID: clientResponse.threadID } ] },
        ));
      }
    }
  }
  if (clientResponsePromises.length > 0) {
    await Promise.all(clientResponsePromises);
  }

  const oldUpdatesCurrentAsOf = request.updatesCurrentAsOf;
  const [
    messagesResult,
    threadsResult,
    entriesResult,
    currentUserInfo,
  ] = await Promise.all([
    fetchMessageInfosSince(
      viewer,
      threadSelectionCriteria,
      clientMessagesCurrentAsOf,
      defaultNumberPerThread,
    ),
    fetchThreadInfos(viewer),
    fetchEntryInfos(viewer, calendarQuery),
    fetchCurrentUserInfo(viewer),
    initializeSession(viewer, calendarQuery, oldUpdatesCurrentAsOf),
  ]);

  const promises = {};
  promises.activityUpdate = updateActivityTime(viewer);
  if (
    viewer.loggedIn &&
    oldUpdatesCurrentAsOf !== null &&
    oldUpdatesCurrentAsOf !== undefined
  ) {
    promises.deleteExpiredUpdates = deleteUpdatesBeforeTimeTargettingCookie(
      viewer,
      oldUpdatesCurrentAsOf,
    );
    promises.fetchUpdateResult = fetchUpdateInfos(
      viewer,
      oldUpdatesCurrentAsOf,
      { ...threadsResult, calendarQuery },
    );
  }

  const { fetchUpdateResult } = await promiseAll(promises);

  let updateUserInfos = {}, updatesResult = null;
  if (fetchUpdateResult) {
    invariant(
      oldUpdatesCurrentAsOf !== null && oldUpdatesCurrentAsOf !== undefined,
      "should be set",
    );
    updateUserInfos = fetchUpdateResult.userInfos;
    const { updateInfos } = fetchUpdateResult;
    const newUpdatesCurrentAsOf = mostRecentUpdateTimestamp(
      [...updateInfos],
      oldUpdatesCurrentAsOf,
    );
    updatesResult = {
      newUpdates: updateInfos,
      currentAsOf: newUpdatesCurrentAsOf,
    };
  }

  const userInfos = values({
    ...messagesResult.userInfos,
    ...entriesResult.userInfos,
    ...threadsResult.userInfos,
    ...updateUserInfos,
  });

  const serverRequests = [];
  if (viewerMissingPlatform) {
    serverRequests.push({ type: serverRequestTypes.PLATFORM });
  }
  if (viewerMissingPlatformDetails) {
    serverRequests.push({ type: serverRequestTypes.PLATFORM_DETAILS });
  }
  if (viewerMissingDeviceToken) {
    serverRequests.push({ type: serverRequestTypes.DEVICE_TOKEN });
  }

  const messagesCurrentAsOf = mostRecentMessageTimestamp(
    messagesResult.rawMessageInfos,
    clientMessagesCurrentAsOf,
  );
  const response: PingResponse = {
    threadInfos: threadsResult.threadInfos,
    currentUserInfo,
    rawMessageInfos: messagesResult.rawMessageInfos,
    truncationStatuses: messagesResult.truncationStatuses,
    messagesCurrentAsOf,
    serverTime: messagesCurrentAsOf,
    rawEntryInfos: entriesResult.rawEntryInfos,
    userInfos,
    serverRequests,
  };
  if (updatesResult) {
    response.updatesResult = updatesResult;
  }

  return response;
}

async function recordThreadPollPushInconsistency(
  viewer: Viewer,
  response: ThreadPollPushInconsistencyClientResponse,
): Promise<void> {
  const { type, ...rest } = response;
  const reportCreationRequest = {
    ...rest,
    type: reportTypes.THREAD_POLL_PUSH_INCONSISTENCY,
  };
  await createReport(viewer, reportCreationRequest);
}

type SessionInitializationResult = {|
  // If this request is part of an already initialized session, then the
  // session is being continued, and we don't have to return as much stuff
  // TODO actually consume this info
  sessionContinued: bool,
|};
async function initializeSession(
  viewer: Viewer,
  calendarQuery: CalendarQuery,
  oldLastUpdate: ?number,
): Promise<SessionInitializationResult> {
  if (!viewer.loggedIn) {
    return { sessionContinued: false };
  }

  let sessionAlreadyInitialized = true, sessionUpdate = {};
  try {
    sessionUpdate = await compareNewCalendarQuery(viewer, calendarQuery);
  } catch (e) {
    if (e.message !== "unknown_error") {
      throw e;
    }
    sessionAlreadyInitialized = false;
  }

  if (sessionAlreadyInitialized) {
    if (oldLastUpdate !== null && oldLastUpdate !== undefined) {
      sessionUpdate.lastUpdate = oldLastUpdate;
    }
    await commitSessionUpdate(viewer, sessionUpdate);
  } else if (oldLastUpdate !== null && oldLastUpdate !== undefined) {
    // We're only able to create the session if we have oldLastUpdate
    await setNewSession(viewer, calendarQuery, oldLastUpdate);
  }

  return { sessionContinued: sessionAlreadyInitialized };
}

export {
  pingResponder,
};
<|endoftext|>


<commit_before>function Alarm(hour, minute, days, active) {
  this.hour = hour;
  this.minute = minute;
  this.days = days;
  this.active = active;
  this.editing = false;
  // this.applyEdits = function(hour, minute, days) {
  //   this.editing = false;
  //   var oldAlarm = this;
  //   this.time = genStartTime(hour, minute);
  //   this.days = days;
  //   pushChanges():
  // };

}
(function() {
  this.toggle = function() {
    this.active = !(this.active);
    pushChanges();
  };
  this.updateStartTime = function() {
    if (this.minute < 30) {
      this.start_minute = this.minute + 30;
      this.start_hour = (this.hour === 0) ? 23 : this.hour - 1;
    } else {
      this.start_minute = this.minute - 30;
      this.start_hour = this.hour;
    }
  };
  this.updateDaysTf = function () {
    this.daysTf = [];
    var j = 0;
    for (var i = 0; i < 7; i++) {
      if (this.days[j] - 1 === i) {
        this.daysTf[i] = true;
        j++;
      } else {
        this.daysTf[i] = false;
      }
    }
  };
  this.updateDays = function () {
    this.days = [];
    for (var i = 0; i < 7; i++) {
      if (this.daysTf[i] === true) {
        this.days.push(i + 1);
      }
    }
  }
  this.edit = function() {
    this.updateDaysTf();
    this.oldAlarm = {
      hour: this.hour,
      minute: this.minute,
      days: this.days
    };
    this.editing = true;
    console.log(this);
  };
  this.saveEdits = function() {
    this.editing = false;
    this.updateDays();
    this.updateStartTime();
    console.log(this)
    $.post("saveAlarm.php", {type: "edit", alarm: JSON.stringify(this)}, function (response) {
      if (response == "OK") {
        alert("The alarm was successfully updated.");
      } else {
        alert("FAIL " + response);
      }
    });
  }
}).call(Alarm.prototype);
<commit_msg>Fixed typo<commit_after>function Alarm(hour, minute, days, active) {
  this.hour = hour;
  this.minute = minute;
  this.days = days;
  this.active = active;
  this.editing = false;
  // this.applyEdits = function(hour, minute, days) {
  //   this.editing = false;
  //   var oldAlarm = this;
  //   this.time = genStartTime(hour, minute);
  //   this.days = days;
  //   pushChanges():
  // };

}
(function() {
  this.toggle = function() {
    this.active = !(this.active);
    pushChanges();
  };
  this.updateStartTime = function() {
    if (this.minute < 30) {
      this.start_minute = this.minute + 30;
      this.start_hour = (this.hour === 0) ? 23 : this.hour - 1;
    } else {
      this.start_minute = this.minute - 30;
      this.start_hour = this.hour;
    }
  };
  this.updateDaysTf = function () {
    this.days_tf = [];
    var j = 0;
    for (var i = 0; i < 7; i++) {
      if (this.days[j] - 1 === i) {
        this.days_tf[i] = true;
        j++;
      } else {
        this.days_tf[i] = false;
      }
    }
  };
  this.updateDays = function () {
    this.days = [];
    for (var i = 0; i < 7; i++) {
      if (this.days_tf[i] === true) {
        this.days.push(i + 1);
      }
    }
  }
  this.edit = function() {
    this.updateDaysTf();
    this.oldAlarm = {
      hour: this.hour,
      minute: this.minute,
      days: this.days
    };
    this.editing = true;
    console.log(this);
  };
  this.saveEdits = function() {
    this.editing = false;
    this.updateDays();
    this.updateStartTime();
    console.log(this)
    $.post("saveAlarm.php", {type: "edit", alarm: JSON.stringify(this)}, function (response) {
      if (response == "OK") {
        alert("The alarm was successfully updated.");
      } else {
        alert("FAIL " + response);
      }
    });
  }
}).call(Alarm.prototype);
<|endoftext|>


<commit_before>import _ from 'lodash';
import Entity from './Entity';

class Accounts {
  constructor(accounts) {
    this._accounts = accounts;
  }

  static fromJson(json) {
    const accounts = json.map(j => Account.fromJson(j));
    return new Accounts(accounts);
  }

  /**
   * @returns {Account|undefined}
   */
  get primary() {
    return _.chain(this._accounts)
      .filter(account => account.isPrimary)
      .head()
      .value();
  }

  /**
   * @returns {[Account] | []}
   */
  get subAccounts() {
    return _.reject(this._accounts, (a) => a.isPrimary);
  }

  get asArray() {
    return this._accounts;
  }

  /**
   * @returns {boolean}
   */
  get isEmpty() {
    return _.isEmpty(this._accounts) || (this._accounts.length == 1 && this._accounts[0].isDummy);
  }

}

class Account extends Entity {
  constructor(raw) {
    super(raw.id_str);
    this._raw = raw;
  }

  static fromJson(userJson) {
    return new this(userJson);
  }

  get id() {
    return this._raw.id_str;
  }

  /**
   * @returns {Credential}
   */
  get credential() {
    return this._raw.credential;
  }

  get isPrimary() {
    return this._raw.is_primary;
  }

  /**
   * @return {boolean}
   */
  get isDummy() {
    return this._raw.id_str == null;
  }

  get profileImageUrl() {
    return this._raw.profile_image_url;
  }

  get screenName() {
    return this._raw.screen_name;
  }

  get tweetCount() {
    return this._raw.statuses_count;
  }

  get followersCount() {
    return this._raw.followers_count;
  }

  get followingCount() {
    return this._raw.friends_count;
  }
}

export { Accounts, Account };
<commit_msg>Add JSDoc to Account<commit_after>import _ from 'lodash';
import Entity from './Entity';

class Accounts {

  /**
   * @param {[Account]} accounts
   */
  constructor(accounts) {
    this._accounts = accounts;
  }

  /**
   * @param {Object} json
   * @return {Accounts}
   */
  static fromJson(json) {
    const accounts = json.map(j => Account.fromJson(j));
    return new Accounts(accounts);
  }

  /**
   * @returns {Account|undefined}
   */
  get primary() {
    return _.chain(this._accounts)
      .filter(account => account.isPrimary)
      .head()
      .value();
  }

  /**
   * @returns {Account[] | []}
   */
  get subAccounts() {
    return _.reject(this._accounts, (a) => a.isPrimary);
  }

  /**
   * @return {Account[]}
   */
  get asArray() {
    return this._accounts;
  }

  /**
   * @returns {boolean}
   */
  get isEmpty() {
    return _.isEmpty(this._accounts) || (this._accounts.length == 1 && this._accounts[0].isDummy);
  }

}

class Account extends Entity {

  /**
   * @param {Object} raw
   */
  constructor(raw) {
    super(raw.id_str);
    this._raw = raw;
  }

  /**
   * @param {Object} userJson
   * @return {Account}
   */
  static fromJson(userJson) {
    return new this(userJson);
  }

  /**
   * @return {string}
   */
  get id() {
    return this._raw.id_str;
  }

  /**
   * @returns {Credential}
   */
  get credential() {
    return this._raw.credential;
  }

  /**
   * @return {boolean}
   */
  get isPrimary() {
    return this._raw.is_primary;
  }

  /**
   * @return {boolean}
   */
  get isDummy() {
    return this._raw.id_str == null;
  }

  /**
   * @return {string}
   */
  get profileImageUrl() {
    return this._raw.profile_image_url;
  }

  /**
   * @return {string}
   */
  get screenName() {
    return this._raw.screen_name;
  }

  /**
   * @return {number}
   */
  get tweetCount() {
    return this._raw.statuses_count;
  }

  /**
   * @return {number}
   */
  get followersCount() {
    return this._raw.followers_count;
  }

  /**
   * @return {number}
   */
  get followingCount() {
    return this._raw.friends_count;
  }
}

export { Accounts, Account };
<|endoftext|>


<commit_before>// @flow
import React, { Component } from 'react';
import { Link } from 'react-router';
import styles from './Home.css';
import { ipcRenderer } from 'electron';
import AudioList from './AudioList';
import Editor from './Editor';
import GettingStarted from './GettingStarted';
import cuid from 'cuid';
import Modal from 'react-modal';

class Home extends Component {
  props: {
    addFiles: () => void,
    updateFile: () => void,
    files: array
  };

  constructor(props) {
    super(props);

    this.state = {
      currentFileId: '',
      keyboardShortcutsModalOpen: false
    };

    this.exportToSrt = this.exportToSrt.bind(this);
    this.openDialog = this.openDialog.bind(this);
    this.updateCurrentFileId = this.updateCurrentFileId.bind(this);
    this.setTranscriptText = this.setTranscriptText.bind(this);
    this.updateTiming = this.updateTiming.bind(this);
    this.toggleKeyboardShortcutsModal = this.toggleKeyboardShortcutsModal.bind(this);
  }

  componentDidMount() {
    ipcRenderer.on('mp3-selected', (event, fileNames) => {
      if(fileNames) {
        const files = fileNames.map((filePath) => {
          return {
            filePath,
            id: cuid(),
            timing: []
          }
        });
        this.props.addFiles(files)
      }
    });

    ipcRenderer.on('session-filename', (event) => {
      event.sender.send('session-data', JSON.stringify(this.props.files));
    });

    ipcRenderer.on('session-loaded', (event, data) => {
      this.props.addFiles(JSON.parse(data));
    });
  }

  exportToSrt(ids: string[]) {
    const data = [];
    for(let id of ids) {
      const file = this.props.files.find(file => file.id === id);
      const { timing, filePath } = file;
      data.push({
        id,
        timing,
        fileName: filePath.split('\\').pop().slice(0, -4) + '.srt'
      });
    }

      ipcRenderer.send('export-to-srt', data);
    }

  openDialog() {
    ipcRenderer.send('add-mp3');
  }

  updateCurrentFileId(id) {
    this.setState({
      currentFileId: id
    });
  }

  setTranscriptText({ id, tempTiming }) {
    // id: string, tempTiming: {id: string, text: string}[]
    const timing = tempTiming.map((block) => {
        return Object.assign({}, block, { startTime: 'not set', endTime: 'not set' });
      });
    this.props.updateFile({ id, timing });
  }

  updateTiming({ id, updatedBlock }) {
    // id: string, updatedBlock: {id: string, text: string, startTime: string, endTime: string}
    const file = this.props.files.find(file => file.id === id);
    const index = file.timing.findIndex(block => block.id === updatedBlock.id);
    const timing = [
      ...file.timing.slice(0, index),
      updatedBlock,
      ...file.timing.slice(index + 1)
    ];
    this.props.updateFile({ id, timing });
  }

  toggleKeyboardShortcutsModal() {
    this.setState((prevState) => {
      return { keyboardShortcutsModalOpen: !prevState.keyboardShortcutsModalOpen };
    });
  }

  render() {
    const { files } = this.props;
    const { currentFileId, keyboardShortcutsModalOpen } = this.state;
    const currentFile = files.find(file => file.id === currentFileId);
    return (

srt maker

                <AudioList files={files} currentFileId={currentFileId} onClick={this.updateCurrentFileId} />

              {currentFileId.length ? <Editor file={currentFile} setTranscriptText={this.setTranscriptText} updateTiming={this.updateTiming} exportToSrt={this.exportToSrt}/> : <GettingStarted/>}

                <button className="btn btn-default" onClick={this.openDialog}> Add audio</button>
                <button className="btn btn-default" onClick={this.toggleKeyboardShortcutsModal}> Keyboard shortcuts</button>

        <Modal
          isOpen={keyboardShortcutsModalOpen}
          style={{
              overlay: {
                  zIndex: 3,
                  background: 'rgba(0,0,0,0.5)',
                  display: 'flex',
                  justifyContent: 'center',
                  alignItems: 'center'
              },
              content: {
                  background: 'rgba(0,0,0,0.8)',
                  position: 'static',
                  maxWidth: '600px'
              }
          }}
          contentLabel="KeyboardShortcuts">

Keyboard shortcuts

a set start time

d set end time

w select previous text block

s select next text block

spacebar toggle play/pause

            <button onClick={this.toggleKeyboardShortcutsModal}>Close</button>

        </Modal>

    );
  }
}

export default Home;<commit_msg>change current file to first file when files are added<commit_after>// @flow
import React, { Component } from 'react';
import { Link } from 'react-router';
import styles from './Home.css';
import { ipcRenderer } from 'electron';
import AudioList from './AudioList';
import Editor from './Editor';
import GettingStarted from './GettingStarted';
import cuid from 'cuid';
import Modal from 'react-modal';

class Home extends Component {
  props: {
    addFiles: () => void,
    updateFile: () => void,
    files: array
  };

  constructor(props) {
    super(props);

    this.state = {
      currentFileId: '',
      keyboardShortcutsModalOpen: false
    };

    this.exportToSrt = this.exportToSrt.bind(this);
    this.openDialog = this.openDialog.bind(this);
    this.updateCurrentFileId = this.updateCurrentFileId.bind(this);
    this.setTranscriptText = this.setTranscriptText.bind(this);
    this.updateTiming = this.updateTiming.bind(this);
    this.toggleKeyboardShortcutsModal = this.toggleKeyboardShortcutsModal.bind(this);
  }

  componentDidMount() {
    ipcRenderer.on('mp3-selected', (event, fileNames) => {
      if(fileNames) {
        const files = fileNames.map((filePath) => {
          return {
            filePath,
            id: cuid(),
            timing: []
          }
        });
        this.props.addFiles(files)
      }
    });

    ipcRenderer.on('session-filename', (event) => {
      event.sender.send('session-data', JSON.stringify(this.props.files));
    });

    ipcRenderer.on('session-loaded', (event, data) => {
      this.props.addFiles(JSON.parse(data));
    });
  }

  componentDidUpdate(prevProps, prevState) {
    if(!prevProps.files.length && this.props.files.length) {
      this.setState({ currentFileId: this.props.files[0].id });
    }
  }

  exportToSrt(ids: string[]) {
    const data = [];
    for(let id of ids) {
      const file = this.props.files.find(file => file.id === id);
      const { timing, filePath } = file;
      data.push({
        id,
        timing,
        fileName: filePath.split('\\').pop().slice(0, -4) + '.srt'
      });
    }

      ipcRenderer.send('export-to-srt', data);
    }

  openDialog() {
    ipcRenderer.send('add-mp3');
  }

  updateCurrentFileId(id) {
    this.setState({
      currentFileId: id
    });
  }

  setTranscriptText({ id, tempTiming }) {
    // id: string, tempTiming: {id: string, text: string}[]
    const timing = tempTiming.map((block) => {
        return Object.assign({}, block, { startTime: 'not set', endTime: 'not set' });
      });
    this.props.updateFile({ id, timing });
  }

  updateTiming({ id, updatedBlock }) {
    // id: string, updatedBlock: {id: string, text: string, startTime: string, endTime: string}
    const file = this.props.files.find(file => file.id === id);
    const index = file.timing.findIndex(block => block.id === updatedBlock.id);
    const timing = [
      ...file.timing.slice(0, index),
      updatedBlock,
      ...file.timing.slice(index + 1)
    ];
    this.props.updateFile({ id, timing });
  }

  toggleKeyboardShortcutsModal() {
    this.setState((prevState) => {
      return { keyboardShortcutsModalOpen: !prevState.keyboardShortcutsModalOpen };
    });
  }

  render() {
    const { files } = this.props;
    const { currentFileId, keyboardShortcutsModalOpen } = this.state;
    const currentFile = files.find(file => file.id === currentFileId);
    return (

srt maker

                <AudioList files={files} currentFileId={currentFileId} onClick={this.updateCurrentFileId} />

              {currentFileId.length ? <Editor file={currentFile} setTranscriptText={this.setTranscriptText} updateTiming={this.updateTiming} exportToSrt={this.exportToSrt}/> : <GettingStarted/>}

                <button className="btn btn-default" onClick={this.openDialog}> Add audio</button>
                <button className="btn btn-default" onClick={this.toggleKeyboardShortcutsModal}> Keyboard shortcuts</button>

        <Modal
          isOpen={keyboardShortcutsModalOpen}
          style={{
              overlay: {
                  zIndex: 3,
                  background: 'rgba(0,0,0,0.5)',
                  display: 'flex',
                  justifyContent: 'center',
                  alignItems: 'center'
              },
              content: {
                  background: 'rgba(0,0,0,0.8)',
                  position: 'static',
                  maxWidth: '600px'
              }
          }}
          contentLabel="KeyboardShortcuts">

Keyboard shortcuts

a set start time

d set end time

w select previous text block

s select next text block

spacebar toggle play/pause

            <button onClick={this.toggleKeyboardShortcutsModal}>Close</button>

        </Modal>

    );
  }
}

export default Home;<|endoftext|>


<commit_before>// Include React and React-Router
import React from 'react';
import { HashRouter, Route, Redirect } from 'react-router-dom';
import axios from 'axios';

// Import components
import Login from './children/Login';
import SignUp from './children/SignUp';
import Home from './children/Home';
import Dashboard from './children/Dashboard';
import FormBuilder from './children/FormBuilder';
import OutsiderView from './children/OutsiderView';

// Create Main component
class Main extends React.Component {
    constructor() {
        super();
        this.state = {
            ready: false,
            signedUp: undefined,
            loggedIn: undefined,
            forms: []
        };
        this.handleSignup = this.handleSignup.bind(this);
        this.handleLogin = this.handleLogin.bind(this);
        this.handleLogout = this.handleLogout.bind(this);
        this.getFormToEdit = this.getFormToEdit.bind(this);
        this.getUserData = this.getUserData.bind(this);
        this.getUserForms = this.getUserForms.bind(this);
    }

    componentDidMount() {
        this.getUserData();
    }

    getUserData() {
        axios.get('/api/data').then((response) => {
            console.log(response);
            this.setState({
                loggedIn: response.data.success,
                forms: response.data.forms,
                ready: true
            });
        }).catch((err) => {
            if (err) console.log(err);
            this.setState({
                loggedIn: false,
                ready: true
            });
        });
    }

    getUserForms() {
        axios.get('/api/data').then((response) => {
            console.log(response);
            this.setState({
                forms: response.data.forms
            });
        }).catch((err) => {
            if (err) console.log(err);
            this.setState({
                loggedIn: false,
                ready: true
            });
        });
    }

    handleSignup(isSignUpSuccess) {
        this.setState({ signedUp: isSignUpSuccess });
    }

    handleLogin(isLogInSuccess) {
        this.setState({ loggedIn: isLogInSuccess });
    }

    handleLogout() {
        axios.get('/auth/logout').then((response) => {
            this.setState({ loggedIn: false });
        });
    }

    getFormToEdit(index) {
        return this.state.forms[index]
    }

    render() {
        return (
            <HashRouter>

                            Formulate
                            {this.state.loggedIn ? Logout :
}

                    {/*Once we have checked to see if the user is authenticated*/}
                    {this.state.ready ? (

                        {/*If they are looged in, redirect to dashboard. Else, show home page*/}
                        <Route exact path='/' component={() => (this.state.loggedIn ?
                            <Redirect to="/dashboard" /> :
                            <Home />
                        )} />
                        {/*If logged in, redirect to dashboard, else show login page*/}
                        <Route path='/login' component={() => (this.state.loggedIn ?
                            <Redirect to="/dashboard" /> :
                            <Login handleResponse={this.handleLogin} />
                        )} />
                        {/*If logged in, redirect to dashboard, else show signup page*/}
                        <Route path='/signup' component={() => (this.state.loggedIn ?
                            <Redirect to="/dashboard" /> :
                            <SignUp handleResponse={this.handleSignup} />
                        )} />
                        {/*If logged in, go to dashboard, else return to home page*/}
                        <Route path='/dashboard' component={() => (this.state.loggedIn ?
                            <Dashboard getUserForms={this.getUserForms} forms={this.state.forms} /> :
                            <Redirect to='/' />
                        )} />
                        <Route path='/form-builder/:status/:target/:index?' component={({ match, history }) => (this.state.loggedIn ?
                            <FormBuilder getUserForms={this.getUserForms} getFormToEdit={this.getFormToEdit} match={match} history={history} /> :
                            <Redirect to='/' />
                        )} />
                        <Route path='/published/:id' component={({ match }) => <OutsiderView match={match} />} />

) :
{/*If we havent heard from the server yet, show an empty div*/}
}

            </HashRouter>
        );
    }
}

// Export the component back for use in other files
export default Main;<commit_msg>Fixed dashboard forms bug<commit_after>// Include React and React-Router
import React from 'react';
import { HashRouter, Route, Redirect } from 'react-router-dom';
import axios from 'axios';

// Import components
import Login from './children/Login';
import SignUp from './children/SignUp';
import Home from './children/Home';
import Dashboard from './children/Dashboard';
import FormBuilder from './children/FormBuilder';
import OutsiderView from './children/OutsiderView';

// Create Main component
class Main extends React.Component {
    constructor() {
        super();
        this.state = {
            ready: false,
            signedUp: undefined,
            loggedIn: undefined,
            forms: []
        };
        this.handleSignup = this.handleSignup.bind(this);
        this.handleLogin = this.handleLogin.bind(this);
        this.handleLogout = this.handleLogout.bind(this);
        this.getFormToEdit = this.getFormToEdit.bind(this);
        this.getUserData = this.getUserData.bind(this);
        this.getUserForms = this.getUserForms.bind(this);
    }

    componentDidMount() {
        this.getUserData();
    }

    getUserData() {
        axios.get('/api/data').then((response) => {
            console.log(response);
            this.getUserForms();

        }).catch((err) => {
            if (err) console.log(err);
            this.setState({
                loggedIn: false,
                ready: true
            });
        });
    }

    getUserForms() {
        axios.get('/api/data').then((response) => {
            console.log(response);
            this.setState({
                loggedIn: response.data.success,
                forms: response.data.forms,
                ready: true
            });
        }).catch((err) => {
            if (err) console.log(err);
            this.setState({
                loggedIn: false,
                ready: true
            });
        });
    }

    handleSignup(isSignUpSuccess) {
        this.setState({ signedUp: isSignUpSuccess });
    }

    handleLogin(isLogInSuccess) {
        this.setState({ loggedIn: isLogInSuccess });
    }

    handleLogout() {
        axios.get('/auth/logout').then((response) => {
            this.setState({ loggedIn: false });
        });
    }

    getFormToEdit(index) {
        return this.state.forms[index]
    }

    render() {
        return (
            <HashRouter>

                            Formulate
                            {this.state.loggedIn ? Logout :
}

                    {/*Once we have checked to see if the user is authenticated*/}
                    {this.state.ready ? (

                        {/*If they are looged in, redirect to dashboard. Else, show home page*/}
                        <Route exact path='/' component={() => (this.state.loggedIn ?
                            <Redirect to="/dashboard" /> :
                            <Home />
                        )} />
                        {/*If logged in, redirect to dashboard, else show login page*/}
                        <Route path='/login' component={() => (this.state.loggedIn ?
                            <Redirect to="/dashboard" /> :
                            <Login handleResponse={this.handleLogin} />
                        )} />
                        {/*If logged in, redirect to dashboard, else show signup page*/}
                        <Route path='/signup' component={() => (this.state.loggedIn ?
                            <Redirect to="/dashboard" /> :
                            <SignUp handleResponse={this.handleSignup} />
                        )} />
                        {/*If logged in, go to dashboard, else return to home page*/}
                        <Route path='/dashboard' component={() => (this.state.loggedIn ?
                            <Dashboard getUserForms={this.getUserForms} forms={this.state.forms} /> :
                            <Redirect to='/' />
                        )} />
                        <Route path='/form-builder/:status/:target/:index?' component={({ match, history }) => (this.state.loggedIn ?
                            <FormBuilder getUserForms={this.getUserForms} getFormToEdit={this.getFormToEdit} match={match} history={history} /> :
                            <Redirect to='/' />
                        )} />
                        <Route path='/published/:id' component={({ match }) => <OutsiderView match={match} />} />

) :
{/*If we havent heard from the server yet, show an empty div*/}
}

            </HashRouter>
        );
    }
}

// Export the component back for use in other files
export default Main;<|endoftext|>


<commit_before>import React, { Component } from 'react';
import LoadingOverlay from './LoadingOverlay'
import AppBar from './AppBar'
import Popup from './Popup'
import Player from './Player'
import '../styles/Main.global.css';

class Main extends Component {

  render() {
    document.body.classList.toggle('dark-theme', true)
    return (

        <AppBar client={this.props.client} profiles={this.props.profiles} />
        <LoadingOverlay {...this.props} />
        <Popup popup={this.props.popup} popupShow={this.props.popupShow} popupHide={this.props.popupHide} />

          {React.cloneElement(this.props.children, this.props)}

        {this.props.player.songs.length > 0 &&
          <Player
          playerPlayPause={this.props.playerPlayPause}
          player={this.props.player}
          profile={this.props.profiles[this.props.client.selectedProfileIndex]}
          />
        }

    )
  }
}

export default Main
<commit_msg>add player previous and next props to Player<commit_after>import React, { Component } from 'react';
import LoadingOverlay from './LoadingOverlay'
import AppBar from './AppBar'
import Popup from './Popup'
import Player from './Player'
import '../styles/Main.global.css';

class Main extends Component {

  render() {
    document.body.classList.toggle('dark-theme', true)
    return (

        <AppBar client={this.props.client} profiles={this.props.profiles} />
        <LoadingOverlay {...this.props} />
        <Popup popup={this.props.popup} popupShow={this.props.popupShow} popupHide={this.props.popupHide} />

          {React.cloneElement(this.props.children, this.props)}

        {this.props.player.songs.length > 0 &&
          <Player
          playerPlayPause={this.props.playerPlayPause}
          playerPrevious={this.props.playerPrevious}
          playerNext={this.props.playerNext}
          player={this.props.player}
          profile={this.props.profiles[this.props.client.selectedProfileIndex]}
          />
        }

    )
  }
}

export default Main
<|endoftext|>


<commit_before>'use strict';

var _ = require('underscore'),
	moment = require('moment'),
	path = require('path'),
	fdaAdapter = require(path.join(global.__adptsdir, 'fdaapi'));

/**
 * Converts input to a valid integer or undefined
 * @param input
 * @returns {Number|undefined}
 * @private
 */
function _validateNumber(input) {
	var temp;
	try {
		temp = parseInt(input, 10);
		if (!_.isFinite(temp)) {
			temp = undefined;
		}
	} catch(e) {
		return undefined;
	}

	return temp;
}

/**
 * Sends invalid argument error to client
 * @param message
 * @param res
 * @private
 */
function _rejectArgument(message, res) {
	res.status(409).json({
		error: {
			code: 'INVALID_ARGUMENT',
			message: message
		}
	});
}

/**
 * Outputs response from database and handles errors
 * @param {Promise} promise
 * @param res
 * @private
 */
function _processResponse(promise, res) {
	promise.then(function (foodResult) {
		res.json(foodResult);
	}).catch(function (err) {
		if (err.message) {
			_rejectArgument(err.message, res);
		} else {
			res.status(err.statusCode).json({
				error: err.error
			});
		}
	}).done();
}

/**
 * Gets recall for specific id
 * @param req
 * @param {Number} req.params.id
 * @param res
 */
exports.getRecallById = function (req, res) {
	var obj = {};

	obj.id = req.params.id; // TODO validate against pattern?

	if (req.query.skip) {
		_rejectArgument('Invalid skip - not allowed', res);
		return;
	}

	if (req.query.limit) {
		_rejectArgument('Invalid limit - not allowed', res);
		return;
	}

	_processResponse(fdaAdapter.getFoodRecallById(obj), res);
};

/* TODO
 * Filter results
 * - nationwide match: make sure there is no state designation
 * - filter out 'no' when found in distribution?
 */
/**
 * Gets recalls for matches against provided input
 * @param req
 * @param {String} [req.query.state]
 * @param {String} [req.query.from]
 * @param {String} [req.query.to]
 * @param {String[]|String} [req.query.classificationlevels]
 * @param {String[]} [req.query.keywords]
 * @param res
 */
exports.getRecalls = function (req, res) {
	var obj = {};

	if (req.query.state) {
		if (!fdaAdapter.isValidState(req.query.state)) {
			_rejectArgument('Invalid state', res);
			return;
		}
		obj.state = req.query.state;
	}

	if (req.query.eventid) {
		obj.eventid = _validateNumber(req.query.eventid);
		if (_.isUndefined(obj.eventid)) {
			_rejectArgument('Invalid eventid', res);
			return;
		}
	}

	if (req.query.firmname) {
		obj.firmname = req.query.firmname;
	}

	if (req.query.from || req.query.to) {
		obj.from = _validateNumber(req.query.from);
		if (_.isUndefined(obj.from) || !moment.unix(obj.from).isValid()) {
			_rejectArgument('Invalid from', res);
			return;
		}
		obj.to = _validateNumber(req.query.to);
		if (_.isUndefined(obj.to) || !moment.unix(obj.to).isValid()) {
			_rejectArgument('Invalid to', res);
			return;
		}

		if (obj.from >= obj.to) {
			_rejectArgument('Invalid from/to - from must be before to', res);
		}
	}

	if (req.query.classificationlevels) {
		if (!_.isArray(req.query.classificationlevels)) {
			req.query.classificationlevels = req.query.classificationlevels.split(',');
		}

		obj.classificationlevels = [];

		var isInvalid = false;
		_.each(req.query.classificationlevels, function (level) {
			var temp = _validateNumber(level);
			if (_.isUndefined(temp)) {
				_rejectArgument('Invalid classificationlevels', res);
				isInvalid = true;
				return null;
			}

			if (temp < 1 || temp > 3) {
				_rejectArgument('Invalid classificationlevels - must be 1, 2, or 3', res);
				isInvalid = true;
				return null;
			}

			obj.classificationlevels.push(temp);
		});
		if (isInvalid) {
			return;
		}
	}

	if (req.query.keywords) {
		if (!_.isArray(req.query.keywords)) {
			_rejectArgument('Invalid keywords', res);
			return;
		}
		var invalid = fdaAdapter.areValidKeywords(req.query.keywords);
		if (invalid !== undefined) {
			_rejectArgument('Invalid keywords - could not match keyword ' + invalid, res);
			return;
		}
		obj.keywords = req.query.keywords;
	}

	if (req.query.skip) {
		obj.skip = _validateNumber(req.query.skip);
		if (_.isUndefined(obj.skip)) {
			_rejectArgument('Invalid skip', res);
			return;
		}
	}

	if (req.query.limit) {
		obj.limit = _validateNumber(req.query.limit);
		if (_.isUndefined(obj.limit)) {
			_rejectArgument('Invalid limit', res);
			return;
		}
	}

	_processResponse(fdaAdapter.searchFoodRecalls(obj), res);
};

/**
 * Gets counts
 * @param req
 * @param {String} req.query.field
 * @param {String} [req.query.state]
 * @param {String} [req.query.status]
 * @param res
 */
exports.getRecallsCounts = function (req, res) {
	var obj = {};

	if (!req.query.field || !fdaAdapter.isValidCountField(req.query.field)) {
		_rejectArgument('Invalid field', res);
		return;
	}
	obj.field = req.query.field;

	if (req.query.state) {
		if (!fdaAdapter.isValidState(req.query.state)) {
			_rejectArgument('Invalid state', res);
			return;
		}
		obj.state = req.query.state;
	}

	if (req.query.status) {
		if (!fdaAdapter.isValidStatus(req.query.status)) {
			_rejectArgument('Invalid status', res);
			return;
		}
		obj.status = req.query.status;
	}

	if (req.query.skip) {
		_rejectArgument('Invalid skip - not allowed', res);
		return;
	}

	if (req.query.limit) {
		_rejectArgument('Invalid limit - not allowed', res);
		return;
	}

	_processResponse(fdaAdapter.getFoodRecallsCounts(obj), res);
};
<commit_msg>fixing classification levels for real<commit_after>'use strict';

var _ = require('underscore'),
	moment = require('moment'),
	path = require('path'),
	fdaAdapter = require(path.join(global.__adptsdir, 'fdaapi'));

/**
 * Converts input to a valid integer or undefined
 * @param input
 * @returns {Number|undefined}
 * @private
 */
function _validateNumber(input) {
	var temp;
	try {
		temp = parseInt(input, 10);
		if (!_.isFinite(temp)) {
			temp = undefined;
		}
	} catch(e) {
		return undefined;
	}

	return temp;
}

/**
 * Sends invalid argument error to client
 * @param message
 * @param res
 * @private
 */
function _rejectArgument(message, res) {
	res.status(409).json({
		error: {
			code: 'INVALID_ARGUMENT',
			message: message
		}
	});
}

/**
 * Outputs response from database and handles errors
 * @param {Promise} promise
 * @param res
 * @private
 */
function _processResponse(promise, res) {
	promise.then(function (foodResult) {
		res.json(foodResult);
	}).catch(function (err) {
		if (err.message) {
			_rejectArgument(err.message, res);
		} else {
			res.status(err.statusCode).json({
				error: err.error
			});
		}
	}).done();
}

/**
 * Gets recall for specific id
 * @param req
 * @param {Number} req.params.id
 * @param res
 */
exports.getRecallById = function (req, res) {
	var obj = {};

	obj.id = req.params.id; // TODO validate against pattern?

	if (req.query.skip) {
		_rejectArgument('Invalid skip - not allowed', res);
		return;
	}

	if (req.query.limit) {
		_rejectArgument('Invalid limit - not allowed', res);
		return;
	}

	_processResponse(fdaAdapter.getFoodRecallById(obj), res);
};

/* TODO
 * Filter results
 * - nationwide match: make sure there is no state designation
 * - filter out 'no' when found in distribution?
 */
/**
 * Gets recalls for matches against provided input
 * @param req
 * @param {String} [req.query.state]
 * @param {String} [req.query.from]
 * @param {String} [req.query.to]
 * @param {String[]|String} [req.query.classificationlevels]
 * @param {String[]} [req.query.keywords]
 * @param res
 */
exports.getRecalls = function (req, res) {
	var obj = {};

	if (req.query.state) {
		if (!fdaAdapter.isValidState(req.query.state)) {
			_rejectArgument('Invalid state', res);
			return;
		}
		obj.state = req.query.state;
	}

	if (req.query.eventid) {
		obj.eventid = _validateNumber(req.query.eventid);
		if (_.isUndefined(obj.eventid)) {
			_rejectArgument('Invalid eventid', res);
			return;
		}
	}

	if (req.query.firmname) {
		obj.firmname = req.query.firmname;
	}

	if (req.query.from || req.query.to) {
		obj.from = _validateNumber(req.query.from);
		if (_.isUndefined(obj.from) || !moment.unix(obj.from).isValid()) {
			_rejectArgument('Invalid from', res);
			return;
		}
		obj.to = _validateNumber(req.query.to);
		if (_.isUndefined(obj.to) || !moment.unix(obj.to).isValid()) {
			_rejectArgument('Invalid to', res);
			return;
		}

		if (obj.from >= obj.to) {
			_rejectArgument('Invalid from/to - from must be before to', res);
		}
	}

	if (req.query.classificationlevels) {
		if (!_.isArray(req.query.classificationlevels)) {
			req.query.classificationlevels = req.query.classificationlevels.split(',');
		}

		obj.classificationlevels = [];

		var i = 0;
		for (; i < req.query.classificationlevels.length; i++) {
			var temp = _validateNumber(req.query.classificationlevels[i]);
			if (_.isUndefined(temp)) {
				_rejectArgument('Invalid classificationlevels', res);
				return;
			}

			if (temp < 1 || temp > 3) {
				_rejectArgument('Invalid classificationlevels - must be 1, 2, or 3', res);
				return;
			}

			obj.classificationlevels.push(temp);
		}
	}

	if (req.query.keywords) {
		if (!_.isArray(req.query.keywords)) {
			req.query.keywords = req.query.keywords.split(',');
		}
		var invalid = fdaAdapter.areValidKeywords(req.query.keywords);
		if (invalid !== undefined) {
			_rejectArgument('Invalid keywords - could not match keyword ' + invalid, res);
			return;
		}
		obj.keywords = req.query.keywords;
	}

	if (req.query.skip) {
		obj.skip = _validateNumber(req.query.skip);
		if (_.isUndefined(obj.skip)) {
			_rejectArgument('Invalid skip', res);
			return;
		}
	}

	if (req.query.limit) {
		obj.limit = _validateNumber(req.query.limit);
		if (_.isUndefined(obj.limit)) {
			_rejectArgument('Invalid limit', res);
			return;
		}
	}

	_processResponse(fdaAdapter.searchFoodRecalls(obj), res);
};

/**
 * Gets counts
 * @param req
 * @param {String} req.query.field
 * @param {String} [req.query.state]
 * @param {String} [req.query.status]
 * @param res
 */
exports.getRecallsCounts = function (req, res) {
	var obj = {};

	if (!req.query.field || !fdaAdapter.isValidCountField(req.query.field)) {
		_rejectArgument('Invalid field', res);
		return;
	}
	obj.field = req.query.field;

	if (req.query.state) {
		if (!fdaAdapter.isValidState(req.query.state)) {
			_rejectArgument('Invalid state', res);
			return;
		}
		obj.state = req.query.state;
	}

	if (req.query.status) {
		if (!fdaAdapter.isValidStatus(req.query.status)) {
			_rejectArgument('Invalid status', res);
			return;
		}
		obj.status = req.query.status;
	}

	if (req.query.skip) {
		_rejectArgument('Invalid skip - not allowed', res);
		return;
	}

	if (req.query.limit) {
		_rejectArgument('Invalid limit - not allowed', res);
		return;
	}

	_processResponse(fdaAdapter.getFoodRecallsCounts(obj), res);
};
<|endoftext|>


<commit_before>// CartoDB SQL API
//
// all requests expect the following URL args:
// - `sql` {String} SQL to execute
//
// for private (read/write) queries:
// - OAuth. Must have proper OAuth 1.1 headers. For OAuth 1.1 spec see Google
//
// eg. /api/v1/?sql=SELECT 1 as one (with a load of OAuth headers or URL arguments)
//
// for public (read only) queries:
// - sql only, provided the subdomain exists in CartoDB and the table's sharing options are public
//
// eg. vizzuality.cartodb.com/api/v1/?sql=SELECT * from my_table

var express= require('express')
    , app      = express.createServer(
    express.logger({buffer:true,
        format:'[:req[X-Real-IP] :date] \033[90m:method\033[0m \033[36m:url\033[0m \033[90m:status :response-time ms -> :res[Content-Type]\033[0m'}))
    , Step     = require('step')
    , Meta     = require(global.settings.app_root + '/app/models/metadata')
    , oAuth    = require(global.settings.app_root + '/app/models/oauth')
    , PSQL     = require(global.settings.app_root + '/app/models/psql')
    , _        = require('underscore');

app.use(express.bodyParser());
app.enable('jsonp callback');

app.all('/api/v1/sql',  function(req, res) { handleQuery(req, res) } );
app.all('/api/v1/sql.:f',  function(req, res) { handleQuery(req, res) } );
function handleQuery(req, res){

    // sanitize input
    var body      = (req.body) ? req.body : {};
    var sql       = req.query.q || body.q; // get and post
    var database  = req.query.database; // deprecate this in future
    var limit     = parseInt(req.query.rows_per_page);
    var offset    = parseInt(req.query.page);
    var format    = (req.query.format) ? req.query.format : null;
    var dp        = (req.query.dp) ? req.query.dp: '15';

    // validate input slightly
    dp        = (dp=== "")        ? '15' : dp;
    format    = (format === "")   ? null : format;
    sql       = (sql === "")      ? null : sql;
    database  = (database === "") ? null : database;
    limit     = (_.isNumber(limit))  ? limit : null;
    offset    = (_.isNumber(offset)) ? offset * limit : null

    // setup step run
    var that  = this;
    var start = new Date().getTime();

    try {
        if (!_.isString(sql)) throw new Error("You must indicate a sql query");
        var pg;

        // 1. Get database from redis via the username stored in the host header subdomain
        // 2. Run the request through OAuth to get R/W user id if signed
        // 3. Run query with r/w or public user
        // 4. package results and send back
        Step(
            function getDatabaseName(){
                Meta.getDatabase(req, this);
            },
            function setDBGetUser(err, data) {
                if (err) throw err;
                database = (data == "" || _.isNull(data)) ? database : data;
                oAuth.verifyRequest(req, this);
            },
            function querySql(err, user_id){
                if (err) throw err;
                pg = new PSQL(user_id, database, limit, offset);

                // TODO: refactor formats to external object
                if (format === 'geojson'){
                    sql = ['SELECT *, ST_AsGeoJSON(the_geom,',dp,') as the_geom FROM (', sql, ') as foo'].join("");
                }

                pg.query(sql, this);
            },
            function packageResults(err, result){
                if (err) throw err;

                // TODO: refactor formats to external object
                if (format === 'geojson'){
                    toGeoJSON(result, res, this);
                } else {
                    var end = new Date().getTime();
                    return {
                        'time' : ((end - start)/1000),
                        'total_rows': result.rows.length,
                        'rows'      : result.rows
                    };
                }
            },
            function sendResults(err, out){
                if (err) throw err;

                // configure headers for geojson
                res.header("Content-Disposition", getContentDisposition(format));

                // allow cross site post
                res.header("Access-Control-Allow-Origin", "*");
                res.header("Access-Control-Allow-Headers", "X-Requested-With");

                // set cache headers
                res.header('Last-Modified', new Date().toUTCString());
                res.header('Cache-Control', 'no-cache,max-age=3600,must-revalidate, public');
                res.header('X-Cache-Channel', database);

                // return to browser
                res.send(out);
            },
            function errorHandle(err, result){
                handleException(err, res);
            }
        );
    } catch (err) {
        console.log('[ERROR]\n' + err);
        handleException(err, res);
    }
}

function toGeoJSON(data, res, callback){
    try{
        var out = {
            type: "FeatureCollection",
            features: []
        };

        _.each(data.rows, function(ele){
            var geojson = {
                type: "Feature",
                properties: { },
                geometry: { }
            };
            geojson.geometry = JSON.parse(ele["the_geom"]);
            delete ele["the_geom"];
            delete ele["the_geom_webmercator"];
            geojson.properties = ele;
            out.features.push(geojson);
        });

        // return payload
        callback(null, out);
    } catch (err) {
        callback(err,null);
    }
}

function getContentDisposition(format){
    var ext = (format === 'geojson') ? 'geojson' : 'json';
    var time = new Date().toUTCString();
    return 'inline; filename=cartodb-query.' + ext + '; modification-date="' + time + '";';
}

function handleException(err, res){
    var msg = (global.settings.environment == 'development') ? {error:[err.message], stack: err.stack} : {error:[err.message]}
    if (global.settings.environment !== 'test'){
        // TODO: email this Exception report
        console.log("EXCEPTION REPORT")
        console.log(err.message);
        console.log(err.stack);
    }

    // if the exception defines a http status code, use that, else a 500
    if (!_.isUndefined(err.http_status)){
        res.send(msg, err.http_status);
    } else {
        res.send(msg, 400);
    }
}

module.exports = app;
<commit_msg>add host to log<commit_after>// CartoDB SQL API
//
// all requests expect the following URL args:
// - `sql` {String} SQL to execute
//
// for private (read/write) queries:
// - OAuth. Must have proper OAuth 1.1 headers. For OAuth 1.1 spec see Google
//
// eg. /api/v1/?sql=SELECT 1 as one (with a load of OAuth headers or URL arguments)
//
// for public (read only) queries:
// - sql only, provided the subdomain exists in CartoDB and the table's sharing options are public
//
// eg. vizzuality.cartodb.com/api/v1/?sql=SELECT * from my_table

var express= require('express')
    , app      = express.createServer(
    express.logger({buffer:true,
        format:'[:req[X-Real-IP] - :req[http_host] :date] \033[90m:method\033[0m \033[36m:url\033[0m \033[90m:status :response-time ms -> :res[Content-Type]\033[0m'}))
    , Step     = require('step')
    , Meta     = require(global.settings.app_root + '/app/models/metadata')
    , oAuth    = require(global.settings.app_root + '/app/models/oauth')
    , PSQL     = require(global.settings.app_root + '/app/models/psql')
    , _        = require('underscore');

app.use(express.bodyParser());
app.enable('jsonp callback');

app.all('/api/v1/sql',  function(req, res) { handleQuery(req, res) } );
app.all('/api/v1/sql.:f',  function(req, res) { handleQuery(req, res) } );
function handleQuery(req, res){

    // sanitize input
    var body      = (req.body) ? req.body : {};
    var sql       = req.query.q || body.q; // get and post
    var database  = req.query.database; // deprecate this in future
    var limit     = parseInt(req.query.rows_per_page);
    var offset    = parseInt(req.query.page);
    var format    = (req.query.format) ? req.query.format : null;
    var dp        = (req.query.dp) ? req.query.dp: '15';

    // validate input slightly
    dp        = (dp=== "")        ? '15' : dp;
    format    = (format === "")   ? null : format;
    sql       = (sql === "")      ? null : sql;
    database  = (database === "") ? null : database;
    limit     = (_.isNumber(limit))  ? limit : null;
    offset    = (_.isNumber(offset)) ? offset * limit : null

    // setup step run
    var that  = this;
    var start = new Date().getTime();

    try {
        if (!_.isString(sql)) throw new Error("You must indicate a sql query");
        var pg;

        // 1. Get database from redis via the username stored in the host header subdomain
        // 2. Run the request through OAuth to get R/W user id if signed
        // 3. Run query with r/w or public user
        // 4. package results and send back
        Step(
            function getDatabaseName(){
                Meta.getDatabase(req, this);
            },
            function setDBGetUser(err, data) {
                if (err) throw err;
                database = (data == "" || _.isNull(data)) ? database : data;
                oAuth.verifyRequest(req, this);
            },
            function querySql(err, user_id){
                if (err) throw err;
                pg = new PSQL(user_id, database, limit, offset);

                // TODO: refactor formats to external object
                if (format === 'geojson'){
                    sql = ['SELECT *, ST_AsGeoJSON(the_geom,',dp,') as the_geom FROM (', sql, ') as foo'].join("");
                }

                pg.query(sql, this);
            },
            function packageResults(err, result){
                if (err) throw err;

                // TODO: refactor formats to external object
                if (format === 'geojson'){
                    toGeoJSON(result, res, this);
                } else {
                    var end = new Date().getTime();
                    return {
                        'time' : ((end - start)/1000),
                        'total_rows': result.rows.length,
                        'rows'      : result.rows
                    };
                }
            },
            function sendResults(err, out){
                if (err) throw err;

                // configure headers for geojson
                res.header("Content-Disposition", getContentDisposition(format));

                // allow cross site post
                res.header("Access-Control-Allow-Origin", "*");
                res.header("Access-Control-Allow-Headers", "X-Requested-With");

                // set cache headers
                res.header('Last-Modified', new Date().toUTCString());
                res.header('Cache-Control', 'no-cache,max-age=3600,must-revalidate, public');
                res.header('X-Cache-Channel', database);

                // return to browser
                res.send(out);
            },
            function errorHandle(err, result){
                handleException(err, res);
            }
        );
    } catch (err) {
        console.log('[ERROR]\n' + err);
        handleException(err, res);
    }
}

function toGeoJSON(data, res, callback){
    try{
        var out = {
            type: "FeatureCollection",
            features: []
        };

        _.each(data.rows, function(ele){
            var geojson = {
                type: "Feature",
                properties: { },
                geometry: { }
            };
            geojson.geometry = JSON.parse(ele["the_geom"]);
            delete ele["the_geom"];
            delete ele["the_geom_webmercator"];
            geojson.properties = ele;
            out.features.push(geojson);
        });

        // return payload
        callback(null, out);
    } catch (err) {
        callback(err,null);
    }
}

function getContentDisposition(format){
    var ext = (format === 'geojson') ? 'geojson' : 'json';
    var time = new Date().toUTCString();
    return 'inline; filename=cartodb-query.' + ext + '; modification-date="' + time + '";';
}

function handleException(err, res){
    var msg = (global.settings.environment == 'development') ? {error:[err.message], stack: err.stack} : {error:[err.message]}
    if (global.settings.environment !== 'test'){
        // TODO: email this Exception report
        console.log("EXCEPTION REPORT")
        console.log(err.message);
        console.log(err.stack);
    }

    // if the exception defines a http status code, use that, else a 500
    if (!_.isUndefined(err.http_status)){
        res.send(msg, err.http_status);
    } else {
        res.send(msg, 400);
    }
}

module.exports = app;
<|endoftext|>


<commit_before>function forwardAuthentication(){

	function castBallot(req){
	    var manifest = JSON.parse(sessionStorage.getItem("manifest"));
            // Make an (ajax) cast request:
            $.post(manifest.collectingServer.URI+"/cast", req)
		.fail(function otpRequestFailed() {  // request failed
		    showError('Cannot connect with the server');
		})
		.done(function castRequestDone(result) {  // the signed acknowledgment
		    parent.postMessage({result: result}, "*");
		});
	}

	window.addEventListener('message',function(event) {
	    //console.log("frame recieved: "+event.data);
	    if(event.data.hasOwnProperty("manifest")){
		sessionStorage.setItem("manifest", JSON.stringify(event.data.manifest));
	    }
	    else if(event.data === "loaded"){
		var manifest = JSON.parse(sessionStorage.getItem("manifest"));
		parent.postMessage({manifest: manifest}, "*");
	    }
	    else if(event.data.hasOwnProperty("credentials")){
		sessionStorage.setItem("toBeSubmitted", JSON.stringify(event.data.credentials));
	    }
	    else if(event.data.hasOwnProperty("ballot")){
		var submit = JSON.parse(sessionStorage.getItem("toBeSubmitted"));
		var manifest = JSON.parse(sessionStorage.getItem("manifest"));
		submit.electionID = manifest.hash;
		submit.ballot = event.data.ballot;
		castBallot(submit);
	    }
	},false);

}
<commit_msg>comment and quality of the code improved<commit_after>function forwardAuthentication(){

	function castBallot(req){
	    var manifest = JSON.parse(sessionStorage.getItem("manifest"));
	    // Make an (ajax) cast request:
            $.post(manifest.collectingServer.URI+"/cast", req)
		.fail(function otpRequestFailed() { // request failed
		    showError('Cannot connect with the server');
		})
		.done(function castRequestDone(result) { // the signed acknowledgment
		    parent.postMessage({result: result}, "*");
		});
	}

	window.addEventListener('message',function(event) {
	    //console.log("frame recieved: "+event.data);
	    if(event.data.hasOwnProperty("manifest")){
		sessionStorage.setItem("manifest", JSON.stringify(event.data.manifest));
	    }
	    else if(event.data === "loaded"){
		// when the parent page is 'loaded', provide to this page the election manifest
		var manifest = JSON.parse(sessionStorage.getItem("manifest"));
		parent.postMessage({manifest: manifest}, "*");
	    }
	    else if(event.data.hasOwnProperty("credentials")){
		// email and otp
		sessionStorage.setItem("toBeSubmitted", JSON.stringify(event.data.credentials));
	    }
	    else if(event.data.hasOwnProperty("ballot")){
		// electionID and ballot
		var submit = JSON.parse(sessionStorage.getItem("toBeSubmitted"));
		var manifest = JSON.parse(sessionStorage.getItem("manifest"));
		submit.electionID = manifest.hash;
		submit.ballot = event.data.ballot;
		// submit := {email, otp, electionID, ballot}
		castBallot(submit);
	    }
	},false);
}
<|endoftext|>