<commit_before>// Copyright 2017 The Oppia Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Page object for the admin page, for use in Protractor
 * tests.
 */

var action = require('./action.js');
var forms = require('./forms.js');
var general = require('./general.js');
var waitFor = require('./waitFor.js');

var AdminPage = function() {
  var ADMIN_URL_SUFFIX = '/admin';
  var REVIEW_CATEGORY_TRANSLATION = 'TRANSLATION';
  var REVIEW_CATEGORY_VOICEOVER = 'VOICEOVER';
  var REVIEW_CATEGORY_QUESTION = 'QUESTION';

  var configTab = element(by.css('.protractor-test-admin-config-tab'));
  var saveAllConfigs = element(by.css('.protractor-test-save-all-configs'));
  var configProperties = element.all(by.css(
    '.protractor-test-config-property'
  ));
  var adminRolesTab = element(by.css('.protractor-test-admin-roles-tab'));
  var adminRolesTabContainer = element(
    by.css('.protractor-test-roles-tab-container'));
  var updateFormName = element(by.css('.protractor-update-form-name'));
  var updateFormSubmit = element(by.css('.protractor-update-form-submit'));
  var roleSelect = element(by.css('.protractor-update-form-role-select'));
  var statusMessage = element(by.css('.protractor-test-status-message'));

  var assignReviewerForm = element(
    by.css('.protractor-test-assign-reviewer-form'));
  var viewReviewerForm = element(by.css('.protractor-test-view-reviewer-form'));
  var languageSelectCss = by.css('.protractor-test-form-language-select');
  var reviewerUsernameCss = by.css('.protractor-test-form-reviewer-username');
  var reviewCategorySelectCss = by.css(
    '.protractor-test-form-review-category-select');
  var reviewerFormSubmitButtonCss = by.css(
    '.protractor-test-reviewer-form-submit-button');
  var userTranslationReviewerLanguageCss = by.css(
    '.protractor-test-translation-reviewer-language');
  var userVoiceoverReviewerLanguageCss = by.css(
    '.protractor-test-voiceover-reviewer-language');
  var userQuestionReviewerCss = by.css('.protractor-test-question-reviewer');
  var viewReviewerMethodInputCss = by.css(
    '.protractor-test-view-reviewer-method');

  var roleDropdown = element(by.css('.protractor-test-role-method'));
  var roleValueOption = element(by.css('.protractor-test-role-value'));
  var roleUsernameOption = element(by.css(
    '.protractor-test-username-value'));
  var viewRoleButton = element(by.css('.protractor-test-role-success'));
  var oneOffJobRows = element.all(by.css('.protractor-test-one-off-jobs-rows'));
  var unfinishedOneOffJobRows = element.all(by.css(
    '.protractor-test-unfinished-one-off-jobs-rows'));
  var unfinishedOffJobIDClassName = (
    '.protractor-test-unfinished-one-off-jobs-id');

  // The reload functions are used for mobile testing
  // done via Browserstack. These functions may cause
  // a problem when used to run tests directly on Travis.
  if (general.isInDevMode()) {
    var explorationElements = element.all(by.css(
      '.protractor-test-reload-exploration-row'
    ));

    var reloadCollectionButtons = element.all(by.css(
      '.protractor-test-reload-collection-button'));

    var getExplorationTitleElement = function(explorationElement) {
      return explorationElement.element(
        by.css('.protractor-test-reload-exploration-title')
      );
    };

    var getExplorationElementReloadButton = function(explorationElement) {
      return explorationElement.element(
        by.css('.protractor-test-reload-exploration-button')
      );
    };

    this.reloadCollection = async function(collectionId) {
      this.get();
      await (
        await reloadCollectionButtons.get(collectionId)
      ).click();
      await general.acceptAlert();
      // Time is needed for the reloading to complete.
      await waitFor.textToBePresentInElement(
        statusMessage, 'Data reloaded successfully.',
        'Collection could not be reloaded');
      return true;
    };

    // The name should be as given in the admin page (including '.yaml' if
    // necessary).
    this.reloadExploration = async function(name) {
      await this.get();
      explorationElements.map(async function(explorationElement) {
        var title = await getExplorationTitleElement(explorationElement)
          .getText();

        // We use match here in case there is whitespace around the name.
        if (title.match(name)) {
          await getExplorationElementReloadButton(explorationElement).click();
          await general.acceptAlert();
          // Time is needed for the reloading to complete.
          await waitFor.textToBePresentInElement(
            statusMessage, 'Data reloaded successfully.',
            'Exploration could not be reloaded');
          return true;
        }
      });
    };
  }

  var _switchToRolesTab = async function() {
    await action.click('Admin roles tab button', adminRolesTab);
    await waitFor.pageToFullyLoad();

    await expect(adminRolesTab.getAttribute('class')).toMatch('active');
    await waitFor.visibilityOf(
      adminRolesTabContainer, 'Roles tab page is not visible.');
  };

  var saveConfigProperty = async function(
      configProperty, propertyName, objectType, editingInstructions) {
    var title = await configProperty.element(
      by.css('.protractor-test-config-title')).getText();
    if (title.match(propertyName)) {
      await editingInstructions(
        await forms.getEditor(objectType)(configProperty));
      await saveAllConfigs.click();
      await general.acceptAlert();
      // Waiting for success message.
      await waitFor.textToBePresentInElement(
        statusMessage, 'saved successfully',
        'New config could not be saved');
      return true;
    }
  };

  this.get = async function() {
    await browser.get(ADMIN_URL_SUFFIX);
    await waitFor.pageToFullyLoad();
  };

  this.getJobsTab = async function() {
    await browser.get(ADMIN_URL_SUFFIX + '#/jobs');
    await waitFor.pageToFullyLoad();
  };

  this.editConfigProperty = async function(
      propertyName, objectType, editingInstructions) {
    await this.get();
    await configTab.click();
    await waitFor.elementToBeClickable(saveAllConfigs);

    const results = [];
    for (let configProperty of (await configProperties)) {
      results.push(
        await saveConfigProperty(
          configProperty, propertyName, objectType, editingInstructions)
      );
    }
    var success = null;
    for (var i = 0; i < results.length; i++) {
      success = success || results[i];
    }
    if (!success) {
      throw new Error('Could not find config property: ' + propertyName);
    }
  };

  this.startOneOffJob = async function(jobName) {
    await this._startOneOffJob(jobName, 0);
  };

  this._startOneOffJob = async function(jobName, i) {
    await waitFor.visibilityOf(
      await oneOffJobRows.first(),
      'Starting one off jobs taking too long to appear.');
    var text = await (await oneOffJobRows.get(i)).getText();
    if (text.toLowerCase().startsWith(jobName.toLowerCase())) {
      await (await oneOffJobRows.get(i)).element(
        by.css('.protractor-test-one-off-jobs-start-btn')).click();
    } else {
      await this._startOneOffJob(jobName, ++i);
    }
  };

  this.stopOneOffJob = async function(jobName) {
    await this._stopOneOffJob(jobName, 0);
  };

  this._stopOneOffJob = async function(jobName, i) {
    var text = await (await unfinishedOneOffJobRows.get(i)).getText();
    if (text.toLowerCase().startsWith(jobName.toLowerCase())) {
      await (await unfinishedOneOffJobRows.get(i)).element(
        by.css('.protractor-test-one-off-jobs-stop-btn')).click();
    } else {
      await this._stopOneOffJob(jobName, ++i);
    }
  };

  this.expectNumberOfRunningOneOffJobs = async function(count) {
    var len = await element.all(by.css(
      '.protractor-test-unfinished-one-off-jobs-id')).count();
    expect(len).toEqual(count);
  };

  this.expectJobToBeRunning = async function(jobName) {
    await browser.refresh();
    await waitFor.pageToFullyLoad();
    await waitFor.visibilityOf(element(
      by.css('.protractor-test-unfinished-jobs-card')),
    'Unfinished Jobs taking too long to appear');
    let regex = new RegExp(`^${jobName.toLowerCase()}.*`, 'i');
    let unfinishedJob = element(
      by.cssContainingText(unfinishedOffJobIDClassName, regex));
    var unfinishedJobName = await unfinishedJob.getText();
    expect(unfinishedJobName.toLowerCase().startsWith(
      jobName.toLowerCase())).toEqual(true);
  };

  this.updateRole = async function(name, newRole) {
    await waitFor.elementToBeClickable(
      adminRolesTab, 'Admin Roles tab is not clickable');
    await adminRolesTab.click();

    // Change values for "update role" form, and submit it.
    await waitFor.visibilityOf(
      updateFormName, 'Update Form Name is not visible');
    await updateFormName.sendKeys(name);
    var roleOption = roleSelect.element(
      by.cssContainingText('option', newRole));
    await waitFor.visibilityOf(roleOption, 'Admin role option is not visible');
    await roleOption.click();
    await updateFormSubmit.click();
    await waitFor.visibilityOf(
      statusMessage, 'Confirmation message not visible');
    await waitFor.textToBePresentInElement(
      statusMessage, 'successfully updated to',
      'Could not set role successfully');
  };

  this.getUsersAsssignedToRole = async function(role) {
    await waitFor.visibilityOf(
      roleDropdown, 'View role dropdown taking too long to be visible');
    await roleDropdown.sendKeys('By Role');

    await roleValueOption.click();
    await roleValueOption.sendKeys(role);

    await viewRoleButton.click();
  };

  this.viewRolesbyUsername = async function(username) {
    await waitFor.visibilityOf(
      roleDropdown, 'View role dropdown taking too long to be visible');
    await roleDropdown.sendKeys('By Username');

    await roleUsernameOption.click();
    await roleUsernameOption.sendKeys(username);

    await viewRoleButton.click();
  };

  this.expectUsernamesToMatch = async function(expectedUsernamesArray) {
    var foundUsersArray = [];
    var usernames = await element.all(
      by.css('.protractor-test-roles-result-rows'))
      .map(async function(elm) {
        var text = await elm.getText();
        return text;
      });

    for (i = 0; i < usernames.length; i++) {
      var name = usernames[i];
      foundUsersArray.push(name);
    }
    expect(foundUsersArray.length).toEqual(expectedUsernamesArray.length);

    expectedUsernamesArray.sort();
    foundUsersArray.sort();
    for (j = 0; j < foundUsersArray.length; j++) {
      var name = foundUsersArray[j];
      expect(name).toEqual(expectedUsernamesArray[j]);
    }
  };

  var _assignReviewer = async function(
      username, reviewCategory, languageDescription = null) {
    await _switchToRolesTab();

    await waitFor.visibilityOf(
      assignReviewerForm, 'Assign reviewer form is not visible');

    var usernameInputField = assignReviewerForm.element(reviewerUsernameCss);
    await action.sendKeys(
      'Username input field', usernameInputField, username);

    var reviewCategorySelectField = assignReviewerForm.element(
      reviewCategorySelectCss);
    await action.select(
      'Review category selector', reviewCategorySelectField, reviewCategory);

    if (languageDescription !== null) {
      var languageSelectField = assignReviewerForm.element(languageSelectCss);
      await action.select(
        'Language selector', languageSelectField, languageDescription);
    }

    var submitButton = assignReviewerForm.element(reviewerFormSubmitButtonCss);
    await action.click('Submit assign reviewer button', submitButton);

    await waitFor.textToBePresentInElement(
      statusMessage, 'Successfully added', (
        'Status message for assigning ' + reviewCategory + ' reviewer takes ' +
        'too long to appear'));
  };

  var _getUserReviewRightsElement = async function(username, reviewCategory) {
    await _switchToRolesTab();

    await waitFor.visibilityOf(
      viewReviewerForm, 'View reviewer form is not visible');

    var viewMethodInput = viewReviewerForm.element(viewReviewerMethodInputCss);
    await action.select(
      'Reviewer view method dropdown', viewMethodInput, 'By Username');

    var usernameInputField = viewReviewerForm.element(reviewerUsernameCss);
    await action.sendKeys(
      'Username input field', usernameInputField, username);

    var submitButton = viewReviewerForm.element(reviewerFormSubmitButtonCss);
    await action.click('View reviewer role button', submitButton);

    await waitFor.textToBePresentInElement(
      statusMessage, 'Success',
      'Could not view reviewer rights successfully');

    if (reviewCategory === REVIEW_CATEGORY_TRANSLATION) {
      return element.all(userTranslationReviewerLanguageCss);
    } else if (reviewCategory === REVIEW_CATEGORY_VOICEOVER) {
      return element.all(userVoiceoverReviewerLanguageCss);
    } else if (reviewCategory === REVIEW_CATEGORY_QUESTION) {
      return element(userQuestionReviewerCss);
    }
  };

  this.assignTranslationReviewer = async function(
      username, languageDescription) {
    await _assignReviewer(
      username, REVIEW_CATEGORY_TRANSLATION, languageDescription);
  };

  this.assignVoiceoverReviewer = async function(username, languageDescription) {
    await _assignReviewer(
      username, REVIEW_CATEGORY_VOICEOVER, languageDescription);
  };

  this.assignQuestionReviewer = async function(username) {
    await _assignReviewer(username, REVIEW_CATEGORY_QUESTION);
  };

  this.expectUserToBeTranslationReviewer = async function(
      username, languageDescription) {
    var reviewRights = await _getUserReviewRightsElement(
      username, REVIEW_CATEGORY_TRANSLATION);
    var languageList = await Promise.all(
      reviewRights.map(function(languageElem) {
        return languageElem.getText();
      }));
    expect(languageList).toContain(languageDescription);
  };

  this.expectUserToBeVoiceoverReviewer = async function(
      username, languageDescription) {
    var reviewRights = await _getUserReviewRightsElement(
      username, REVIEW_CATEGORY_VOICEOVER);
    var languageList = await Promise.all(reviewRights.map(
      function(languageElem) {
        return languageElem.getText();
      }));
    expect(languageList).toContain(languageDescription);
  };

  this.expectUserToBeQuestionReviewer = async function(username) {
    var reviewRight = await _getUserReviewRightsElement(
      username, REVIEW_CATEGORY_QUESTION);
    expect(await reviewRight.getText()).toBe('Allowed');
  };
};

exports.AdminPage = AdminPage;
<commit_msg>Fix part of #10798:Fixing End-to-End Tests with action.js and waitFor.js (#10954)<commit_after>// Copyright 2017 The Oppia Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Page object for the admin page, for use in Protractor
 * tests.
 */

var action = require('./action.js');
var forms = require('./forms.js');
var general = require('./general.js');
var waitFor = require('./waitFor.js');

var AdminPage = function() {
  var ADMIN_URL_SUFFIX = '/admin';
  var REVIEW_CATEGORY_TRANSLATION = 'TRANSLATION';
  var REVIEW_CATEGORY_VOICEOVER = 'VOICEOVER';
  var REVIEW_CATEGORY_QUESTION = 'QUESTION';

  var configTab = element(by.css('.protractor-test-admin-config-tab'));
  var saveAllConfigs = element(by.css('.protractor-test-save-all-configs'));
  var configProperties = element.all(by.css(
    '.protractor-test-config-property'
  ));
  var adminRolesTab = element(by.css('.protractor-test-admin-roles-tab'));
  var adminRolesTabContainer = element(
    by.css('.protractor-test-roles-tab-container'));
  var updateFormName = element(by.css('.protractor-update-form-name'));
  var updateFormSubmit = element(by.css('.protractor-update-form-submit'));
  var roleSelect = element(by.css('.protractor-update-form-role-select'));
  var statusMessage = element(by.css('.protractor-test-status-message'));

  var assignReviewerForm = element(
    by.css('.protractor-test-assign-reviewer-form'));
  var viewReviewerForm = element(by.css('.protractor-test-view-reviewer-form'));
  var languageSelectCss = by.css('.protractor-test-form-language-select');
  var reviewerUsernameCss = by.css('.protractor-test-form-reviewer-username');
  var reviewCategorySelectCss = by.css(
    '.protractor-test-form-review-category-select');
  var reviewerFormSubmitButtonCss = by.css(
    '.protractor-test-reviewer-form-submit-button');
  var userTranslationReviewerLanguageCss = by.css(
    '.protractor-test-translation-reviewer-language');
  var userVoiceoverReviewerLanguageCss = by.css(
    '.protractor-test-voiceover-reviewer-language');
  var userQuestionReviewerCss = by.css('.protractor-test-question-reviewer');
  var viewReviewerMethodInputCss = by.css(
    '.protractor-test-view-reviewer-method');

  var roleDropdown = element(by.css('.protractor-test-role-method'));
  var roleValueOption = element(by.css('.protractor-test-role-value'));
  var roleUsernameOption = element(by.css(
    '.protractor-test-username-value'));
  var viewRoleButton = element(by.css('.protractor-test-role-success'));
  var oneOffJobRows = element.all(by.css('.protractor-test-one-off-jobs-rows'));
  var unfinishedOneOffJobRows = element.all(by.css(
    '.protractor-test-unfinished-one-off-jobs-rows'));
  var unfinishedOffJobIDClassName = (
    '.protractor-test-unfinished-one-off-jobs-id');

  // The reload functions are used for mobile testing
  // done via Browserstack. These functions may cause
  // a problem when used to run tests directly on Travis.
  if (general.isInDevMode()) {
    var explorationElements = element.all(by.css(
      '.protractor-test-reload-exploration-row'
    ));

    var reloadCollectionButtons = element.all(by.css(
      '.protractor-test-reload-collection-button'));

    var getExplorationTitleElement = function(explorationElement) {
      return explorationElement.element(
        by.css('.protractor-test-reload-exploration-title')
      );
    };

    var getExplorationElementReloadButton = function(explorationElement) {
      return explorationElement.element(
        by.css('.protractor-test-reload-exploration-button')
      );
    };

    this.reloadCollection = async function(collectionId) {
      await this.get();
      var reloadCollectionButton = reloadCollectionButtons.get(collectionId);
      await action.click('Reload Collection Buttons', reloadCollectionButton);
      await general.acceptAlert();
      // Time is needed for the reloading to complete.
      await waitFor.textToBePresentInElement(
        statusMessage, 'Data reloaded successfully.',
        'Collection could not be reloaded');
      return true;
    };

    // The name should be as given in the admin page (including '.yaml' if
    // necessary).
    this.reloadExploration = async function(name) {
      await this.get();
      explorationElements.map(async function(explorationElement) {
        await waitFor.visibilityOf(
          getExplorationTitleElement(
            explorationElement,
            'Exploration title taking too long to appear'));
        var title = await getExplorationTitleElement(
          explorationElement).getText();

        // We use match here in case there is whitespace around the name.
        if (title.match(name)) {
          var explorationElementReloadButton =
            getExplorationElementReloadButton(explorationElement);
          await action.click(
            'Exploration Element Reload Button',
            explorationElementReloadButton);
          await general.acceptAlert();
          // Time is needed for the reloading to complete.
          await waitFor.textToBePresentInElement(
            statusMessage, 'Data reloaded successfully.',
            'Exploration could not be reloaded');
          return true;
        }
      });
    };
  }

  var _switchToRolesTab = async function() {
    await action.click('Admin roles tab button', adminRolesTab);
    await waitFor.pageToFullyLoad();

    expect(await adminRolesTab.getAttribute('class')).toMatch('active');
    await waitFor.visibilityOf(
      adminRolesTabContainer, 'Roles tab page is not visible.');
  };

  var saveConfigProperty = async function(
      configProperty, propertyName, objectType, editingInstructions) {
    await waitFor.visibilityOf(
      configProperty.element(
        by.css('.protractor-test-config-title')),
      'Config Title taking too long too appear');
    var title = await configProperty.element(
      by.css('.protractor-test-config-title')).getText();
    if (title.match(propertyName)) {
      await editingInstructions(
        await forms.getEditor(objectType)(configProperty));
      await action.click('Save All Configs Button', saveAllConfigs);
      await general.acceptAlert();
      // Waiting for success message.
      await waitFor.textToBePresentInElement(
        statusMessage, 'saved successfully',
        'New config could not be saved');
      return true;
    }
  };

  this.get = async function() {
    await browser.get(ADMIN_URL_SUFFIX);
    await waitFor.pageToFullyLoad();
  };

  this.getJobsTab = async function() {
    await browser.get(ADMIN_URL_SUFFIX + '#/jobs');
    await waitFor.pageToFullyLoad();
  };

  this.editConfigProperty = async function(
      propertyName, objectType, editingInstructions) {
    await this.get();
    await action.click('Config Tab', configTab);
    await waitFor.elementToBeClickable(saveAllConfigs);

    const results = [];
    for (let configProperty of (await configProperties)) {
      results.push(
        await saveConfigProperty(
          configProperty, propertyName, objectType, editingInstructions)
      );
    }
    var success = null;
    for (var i = 0; i < results.length; i++) {
      success = success || results[i];
    }
    if (!success) {
      throw new Error('Could not find config property: ' + propertyName);
    }
  };

  this.startOneOffJob = async function(jobName) {
    await this._startOneOffJob(jobName, 0);
  };

  this._startOneOffJob = async function(jobName, i) {
    await waitFor.visibilityOf(
      await oneOffJobRows.first(),
      'Starting one off jobs taking too long to appear.');
    await waitFor.visibilityOf(
      oneOffJobRows.get(i), 'Could not get One Off Jobs');
    var text = await oneOffJobRows.get(i).getText();
    if (text.toLowerCase().startsWith(jobName.toLowerCase())) {
      var oneOffJobRowsButton = oneOffJobRows.get(i).element(
        by.css('.protractor-test-one-off-jobs-start-btn'));
      await action.click('One Off Job Rows Button', oneOffJobRowsButton);
    } else {
      await this._startOneOffJob(jobName, ++i);
    }
  };

  this.stopOneOffJob = async function(jobName) {
    await this._stopOneOffJob(jobName, 0);
  };

  this._stopOneOffJob = async function(jobName, i) {
    await waitFor.visibilityOf(
      unfinishedOneOffJobRows.get(i),
      'Could not get Unfinished Off Job');
    var text = await unfinishedOneOffJobRows.get(i).getText();
    if (text.toLowerCase().startsWith(jobName.toLowerCase())) {
      var unfinishedOffJobRowsButton = (
        unfinishedOneOffJobRows.get(i)
      ).element(
        by.css('.protractor-test-one-off-jobs-stop-btn'));
      await action.click(
        'UnfinishedOffJobRowsButton', unfinishedOffJobRowsButton);
    } else {
      await this._stopOneOffJob(jobName, ++i);
    }
  };

  this.expectNumberOfRunningOneOffJobs = async function(count) {
    var len = await element.all(by.css(
      '.protractor-test-unfinished-one-off-jobs-id')).count();
    expect(len).toEqual(count);
  };

  this.expectJobToBeRunning = async function(jobName) {
    await browser.refresh();
    await waitFor.pageToFullyLoad();
    await waitFor.visibilityOf(element(
      by.css('.protractor-test-unfinished-jobs-card')),
    'Unfinished Jobs taking too long to appear');
    let regex = new RegExp(`^${jobName.toLowerCase()}.*`, 'i');
    let unfinishedJob = element(
      by.cssContainingText(unfinishedOffJobIDClassName, regex));
    var unfinishedJobName = await unfinishedJob.getText();
    expect(unfinishedJobName.toLowerCase().startsWith(
      jobName.toLowerCase())).toEqual(true);
  };

  this.updateRole = async function(name, newRole) {
    await waitFor.elementToBeClickable(
      adminRolesTab, 'Admin Roles tab is not clickable');
    await action.click('Admin Roles Tab', adminRolesTab);

    // Change values for "update role" form, and submit it.
    await action.sendKeys('Update Form Name', updateFormName, name);
    var roleOption = roleSelect.element(
      by.cssContainingText('option', newRole));
    await action.click('Role Option', roleOption);
    await action.click('Update Form Submit', updateFormSubmit);
    await waitFor.visibilityOf(
      statusMessage, 'Confirmation message not visible');
    await waitFor.textToBePresentInElement(
      statusMessage, 'successfully updated to',
      'Could not set role successfully');
  };

  this.getUsersAsssignedToRole = async function(role) {
    await action.sendKeys('Role Drop Down', roleDropdown, 'By Role');

    await action.sendKeys('Role Value Option', roleValueOption, role);

    await action.click('View Role Button', viewRoleButton);
  };

  this.viewRolesbyUsername = async function(username) {
    await action.sendKeys('Role Drop Down', roleDropdown, 'By Username');

    await action.sendKeys('Role Username Option', roleUsernameOption, username);

    await action.click('View Role Button', viewRoleButton);
  };

  this.expectUsernamesToMatch = async function(expectedUsernamesArray) {
    var foundUsersArray = [];
    var usernames = await element.all(
      by.css('.protractor-test-roles-result-rows'))
      .map(async function(elm) {
        var text = await elm.getText();
        return text;
      });

    for (i = 0; i < usernames.length; i++) {
      var name = usernames[i];
      foundUsersArray.push(name);
    }
    expect(foundUsersArray.length).toEqual(expectedUsernamesArray.length);

    expectedUsernamesArray.sort();
    foundUsersArray.sort();
    for (j = 0; j < foundUsersArray.length; j++) {
      var name = foundUsersArray[j];
      expect(name).toEqual(expectedUsernamesArray[j]);
    }
  };

  var _assignReviewer = async function(
      username, reviewCategory, languageDescription = null) {
    await _switchToRolesTab();

    await waitFor.visibilityOf(
      assignReviewerForm, 'Assign reviewer form is not visible');

    var usernameInputField = assignReviewerForm.element(reviewerUsernameCss);
    await action.sendKeys(
      'Username input field', usernameInputField, username);

    var reviewCategorySelectField = assignReviewerForm.element(
      reviewCategorySelectCss);
    await action.select(
      'Review category selector', reviewCategorySelectField, reviewCategory);

    if (languageDescription !== null) {
      var languageSelectField = assignReviewerForm.element(languageSelectCss);
      await action.select(
        'Language selector', languageSelectField, languageDescription);
    }

    var submitButton = assignReviewerForm.element(reviewerFormSubmitButtonCss);
    await action.click('Submit assign reviewer button', submitButton);

    await waitFor.textToBePresentInElement(
      statusMessage, 'Successfully added', (
        'Status message for assigning ' + reviewCategory + ' reviewer takes ' +
        'too long to appear'));
  };

  var _getUserReviewRightsElement = async function(username, reviewCategory) {
    await _switchToRolesTab();

    await waitFor.visibilityOf(
      viewReviewerForm, 'View reviewer form is not visible');

    var viewMethodInput = viewReviewerForm.element(viewReviewerMethodInputCss);
    await action.select(
      'Reviewer view method dropdown', viewMethodInput, 'By Username');

    var usernameInputField = viewReviewerForm.element(reviewerUsernameCss);
    await action.sendKeys(
      'Username input field', usernameInputField, username);

    var submitButton = viewReviewerForm.element(reviewerFormSubmitButtonCss);
    await action.click('View reviewer role button', submitButton);

    await waitFor.textToBePresentInElement(
      statusMessage, 'Success',
      'Could not view reviewer rights successfully');

    if (reviewCategory === REVIEW_CATEGORY_TRANSLATION) {
      return element.all(userTranslationReviewerLanguageCss);
    } else if (reviewCategory === REVIEW_CATEGORY_VOICEOVER) {
      return element.all(userVoiceoverReviewerLanguageCss);
    } else if (reviewCategory === REVIEW_CATEGORY_QUESTION) {
      return element(userQuestionReviewerCss);
    }
  };

  this.assignTranslationReviewer = async function(
      username, languageDescription) {
    await _assignReviewer(
      username, REVIEW_CATEGORY_TRANSLATION, languageDescription);
  };

  this.assignVoiceoverReviewer = async function(username, languageDescription) {
    await _assignReviewer(
      username, REVIEW_CATEGORY_VOICEOVER, languageDescription);
  };

  this.assignQuestionReviewer = async function(username) {
    await _assignReviewer(username, REVIEW_CATEGORY_QUESTION);
  };

  this.expectUserToBeTranslationReviewer = async function(
      username, languageDescription) {
    var reviewRights = await _getUserReviewRightsElement(
      username, REVIEW_CATEGORY_TRANSLATION);
    var languageList = await Promise.all(
      reviewRights.map(function(languageElem) {
        return languageElem.getText();
      }));
    expect(languageList).toContain(languageDescription);
  };

  this.expectUserToBeVoiceoverReviewer = async function(
      username, languageDescription) {
    var reviewRights = await _getUserReviewRightsElement(
      username, REVIEW_CATEGORY_VOICEOVER);
    var languageList = await Promise.all(reviewRights.map(
      function(languageElem) {
        return languageElem.getText();
      }));
    expect(languageList).toContain(languageDescription);
  };

  this.expectUserToBeQuestionReviewer = async function(username) {
    var reviewRight = await _getUserReviewRightsElement(
      username, REVIEW_CATEGORY_QUESTION);
    await waitFor.visibilityOf(
      reviewRight, 'Review Right Element taking too long to appear');
    expect(await reviewRight.getText()).toBe('Allowed');
  };
};

exports.AdminPage = AdminPage;
<|endoftext|>


<commit_before>/*
Copyright 2010 WebDriver committers
Copyright 2010 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

function attachEvaluateListener() {
  var handleEvaluateEvent = function(event) {
    var script = document.getUserData('webdriver-evaluate-script');

    try {
      var result = eval(script);
      document.setUserData('webdriver-evaluate-result', result, null);
      document.setUserData('webdriver-evaluate-code', 0, null);
    } catch (e) {
      document.setUserData('webdriver-evaluate-result', e, null);
      // "Unhandled JS error" == 17
      document.setUserData('webdriver-evaluate-code', 17, null);
    }

    // Clear up
    document.setUserData('webdriver-evaluate-script', null, null);
    document.setUserData('webdriver-evaluate-args', null, null);

    // Respond
    var response = document.createEvent('Events');
    response.initEvent('webdriver-evaluate-response', true, false);
    document.dispatchEvent(response);
  };

  document.addEventListener('webdriver-evaluate', handleEvaluateEvent, true);

  // Make it clear that we're here.
  document.setUserData('webdriver-evaluate-attached', true, null);
}

(function() {
  attachEvaluateListener();
})();
<commit_msg>JasonLeyba: The FirefoxDriver's executeScript helper should not pollute the global js namespace.<commit_after>/*
Copyright 2010 WebDriver committers
Copyright 2010 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

(function() {
  var handleEvaluateEvent = function(event) {
    var script = document.getUserData('webdriver-evaluate-script');

    try {
      var result = eval(script);
      document.setUserData('webdriver-evaluate-result', result, null);
      document.setUserData('webdriver-evaluate-code', 0, null);
    } catch (e) {
      document.setUserData('webdriver-evaluate-result', e, null);
      // "Unhandled JS error" == 17
      document.setUserData('webdriver-evaluate-code', 17, null);
    }

    // Clear up
    document.setUserData('webdriver-evaluate-script', null, null);
    document.setUserData('webdriver-evaluate-args', null, null);

    // Respond
    var response = document.createEvent('Events');
    response.initEvent('webdriver-evaluate-response', true, false);
    document.dispatchEvent(response);
  };

  document.addEventListener('webdriver-evaluate', handleEvaluateEvent, true);

  // Make it clear that we're here.
  document.setUserData('webdriver-evaluate-attached', true, null);
})();
<|endoftext|>


<commit_before>const redis = require('./redis');
const config = require('../config');
const { randomBid } = require('../simulation/vehicles');
const { getVehicle } = require('../store/vehicles');
const { getNeed } = require('./needs');

const saveBid = async ({ vehicle_id, price, time_to_pickup, time_to_dropoff }, needId, userId) => {
  // get new unique id for bid
  const bidId = await redis.incrAsync('next_bid_id');

  // Save bid id in need_bids
  redis.rpushAsync(`need_bids:${needId}`, bidId);

  // Add bid to bids
  redis.hmsetAsync(`bids:${bidId}`,
    'id', bidId,
    'vehicle_id', vehicle_id,
    'user_id', userId,
    'price', price,
    'time_to_pickup', time_to_pickup,
    'time_to_dropoff', time_to_dropoff,
    'need_id', needId,
  );

  // Set TTL for bid
  setBidTTL(bidId);
  return bidId;
};

const getBid = async bidId => {
  // Set TTL for bid
  setBidTTL(bidId);
  return await redis.hgetallAsync(`bids:${bidId}`);
};

const getBidsForNeed = async needId => {
  // get request details
  const need = await getNeed(needId);
  if (!need) return [];

  const userId = need.user_id;

  // get bids for need
  const bidIds = await redis.lrangeAsync(`need_bids:${needId}`, 0, -1);
  const bids = await Promise.all(
    bidIds.map(bidId => {
      setBidTTL(bidId);
      return redis.hgetallAsync(`bids:${bidId}`);
    }),
  );

  // If not enough bids, make some up
  if (bidIds.length < 10) {
    const { pickup_longitude, pickup_latitude, dropoff_latitude, dropoff_longitude } = need;
    const pickup = { lat: pickup_latitude, long: pickup_longitude };
    const dropoff = { lat: dropoff_latitude, long: dropoff_longitude };
    const vehicleIds = await redis.georadiusAsync(
      'vehicle_positions',
      pickup_longitude,
      pickup_latitude,
      2000,
      'm',
    );
    if (vehicleIds.length > bidIds.length) {
      // Just a hacky way to get more bids from different vehicles.
      // Not guaranteed to not have duplicate bids from same vehicle
      const vehicleId = vehicleIds[bidIds.length];
      const vehicle = await getVehicle(vehicleId);
      const origin = { lat: vehicle.lat, long: vehicle.long };
      let newBid = randomBid(origin, pickup, dropoff);
      newBid.vehicle_id = vehicleId;
      const newBidId = await saveBid(newBid, needId, userId);
      newBid.id = newBidId;
      bids.push(newBid);
    }
  }

  return bids;
};

const deleteBidsForNeed = async needId => {
  const bidIds = await redis.lrangeAsync(`need_bids:${needId}`, 0, -1);
  await Promise.all(
    bidIds.map(bidId => redis.del(`bids:${bidId}`))
  );
  return await redis.del(`need_bids:${needId}`);
};

module.exports = {
  getBidsForNeed,
  getBid,
  deleteBidsForNeed,
};

const setBidTTL = async bidId => redis.expire(`bids:${bidId}`, config('bids_ttl'));<commit_msg>Fix : Remove asyn on the function setBidTTL(bidId)<commit_after>const redis = require('./redis');
const config = require('../config');
const { randomBid } = require('../simulation/vehicles');
const { getVehicle } = require('../store/vehicles');
const { getNeed } = require('./needs');

const saveBid = async ({ vehicle_id, price, time_to_pickup, time_to_dropoff }, needId, userId) => {
  // get new unique id for bid
  const bidId = await redis.incrAsync('next_bid_id');

  // Save bid id in need_bids
  redis.rpushAsync(`need_bids:${needId}`, bidId);

  // Add bid to bids
  redis.hmsetAsync(`bids:${bidId}`,
    'id', bidId,
    'vehicle_id', vehicle_id,
    'user_id', userId,
    'price', price,
    'time_to_pickup', time_to_pickup,
    'time_to_dropoff', time_to_dropoff,
    'need_id', needId,
  );

  // Set TTL for bid
  setBidTTL(bidId);
  return bidId;
};

const getBid = async bidId => {
  // Set TTL for bid
  setBidTTL(bidId);
  return await redis.hgetallAsync(`bids:${bidId}`);
};

const getBidsForNeed = async needId => {
  // get request details
  const need = await getNeed(needId);
  if (!need) return [];

  const userId = need.user_id;

  // get bids for need
  const bidIds = await redis.lrangeAsync(`need_bids:${needId}`, 0, -1);
  const bids = await Promise.all(
    bidIds.map(bidId => {
      setBidTTL(bidId);
      return redis.hgetallAsync(`bids:${bidId}`);
    }),
  );

  // If not enough bids, make some up
  if (bidIds.length < 10) {
    const { pickup_longitude, pickup_latitude, dropoff_latitude, dropoff_longitude } = need;
    const pickup = { lat: pickup_latitude, long: pickup_longitude };
    const dropoff = { lat: dropoff_latitude, long: dropoff_longitude };
    const vehicleIds = await redis.georadiusAsync(
      'vehicle_positions',
      pickup_longitude,
      pickup_latitude,
      2000,
      'm',
    );
    if (vehicleIds.length > bidIds.length) {
      // Just a hacky way to get more bids from different vehicles.
      // Not guaranteed to not have duplicate bids from same vehicle
      const vehicleId = vehicleIds[bidIds.length];
      const vehicle = await getVehicle(vehicleId);
      const origin = { lat: vehicle.lat, long: vehicle.long };
      let newBid = randomBid(origin, pickup, dropoff);
      newBid.vehicle_id = vehicleId;
      const newBidId = await saveBid(newBid, needId, userId);
      newBid.id = newBidId;
      bids.push(newBid);
    }
  }

  return bids;
};

const deleteBidsForNeed = async needId => {
  const bidIds = await redis.lrangeAsync(`need_bids:${needId}`, 0, -1);
  await Promise.all(
    bidIds.map(bidId => redis.del(`bids:${bidId}`))
  );
  return await redis.del(`need_bids:${needId}`);
};

module.exports = {
  getBidsForNeed,
  getBid,
  deleteBidsForNeed,
};

const setBidTTL = bidId => redis.expire(`bids:${bidId}`, config('bids_ttl'));<|endoftext|>


<commit_before>function Set_Cookie( name, value, expires, path, domain, secure )
{
var today = new Date();
today.setTime( today.getTime() );

if ( expires )
{
expires = expires * 1000 * 60 * 60 * 24;
}
var expires_date = new Date( today.getTime() + (expires) );

document.cookie = name + "=" +escape( value ) +
( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) +
( ( path ) ? ";path=" + path : "" ) +
( ( domain ) ? ";domain=" + domain : "" ) +
( ( secure ) ? ";secure" : "" );
}

function Get_Cookie( check_name ) {
  var a_all_cookies = document.cookie.split( ';' );
  var a_temp_cookie = '';
  var cookie_name = '';
  var cookie_value = '';
  var b_cookie_found = false; // set boolean t/f default f

  for ( i = 0; i < a_all_cookies.length; i++ )
  {

    a_temp_cookie = a_all_cookies[i].split( '=' );

    cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');

    // if the extracted name matches passed check_name
    if ( cookie_name == check_name )
    {
      b_cookie_found = true;
      // we need to handle case where cookie has no value but exists (no = sign, that is):
      if ( a_temp_cookie.length > 1 )
      {
        cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
      }
      // note that in cases where cookie is initialized but no value, null is returned
      return cookie_value;
      break;
    }
    a_temp_cookie = null;
    cookie_name = '';
  }
  if ( !b_cookie_found )
  {
    return null;
  }
}

// this deletes the cookie when called
function Delete_Cookie( name, path, domain ) {
if ( Get_Cookie( name ) ) document.cookie = name + "=" +
( ( path ) ? ";path=" + path : "") +
( ( domain ) ? ";domain=" + domain : "" ) +
";expires=Thu, 01-Jan-1970 00:00:01 GMT";
}

function hideClass(objClass){

var elements = document.getElementsByTagName('li');
  for (i=0; i<elements.length; i++){
    if (elements[i].className==objClass){
      elements[i].style.display="none"
    }
  }
}

function showClass(objClass){

var elements = document.getElementsByTagName('li');
  for (i=0; i<elements.length; i++){
    if (elements[i].className==objClass){
      elements[i].style.display="block"
    }
  }
}

function go(location){

  if (location=='home')
    $.mobile.changePage('index.php');
  else
    $.mobile.changePage('index.php?action='+location);

}

function toggle(cb,img){

  var checked = document.getElementById(cb).checked;

  var src = document.getElementById(img).src;

  if(!checked)
    document.getElementById(img).src = src.replace('On','Off');
  else
    document.getElementById(img).src = src.replace('Off','On');

}

function hashLoginPassword(doForm, cur_session_id)
{
  // Compatibility.
  if (cur_session_id == null)
    cur_session_id = smf_session_id;

  if (typeof(hex_sha1) == "undefined")
    return;
  // Are they using an email address?
  if (doForm.user.value.indexOf("@") != -1)
    return;

  // Unless the browser is Opera, the password will not save properly.
  if (typeof(window.opera) == "undefined")
    doForm.passwrd.autocomplete = "off";

  doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id);

  // It looks nicer to fill it with asterisks, but Firefox will try to save that.
  if (navigator.userAgent.indexOf("Firefox/") != -1)
    doForm.passwrd.value = "";
  else
    doForm.passwrd.value = doForm.passwrd.value.replace(/./g, "*");
}

function iswitch(id){

  if(id=='switcher'){
    document.getElementById(id).id = 'switcheralt';

    var x= document.getElementsByTagName('ul');

    for(var i = 0;i<x.length;i++){

      if(x[i].id=='contentrecent')
        x[i].style.display='block';
      if(x[i].id=='contentboards')
        x[i].style.display='none';
        }
    document.getElementById('childbuttondiv').style.display='none';

    var x= document.getElementsByTagName('h2');

    for(var i = 0;i<x.length;i++){

      if(x[i].id=='contentrecenth2')
        x[i].style.display='block';
      if(x[i].id=='contentboardsh2')
        x[i].style.display='none';
        }

  }else{

    document.getElementById(id).id = 'switcher';

    var x= document.getElementsByTagName('ul');

    for(var i = 0;i<x.length;i++){

      if(x[i].id=='contentrecent')
        x[i].style.display='none';
      if(x[i].id=='contentboards')
        x[i].style.display='block';
        }
    document.getElementById('childbuttondiv').style.display='block';

    var x= document.getElementsByTagName('h2');

    for(var i = 0;i<x.length;i++){

      if(x[i].id=='contentrecenth2')
        x[i].style.display='none';
      if(x[i].id=='contentboardsh2')
        x[i].style.display='block';
        }

    }
}

$(function(){
  $(document).on("swiperight", function (event) { window.history.back(); } );
  $(document).on("swipeleft", function (event) { window.history.forward(); } );
});

$(window).on('beforeunload' , function() {
  $('.clicked').each(function() {
    $(this).removeClass = 'clicked';
  });
});

// Hide toolbar when input is focused
if(/iPhone|iPod|Android|iPad/.test(window.navigator.platform)){
  $(document)
  .on('focus', 'textarea,input,select', function(e) {
    $('.toolbar').css('display', 'none');
    $('#copyright').css('margin-bottom', '4px');
  })
  .on('blur', 'textarea,input,select', function(e) {
    $('.toolbar').css('display', 'initial');
    $('#copyright').css('margin-bottom', '47px');
  });
}

$.mobile.ignoreContentEnabled = true;
$.event.special.swipe.horizontalDistanceThreshold = 100;
$.mobile.hideUrlBar = false;

$(document).bind("mobileinit", function() {
    $.mobile.buttonMarkup.hoverDelay = 5000
});

$(function() {
  $("#toolbar").removeClass( "ui-footer" )
});

$(document).one('pagehide', document, function(event, ui){
  $('[data-role="page"]').not(".ui-page-active").remove();
});<commit_msg>Revert<commit_after>function Set_Cookie( name, value, expires, path, domain, secure )
{
var today = new Date();
today.setTime( today.getTime() );

if ( expires )
{
expires = expires * 1000 * 60 * 60 * 24;
}
var expires_date = new Date( today.getTime() + (expires) );

document.cookie = name + "=" +escape( value ) +
( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) +
( ( path ) ? ";path=" + path : "" ) +
( ( domain ) ? ";domain=" + domain : "" ) +
( ( secure ) ? ";secure" : "" );
}

function Get_Cookie( check_name ) {
  var a_all_cookies = document.cookie.split( ';' );
  var a_temp_cookie = '';
  var cookie_name = '';
  var cookie_value = '';
  var b_cookie_found = false; // set boolean t/f default f

  for ( i = 0; i < a_all_cookies.length; i++ )
  {

    a_temp_cookie = a_all_cookies[i].split( '=' );

    cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');

    // if the extracted name matches passed check_name
    if ( cookie_name == check_name )
    {
      b_cookie_found = true;
      // we need to handle case where cookie has no value but exists (no = sign, that is):
      if ( a_temp_cookie.length > 1 )
      {
        cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
      }
      // note that in cases where cookie is initialized but no value, null is returned
      return cookie_value;
      break;
    }
    a_temp_cookie = null;
    cookie_name = '';
  }
  if ( !b_cookie_found )
  {
    return null;
  }
}

// this deletes the cookie when called
function Delete_Cookie( name, path, domain ) {
if ( Get_Cookie( name ) ) document.cookie = name + "=" +
( ( path ) ? ";path=" + path : "") +
( ( domain ) ? ";domain=" + domain : "" ) +
";expires=Thu, 01-Jan-1970 00:00:01 GMT";
}

function hideClass(objClass){

var elements = document.getElementsByTagName('li');
  for (i=0; i<elements.length; i++){
    if (elements[i].className==objClass){
      elements[i].style.display="none"
    }
  }
}

function showClass(objClass){

var elements = document.getElementsByTagName('li');
  for (i=0; i<elements.length; i++){
    if (elements[i].className==objClass){
      elements[i].style.display="block"
    }
  }
}

function go(location){

  if (location=='home')
    $.mobile.changePage('index.php');
  else
    $.mobile.changePage('index.php?action='+location);

}

function toggle(cb,img){

  var checked = document.getElementById(cb).checked;

  var src = document.getElementById(img).src;

  if(!checked)
    document.getElementById(img).src = src.replace('On','Off');
  else
    document.getElementById(img).src = src.replace('Off','On');

}

function hashLoginPassword(doForm, cur_session_id)
{
  // Compatibility.
  if (cur_session_id == null)
    cur_session_id = smf_session_id;

  if (typeof(hex_sha1) == "undefined")
    return;
  // Are they using an email address?
  if (doForm.user.value.indexOf("@") != -1)
    return;

  // Unless the browser is Opera, the password will not save properly.
  if (typeof(window.opera) == "undefined")
    doForm.passwrd.autocomplete = "off";

  doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id);

  // It looks nicer to fill it with asterisks, but Firefox will try to save that.
  if (navigator.userAgent.indexOf("Firefox/") != -1)
    doForm.passwrd.value = "";
  else
    doForm.passwrd.value = doForm.passwrd.value.replace(/./g, "*");
}

function iswitch(id){

  if(id=='switcher'){
    document.getElementById(id).id = 'switcheralt';

    var x= document.getElementsByTagName('ul');

    for(var i = 0;i<x.length;i++){

      if(x[i].id=='contentrecent')
        x[i].style.display='block';
      if(x[i].id=='contentboards')
        x[i].style.display='none';
        }
    document.getElementById('childbuttondiv').style.display='none';

    var x= document.getElementsByTagName('h2');

    for(var i = 0;i<x.length;i++){

      if(x[i].id=='contentrecenth2')
        x[i].style.display='block';
      if(x[i].id=='contentboardsh2')
        x[i].style.display='none';
        }

  }else{

    document.getElementById(id).id = 'switcher';

    var x= document.getElementsByTagName('ul');

    for(var i = 0;i<x.length;i++){

      if(x[i].id=='contentrecent')
        x[i].style.display='none';
      if(x[i].id=='contentboards')
        x[i].style.display='block';
        }
    document.getElementById('childbuttondiv').style.display='block';

    var x= document.getElementsByTagName('h2');

    for(var i = 0;i<x.length;i++){

      if(x[i].id=='contentrecenth2')
        x[i].style.display='none';
      if(x[i].id=='contentboardsh2')
        x[i].style.display='block';
        }

    }
}

$(function(){
  $(document).on("swiperight", function (event) { window.history.back(); } );
  $(document).on("swipeleft", function (event) { window.history.forward(); } );
});

$(window).on('beforeunload' , function() {
  $('.clicked').each(function() {
    $(this).removeClass = 'clicked';
  });
});

// Hide toolbar when input is focused
if(/iPhone|iPod|Android|iPad/.test(window.navigator.platform)){
  $(document)
  .on('focus', 'textarea,input,select', function(e) {
    $('.toolbar').css('display', 'none');
    $('#copyright').css('margin-bottom', '4px');
  })
  .on('blur', 'textarea,input,select', function(e) {
    $('.toolbar').css('display', 'initial');
    $('#copyright').css('margin-bottom', '47px');
  });
}

$.mobile.ignoreContentEnabled = true;
$.event.special.swipe.horizontalDistanceThreshold = 100;

$(document).bind("mobileinit", function() {
    $.mobile.buttonMarkup.hoverDelay = 5000
});

$(function() {
  $("#toolbar").removeClass( "ui-footer" )
});

$(document).one('pagehide', document, function(event, ui){
  $('[data-role="page"]').not(".ui-page-active").remove();
});<|endoftext|>


<commit_before>/**
 * Stand alone source grabber.
 */

if( ! window.kWidget ){
	window.kWidget = {};
}
( function( kWidget ) {
	// Add master exported function:
	kWidget.getSources = function( settings ){
		var sourceApi = new kWidget.api( { 'wid' : '_' + settings.partnerId } );
		sourceApi.doRequest([
		{
			'contextDataParams' : {
				'referrer' : document.URL,
				'objectType' : 'KalturaEntryContextDataParams',
				'flavorTags': 'all'
			},
			'service' : 'baseentry',
			'entryId' : settings.entryId,
			'action' : 'getContextData'
		},
		{
			'service' : 'baseentry',
			'action' : 'get',
			'version' : '-1',
			'entryId' : settings.entryId
		}], function( result ){ // API result
			var ks = sourceApi.ks;
			var ipadAdaptiveFlavors = [];
			var iphoneAdaptiveFlavors = [];
			var deviceSources = [];
			var protocol = location.protocol.substr(0, location.protocol.length-1);
			// Set the service url based on protocol type
			var serviceUrl;
			if( protocol == 'https' ){
				serviceUrl = 'https://www.kaltura.com';
			} else {
				serviceUrl = 'http://cdnbakmi.kaltura.com';
			}

			var baseUrl = serviceUrl + '/p/' + settings.partnerId +
					'/sp/' + settings.partnerId + '00/playManifest';
			for( var i in result[1]['flavorAssets'] ){
				var asset = result[1]['flavorAssets'][i];
				// Continue if clip is not ready (2)
				if( asset.status != 2  ) {
					continue;
				}
				// Setup a source object:
				var source = {
					'data-bitrate' : asset.bitrate * 8,
					'data-width' : asset.width,
					'data-height' : asset.height
				};

				var src  = baseUrl + '/entryId/' + asset.entryId;
				// Check if Apple http streaming is enabled and the tags include applembr ( single stream HLS )
				if( asset.tags.indexOf('applembr') != -1 ) {
					src += '/format/applehttp/protocol/'+ protocol + '/a.m3u8';

					deviceSources.push({
						'data-flavorid' : 'AppleMBR',
						'type' : 'application/vnd.apple.mpegurl',
						'src' : src
					});

					continue;
				} else {
					src += '/flavorId/' + asset.id + '/format/url/protocol/' + protocol;
				}

				// add the file extension:
				if( asset.tags.toLowerCase().indexOf('ipad') != -1 ){
					source['src'] = src + '/a.mp4';
					source['data-flavorid'] = 'iPad';
					source['type'] = 'video/h264';
				}

				// Check for iPhone src
				if( asset.tags.toLowerCase().indexOf('iphone') != -1 ){
					source['src'] = src + '/a.mp4';
					source['data-flavorid'] = 'iPhone';
					source['type'] = 'video/h264';
				}
				// Check for ogg source
				if( asset.fileExt &&
					(
						asset.fileExt.toLowerCase() == 'ogg'
						||
						asset.fileExt.toLowerCase() == 'ogv'
						||
						( asset.containerFormat && asset.containerFormat.toLowerCase() == 'ogg' )
					)
				){
					source['src'] = src + '/a.ogg';
					source['data-flavorid'] = 'ogg';
					source['type'] = 'video/ogg';
				}

				// Check for webm source
				if( asset.fileExt == 'webm'
					||
					asset.tags.indexOf('webm') != -1
					|| // Kaltura transcodes give: 'matroska'
					( asset.containerFormat && asset.containerFormat.toLowerCase() == 'matroska' )
					|| // some ingestion systems give "webm"
					( asset.containerFormat && asset.containerFormat.toLowerCase() == 'webm' )
				){
					source['src'] = src + '/a.webm';
					source['data-flavorid'] = 'webm';
					source['type'] = 'video/webm';
				}

				// Check for 3gp source
				if( asset.fileExt == '3gp' ){
					source['src'] = src + '/a.3gp';
					source['data-flavorid'] = '3gp'
					source['type'] = 'video/3gp';
				}

				// Add the device sources
				if( source['src'] ){
					deviceSources.push( source );
				}

				// Check for adaptive compatible flavor:
				if( asset.tags.toLowerCase().indexOf('ipadnew') != -1 ){
					ipadAdaptiveFlavors.push( asset.id );
				}
				if( asset.tags.toLowerCase().indexOf('iphonenew') != -1 ){
					iphoneAdaptiveFlavors.push( asset.id );
				}

			};
			// Add the flavor list adaptive style urls ( multiple flavor HLS ):
			// Create iPad flavor for Akamai HTTP
			if( ipadAdaptiveFlavors.length != 0 ) {
				deviceSources.push({
					'data-flavorid' : 'iPadNew',
					'type' : 'application/vnd.apple.mpegurl',
					'src' : baseUrl + '/entryId/' + asset.entryId + '/flavorIds/' + ipadAdaptiveFlavors.join(',')  + '/format/applehttp/protocol/' + protocol + '/a.m3u8'
				});
			}
			// Create iPhone flavor for Akamai HTTP
			if(iphoneAdaptiveFlavors.length != 0 ) {
				deviceSources.push({
					'data-flavorid' : 'iPhoneNew',
					'type' : 'application/vnd.apple.mpegurl',
					'src' : baseUrl + '/entryId/' + asset.entryId + '/flavorIds/' + iphoneAdaptiveFlavors.join(',')  + '/format/applehttp/protocol/' + protocol + '/a.m3u8'
				});
			}

			// callback with device sources, poster
			if( settings.callback ){
				settings.callback({
					'poster': result[2]['thumbnailUrl'],
					'duration': result[2]['duration'],
					'name': result[2]['name'],
					'entryId' :  result[2]['id'],
					'description': result[2]['description'],
					'sources': deviceSources
				});
			}
		});
	};
} )( window.kWidget );
<commit_msg>fix getSources to include HD / web flavors<commit_after>/**
 * Stand alone source grabber.
 */

if( ! window.kWidget ){
	window.kWidget = {};
}
( function( kWidget ) {
	// Add master exported function:
	kWidget.getSources = function( settings ){
		var sourceApi = new kWidget.api( { 'wid' : '_' + settings.partnerId } );
		sourceApi.doRequest([
		{
			'contextDataParams' : {
				'referrer' : document.URL,
				'objectType' : 'KalturaEntryContextDataParams',
				'flavorTags': 'all'
			},
			'service' : 'baseentry',
			'entryId' : settings.entryId,
			'action' : 'getContextData'
		},
		{
			'service' : 'baseentry',
			'action' : 'get',
			'version' : '-1',
			'entryId' : settings.entryId
		}], function( result ){ // API result
			var ks = sourceApi.ks;
			var ipadAdaptiveFlavors = [];
			var iphoneAdaptiveFlavors = [];
			var deviceSources = [];
			var protocol = location.protocol.substr(0, location.protocol.length-1);
			// Set the service url based on protocol type
			var serviceUrl;
			if( protocol == 'https' ){
				serviceUrl = 'https://www.kaltura.com';
			} else {
				serviceUrl = 'http://cdnbakmi.kaltura.com';
			}

			var baseUrl = serviceUrl + '/p/' + settings.partnerId +
					'/sp/' + settings.partnerId + '00/playManifest';
			for( var i in result[1]['flavorAssets'] ){
				var asset = result[1]['flavorAssets'][i];
				// Continue if clip is not ready (2)
				if( asset.status != 2  ) {
					continue;
				}
				// Setup a source object:
				var source = {
					'data-bitrate' : asset.bitrate * 8,
					'data-width' : asset.width,
					'data-height' : asset.height
				};

				var src  = baseUrl + '/entryId/' + asset.entryId;

				// Check if Apple http streaming is enabled and the tags include applembr ( single stream HLS )
				if( asset.tags.indexOf('applembr') != -1 ) {
					src += '/format/applehttp/protocol/'+ protocol + '/a.m3u8';

					deviceSources.push({
						'data-flavorid' : 'AppleMBR',
						'type' : 'application/vnd.apple.mpegurl',
						'src' : src
					});

					continue;
				} else {
					src += '/flavorId/' + asset.id + '/format/url/protocol/' + protocol;
				}
				// add source data if a web flavor:
				if( asset.tags.toLowerCase().indexOf('web') != -1 ){
					source['data-flavorid'] = asset.videoCodecId + ' ' + asset.height + 'P';
					source['src'] = src + '/a.' + asset.fileExt;
					source['type'] = 'video/h264';
				}
				// add the file extension:
				if( asset.tags.toLowerCase().indexOf('ipad') != -1 ){
					source['src'] = src + '/a.mp4';
					source['data-flavorid'] = 'iPad';
					source['type'] = 'video/h264';
				}

				// Check for iPhone src
				if( asset.tags.toLowerCase().indexOf('iphone') != -1 ){
					source['src'] = src + '/a.mp4';
					source['data-flavorid'] = 'iPhone';
					source['type'] = 'video/h264';
				}
				// Check for ogg source
				if( asset.fileExt &&
					(
						asset.fileExt.toLowerCase() == 'ogg'
						||
						asset.fileExt.toLowerCase() == 'ogv'
						||
						( asset.containerFormat && asset.containerFormat.toLowerCase() == 'ogg' )
					)
				){
					source['src'] = src + '/a.ogg';
					source['data-flavorid'] = 'ogg';
					source['type'] = 'video/ogg';
				}

				// Check for webm source
				if( asset.fileExt == 'webm'
					||
					asset.tags.indexOf('webm') != -1
					|| // Kaltura transcodes give: 'matroska'
					( asset.containerFormat && asset.containerFormat.toLowerCase() == 'matroska' )
					|| // some ingestion systems give "webm"
					( asset.containerFormat && asset.containerFormat.toLowerCase() == 'webm' )
				){
					source['src'] = src + '/a.webm';
					source['data-flavorid'] = 'webm';
					source['type'] = 'video/webm';
				}

				// Check for 3gp source
				if( asset.fileExt == '3gp' ){
					source['src'] = src + '/a.3gp';
					source['data-flavorid'] = '3gp'
					source['type'] = 'video/3gp';
				}

				// Add the device sources
				if( source['src'] ){
					deviceSources.push( source );
				}

				// Check for adaptive compatible flavor:
				if( asset.tags.toLowerCase().indexOf('ipadnew') != -1 ){
					ipadAdaptiveFlavors.push( asset.id );
				}
				if( asset.tags.toLowerCase().indexOf('iphonenew') != -1 ){
					iphoneAdaptiveFlavors.push( asset.id );
				}

			};
			// Add the flavor list adaptive style urls ( multiple flavor HLS ):
			// Create iPad flavor for Akamai HTTP
			if( ipadAdaptiveFlavors.length != 0 ) {
				deviceSources.push({
					'data-flavorid' : 'iPadNew',
					'type' : 'application/vnd.apple.mpegurl',
					'src' : baseUrl + '/entryId/' + asset.entryId + '/flavorIds/' + ipadAdaptiveFlavors.join(',')  + '/format/applehttp/protocol/' + protocol + '/a.m3u8'
				});
			}
			// Create iPhone flavor for Akamai HTTP
			if(iphoneAdaptiveFlavors.length != 0 ) {
				deviceSources.push({
					'data-flavorid' : 'iPhoneNew',
					'type' : 'application/vnd.apple.mpegurl',
					'src' : baseUrl + '/entryId/' + asset.entryId + '/flavorIds/' + iphoneAdaptiveFlavors.join(',')  + '/format/applehttp/protocol/' + protocol + '/a.m3u8'
				});
			}

			// callback with device sources, poster
			if( settings.callback ){
				settings.callback({
					'poster': result[2]['thumbnailUrl'],
					'duration': result[2]['duration'],
					'name': result[2]['name'],
					'entryId' :  result[2]['id'],
					'description': result[2]['description'],
					'sources': deviceSources
				});
			}
		});
	};
} )( window.kWidget );
<|endoftext|>


<commit_before>describe('Acceptance testing for the AngularJS web interface', function () {

    it('the web interface should have a title', function () {
        browser.get('http://localhost:8080/#/');
        expect(browser.getTitle()).toEqual('IIFramework');
    });

    it('should be able to move to the sources list by clicking the sidebar button', function () {
        browser.get('http://localhost:8080/#/');
        element(by.partialLinkText('Information Sources')).click();
        expect(browser.getTitle()).toEqual('IIFramework');
    });

    it('should be able view a sensor and filter its readouts', function () {
        browser.get('http://localhost:8080/#/sources/');
        element(by.partialLinkText('View')).click();
        element(by.partialLinkText('List')).click();
        element(by.model("more")).sendKeys("22.50");
        element(by.model("less")).sendKeys("22.80");
        element(by.id('filter')).click();
        expect(browser.getTitle()).toEqual('IIFramework');
    });

    it('should be able create a new information source', function () {
        browser.get('http://localhost:8080/#/sources/');
        element(by.partialLinkText('Add New')).click();
        var name = element(by.model("is.name"));
        var type = element(by.model("is.type"));
        var url = element(by.model("is.url"));
        var frequency = element(by.model("is.readFrequency"));
        name.sendKeys('NewAcceptanceTestSource');
        element(by.cssContainingText('option', 'XML')).click();
        url.sendKeys('http://axwikstr.users.cs.helsinki.fi/data.xml');
        frequency.sendKeys('154');
        element(by.id('roundbutton')).click();
        expect(browser.getTitle()).toEqual('IIFramework');
    });
});<commit_msg>improved the acceptance tests quite a bit and added a guide for using them as a comment<commit_after>describe('Acceptance testing for the AngularJS web interface', function () {

    // guide to using these acceptance tests:
    // 1) install Protractor by typing "npm install -g protractor" to the command line
    // 2) then type "webdriver-manager update" to the command line
    // 3) now every time you want to run these tests, first type "webdriver-manager start" to start the test server,
    // choose "Run Project" in NetBeans and then type "protractor conf.js" in the
    // cd iot-industrial-internet/src/test/java/fi/iot/iiframework/acceptance/ folder
    // 4) make sure to choose "Run Project" in Netbeans again before running the tests a second time
    // or there will be errors in the tests

    it('the web interface should have a title', function () {
        browser.get('http://localhost:8080/#/');
        expect(browser.getTitle()).toEqual('IIFramework');
    });

    it('should be able to move to the sources list by clicking the sidebar button', function () {
        browser.get('http://localhost:8080/#/');
        element(by.partialLinkText('Information Sources')).click();
        expect(element.all(by.repeater('ds in sources')).count()).toEqual(1);
    });

    it('should be able view a sensor and filter its readouts', function () {
        browser.get('http://localhost:8080/#/sources/');
        element(by.partialLinkText('View')).click();
        element(by.partialLinkText('List')).click();
        element(by.model("more")).sendKeys("22.50");
        element(by.model("less")).sendKeys("22.80");
        element(by.id('filter')).click();
        expect(element.all(by.repeater('readout in readouts')).count()).toEqual(10);
    });

    it('should be able create a new information source', function () {
        browser.get('http://localhost:8080/#/sources/');
        element(by.partialLinkText('Add New')).click();
        var name = element(by.model("is.name"));
        var type = element(by.model("is.type"));
        var url = element(by.model("is.url"));
        var frequency = element(by.model("is.readFrequency"));
        name.sendKeys('NewAcceptanceTestSource');
        element(by.cssContainingText('option', 'XML')).click();
        url.sendKeys('http://axwikstr.users.cs.helsinki.fi/data.xml');
        frequency.sendKeys('154');
        element(by.id('roundbutton')).click();
        element(by.partialLinkText('Configurations')).click();
        expect(element.all(by.repeater('c in configurations')).count()).toEqual(2);
    });
});<|endoftext|>


<commit_before>import {GraphQLError} from 'graphql/error'

import {parseQueryError} from 'src/server/db/errors'

export class LGCustomQueryError extends Error {
  constructor(message) {
    super(message)
    this.message = message || 'There was a problem with the query.'
    this.name = 'LGCustomQueryError'
  }
}

export class LGBadInputError extends Error {
  constructor(message) {
    super()
    Error.captureStackTrace(this, this.constructor)
    this.name = 'LGBadInputError'
    this.message = message
  }
}

export class LGInternalServerError extends Error {
  constructor(value) {
    if (typeof value === 'string') {
      super(value)
    } else {
      super()
      this.message = 'An internal server error occurred'
      if (value instanceof Error) {
        this.originalError = value
      }
    }
    this.name = 'LGInternalServerError'
    this.statusCode = 500
  }
}

export class LGTokenExpiredError extends Error {
  constructor(value) {
    if (typeof value === 'string') {
      super(value)
    } else {
      super()
      this.message = 'Your authentication token has expired.'
      if (value instanceof Error) {
        this.originalError = value
      }
    }
    this.name = 'LGTokenExpiredError'
    this.statusCode = 401
  }
}

export class LGNotAuthorizedError extends Error {
  constructor(value) {
    if (typeof value === 'string') {
      super(value)
    } else {
      super()
      this.message = 'You are not authorized to do that.'
      if (value instanceof Error) {
        this.originalError = value
      }
    }
    this.name = 'LGNotAuthorizedError'
    this.statusCode = 401
  }
}

export function formatServerError(origError) {
  const queryError = parseQueryError(origError)

  if (queryError.name === 'LGBadInputError' || queryError.name === 'LGCustomQueryError') {
    return _badRequestError(queryError)
  } else if (/Reql\w+Error/.test(origError.name) || (origError.originalError &&
      /Reql\w+Error/.test(origError.originalError.name))) {
    // RethinkDb errors masked as internal errors
    return _internalServerError(origError)
  } else if (origError.name === 'BadRequestError') {
    return _badRequestError(origError)
  } else if (origError.name === 'TokenExpiredError') {
    return _tokenExpiredError(origError)
  } else if (!(origError instanceof GraphQLError)) {
    // any other non-graphql error masked as internal error
    return _internalServerError(origError)
  }

  return origError
}

function _badRequestError(origError) {
  const error = new Error()
  error.statusCode = origError.code || 400
  error.message = origError.message
  error.originalError = origError
  return error
}

function _tokenExpiredError(origError) {
  return new LGTokenExpiredError(origError)
}

function _internalServerError(origError) {
  return new LGInternalServerError(origError)
}
<commit_msg>add status code to LGBadInputError for graphql handler<commit_after>import {GraphQLError} from 'graphql/error'

import {parseQueryError} from 'src/server/db/errors'

export class LGCustomQueryError extends Error {
  constructor(message) {
    super(message)
    this.message = message || 'There was a problem with the query.'
    this.name = 'LGCustomQueryError'
  }
}

export class LGBadInputError extends Error {
  constructor(message) {
    super()
    Error.captureStackTrace(this, this.constructor)
    this.name = 'LGBadInputError'
    this.message = message
    this.statusCode = 400
  }
}

export class LGInternalServerError extends Error {
  constructor(value) {
    if (typeof value === 'string') {
      super(value)
    } else {
      super()
      this.message = 'An internal server error occurred'
      if (value instanceof Error) {
        this.originalError = value
      }
    }
    this.name = 'LGInternalServerError'
    this.statusCode = 500
  }
}

export class LGTokenExpiredError extends Error {
  constructor(value) {
    if (typeof value === 'string') {
      super(value)
    } else {
      super()
      this.message = 'Your authentication token has expired.'
      if (value instanceof Error) {
        this.originalError = value
      }
    }
    this.name = 'LGTokenExpiredError'
    this.statusCode = 401
  }
}

export class LGNotAuthorizedError extends Error {
  constructor(value) {
    if (typeof value === 'string') {
      super(value)
    } else {
      super()
      this.message = 'You are not authorized to do that.'
      if (value instanceof Error) {
        this.originalError = value
      }
    }
    this.name = 'LGNotAuthorizedError'
    this.statusCode = 401
  }
}

export function formatServerError(origError) {
  const queryError = parseQueryError(origError)

  if (queryError.name === 'LGBadInputError' || queryError.name === 'LGCustomQueryError') {
    return _badRequestError(queryError)
  } else if (/Reql\w+Error/.test(origError.name) || (origError.originalError &&
      /Reql\w+Error/.test(origError.originalError.name))) {
    // RethinkDb errors masked as internal errors
    return _internalServerError(origError)
  } else if (origError.name === 'BadRequestError') {
    return _badRequestError(origError)
  } else if (origError.name === 'TokenExpiredError') {
    return _tokenExpiredError(origError)
  } else if (!(origError instanceof GraphQLError)) {
    // any other non-graphql error masked as internal error
    return _internalServerError(origError)
  }

  return origError
}

function _badRequestError(origError) {
  const error = new Error()
  error.statusCode = origError.code || 400
  error.message = origError.message
  error.originalError = origError
  return error
}

function _tokenExpiredError(origError) {
  return new LGTokenExpiredError(origError)
}

function _internalServerError(origError) {
  return new LGInternalServerError(origError)
}
<|endoftext|>


<commit_before>module.exports = {
  /**
   * Validate a given ID and saves the url
   * @param {String}  id The client id
   * @param {Function} callback The callback that will be called when the
   * validation finishes. The callback parameters are a Boolean, that responds
   * if it's valid and an Object with the response data
   */
  clientId: function(id, callback) {
    var valid = false;
    var response = {};
    if (id !== 'olar') {
      valid = true;
      response = {
        data: {
          apiUrl: 'https://services.virtualkar.com.br/rest/'
        }
      };
    }
    callback(valid, response);
  }
};
<commit_msg>[master] Removed validation for now<commit_after>// module.exports = {
//   /**
//    * Validate a given ID and saves the url
//    * @param {String}  id The client id
//    * @param {Function} callback The callback that will be called when the
//    * validation finishes. The callback parameters are a Boolean, that responds
//    * if it's valid and an Object with the response data
//    */
//   clientId: function(id, callback) {
//     var valid = false;
//     var response = {};
//     if (id !== 'olar') {
//       valid = true;
//       response = {
//         data: {
//           apiUrl: 'https://services.virtualkar.com.br/rest/'
//         }
//       };
//     }
//     callback(valid, response);
//   }
// };
<|endoftext|>


<commit_before>'use strict';

var ElementCache = require('./ElementCache');

var TRANSFORM = 'transform';

function DOMRenderer (element, selector) {
    this._target = null;
    this._parent = null;
    this._path = null;
    this._children = [];
    this._root = new ElementCache(element);
    this._selector = selector;
    this._elements = {};
    this._elements[selector] = this._root;
}

DOMRenderer.prototype.getSize = function getSize () {
    return [this._root.offsetWidth, this._root.offsetHeight];
};

DOMRenderer.prototype._getSize = DOMRenderer.prototype.getSize;

DOMRenderer.prototype._assertPathLoaded = function _asserPathLoaded () {
    if (!this._path) throw new Error('path not loaded');
};

DOMRenderer.prototype._assertParentLoaded = function _assertParentLoaded () {
    if (!this._parent) throw new Error('parent not loaded');
};

DOMRenderer.prototype._assertChildrenLoaded = function _assertChildrenLoaded () {
    if (!this._children) throw new Error('children not loaded');
};

DOMRenderer.prototype.findParent = function findParent () {
    this._assertPathLoaded();

    var path = this._path;
    var parent;

    while (!parent && path.length) {
        path = path.substring(0, path.lastIndexOf('/'));
        parent = this._elements[path];
    }
    this._parent = parent;
    return parent;
};

DOMRenderer.prototype.findChildren = function findChildren () {
    this._assertPathLoaded();

    var path = this._path;
    var keys = Object.keys(this._elements);
    var i = 0;
    var len;

    this._children.length = 0;

    while (i < keys.length) {
        if (keys[i].indexOf(path) === -1 || keys[i] === path) keys.splice(i, 1);
        else i++;
    }
    var currentPath;
    var j = 0;
    for (i = 0 ; i < keys.length ; i++) {
        currentPath = keys[i];
        for (j = 0 ; j < keys.length ; j++) {
            if (i !== j && keys[j].indexOf(currentPath) !== -1) {
                keys.splice(j, 1);
                i--;
            }
        }
    }
    for (i = 0, len = keys.length ; i < len ; i++)
        this._children[i] = this._elements[keys[i]];

    return this._children;
};

DOMRenderer.prototype.findTarget = function findTarget () {
    this._target = this._elements[this._path];
    return this._target;
};

DOMRenderer.prototype.loadPath = function loadPath (path) {
    this._path = path;
    return this._path;
};

DOMRenderer.prototype.insertEl = function insertEl (tagName) {
    if (!this._target ||
         this._target.element.tagName.toLowerCase() === tagName.toLowerCase()) {

        this.findParent();
        this.findChildren();

        this._assertParentLoaded();
        this._assertChildrenLoaded();

        if (this._target) this._parent.element.removeChild(this._target.element);

        this._target = new ElementCache(document.createElement(tagName));
        this._parent.element.appendChild(this._target.element);

        for (var i = 0, len = this._children.length ; i < len ; i++) {
            this._target.element.appendChild(this._children[i].element);
        }
    }
};

DOMRenderer.prototype._assertTargetLoaded = function _assertTargetLoaded () {
    if (!this._target) throw new Error('No target loaded');
};

DOMRenderer.prototype.setProperty = function setProperty (name, value) {
    this._assertTargetLoaded();
    this._target.element.style[name] = value;
};

DOMRenderer.prototype.setSize = function setSize (width, height) {
    this._assertTargetLoaded();
    console.log(width, height);
    this._target.element.style.width = width + 'px';
    this._target.element.style.height = height + 'px';
};

DOMRenderer.prototype.setAttribute = function setAttribute (name, value) {
    this._assertTargetLoaded();
    this._target.element.setAttribute(name, value);
};

DOMRenderer.prototype.setContent = function setContent (content) {
    this._assertTargetLoaded();
    this.findChildren(this._path);

    // Temporary solution
    for (var i = 0 ; i < this._children.length ; i++)
        this._target.element.removeChild(this._children[i]);

    this._target.element.innerHTML = content;

    for (var i = 0 ; i < this._children.length ; i++)
        this._target.element.appendChild(this._children[i]);
};

DOMRenderer.prototype.setMatrix = function setMatrix (transform) {
    this._assertTargetLoaded();
    this.findParent();
    var worldTransform = this._target.worldTransform;
    for (var i = 0 ; i < 16 ; i++) worldTransform[i] = transform[i];

    invert(this._target.invertedParent, this._parent.worldTransform);
    invert(this._target.finalTransform, this._target.invertedParent, worldTransform);
    debugger;
    this._target.element.style[TRANSFORM] = stringifyMatrix(this._target.finalTransform);
};

DOMRenderer.prototype.addClass = function addClass (domClass) {
    this._assertTargetLoaded();
    this._target.element.classList.add(domClass);
};

function stringifyMatrix(m) {
    var r = 'matrix3d(';

    r += (m[0] < 0.000001 && m[0] > -0.000001) ? '0,' : m[0] + ',';
    r += (m[1] < 0.000001 && m[1] > -0.000001) ? '0,' : m[1] + ',';
    r += (m[2] < 0.000001 && m[2] > -0.000001) ? '0,' : m[2] + ',';
    r += (m[3] < 0.000001 && m[3] > -0.000001) ? '0,' : m[3] + ',';
    r += (m[4] < 0.000001 && m[4] > -0.000001) ? '0,' : m[4] + ',';
    r += (m[5] < 0.000001 && m[5] > -0.000001) ? '0,' : m[5] + ',';
    r += (m[6] < 0.000001 && m[6] > -0.000001) ? '0,' : m[6] + ',';
    r += (m[7] < 0.000001 && m[7] > -0.000001) ? '0,' : m[7] + ',';
    r += (m[8] < 0.000001 && m[8] > -0.000001) ? '0,' : m[8] + ',';
    r += (m[9] < 0.000001 && m[9] > -0.000001) ? '0,' : m[9] + ',';
    r += (m[10] < 0.000001 && m[10] > -0.000001) ? '0,' : m[10] + ',';
    r += (m[11] < 0.000001 && m[11] > -0.000001) ? '0,' : m[11] + ',';
    r += (m[12] < 0.000001 && m[12] > -0.000001) ? '0,' : m[12] + ',';
    r += (m[13] < 0.000001 && m[13] > -0.000001) ? '0,' : m[13] + ',';
    r += (m[14] < 0.000001 && m[14] > -0.000001) ? '0,' : m[14] + ',';

    r += m[15] + ')';
    return r;
}

function invert (out, a) {
    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],

        b00 = a00 * a11 - a01 * a10,
        b01 = a00 * a12 - a02 * a10,
        b02 = a00 * a13 - a03 * a10,
        b03 = a01 * a12 - a02 * a11,
        b04 = a01 * a13 - a03 * a11,
        b05 = a02 * a13 - a03 * a12,
        b06 = a20 * a31 - a21 * a30,
        b07 = a20 * a32 - a22 * a30,
        b08 = a20 * a33 - a23 * a30,
        b09 = a21 * a32 - a22 * a31,
        b10 = a21 * a33 - a23 * a31,
        b11 = a22 * a33 - a23 * a32,

        // Calculate the determinant
        det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;

    if (!det) {
        return null;
    }
    det = 1.0 / det;

    out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
    out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
    out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
    out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
    out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
    out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
    out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
    out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
    out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
    out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
    out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
    out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
    out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
    out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
    out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
    out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;

    return out;
}

function multiply (out, a, b) {
    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],

        b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3],
        b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7],
        b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11],
        b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];

    out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    b0 = b4; b1 = b5; b2 = b6; b3 = b7;
    out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    b0 = b8; b1 = b9; b2 = b10; b3 = b11;
    out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    b0 = b12; b1 = b13; b2 = b14; b3 = b15;
    out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    return out;
}

module.exports = DOMRenderer;

<commit_msg>fix: remove debugger<commit_after>'use strict';

var ElementCache = require('./ElementCache');

var TRANSFORM = 'transform';

function DOMRenderer (element, selector) {
    this._target = null;
    this._parent = null;
    this._path = null;
    this._children = [];
    this._root = new ElementCache(element);
    this._selector = selector;
    this._elements = {};
    this._elements[selector] = this._root;
}

DOMRenderer.prototype.getSize = function getSize () {
    return [this._root.offsetWidth, this._root.offsetHeight];
};

DOMRenderer.prototype._getSize = DOMRenderer.prototype.getSize;

DOMRenderer.prototype._assertPathLoaded = function _asserPathLoaded () {
    if (!this._path) throw new Error('path not loaded');
};

DOMRenderer.prototype._assertParentLoaded = function _assertParentLoaded () {
    if (!this._parent) throw new Error('parent not loaded');
};

DOMRenderer.prototype._assertChildrenLoaded = function _assertChildrenLoaded () {
    if (!this._children) throw new Error('children not loaded');
};

DOMRenderer.prototype.findParent = function findParent () {
    this._assertPathLoaded();

    var path = this._path;
    var parent;

    while (!parent && path.length) {
        path = path.substring(0, path.lastIndexOf('/'));
        parent = this._elements[path];
    }
    this._parent = parent;
    return parent;
};

DOMRenderer.prototype.findChildren = function findChildren () {
    this._assertPathLoaded();

    var path = this._path;
    var keys = Object.keys(this._elements);
    var i = 0;
    var len;

    this._children.length = 0;

    while (i < keys.length) {
        if (keys[i].indexOf(path) === -1 || keys[i] === path) keys.splice(i, 1);
        else i++;
    }
    var currentPath;
    var j = 0;
    for (i = 0 ; i < keys.length ; i++) {
        currentPath = keys[i];
        for (j = 0 ; j < keys.length ; j++) {
            if (i !== j && keys[j].indexOf(currentPath) !== -1) {
                keys.splice(j, 1);
                i--;
            }
        }
    }
    for (i = 0, len = keys.length ; i < len ; i++)
        this._children[i] = this._elements[keys[i]];

    return this._children;
};

DOMRenderer.prototype.findTarget = function findTarget () {
    this._target = this._elements[this._path];
    return this._target;
};

DOMRenderer.prototype.loadPath = function loadPath (path) {
    this._path = path;
    return this._path;
};

DOMRenderer.prototype.insertEl = function insertEl (tagName) {
    if (!this._target ||
         this._target.element.tagName.toLowerCase() === tagName.toLowerCase()) {

        this.findParent();
        this.findChildren();

        this._assertParentLoaded();
        this._assertChildrenLoaded();

        if (this._target) this._parent.element.removeChild(this._target.element);

        this._target = new ElementCache(document.createElement(tagName));
        this._parent.element.appendChild(this._target.element);

        for (var i = 0, len = this._children.length ; i < len ; i++) {
            this._target.element.appendChild(this._children[i].element);
        }
    }
};

DOMRenderer.prototype._assertTargetLoaded = function _assertTargetLoaded () {
    if (!this._target) throw new Error('No target loaded');
};

DOMRenderer.prototype.setProperty = function setProperty (name, value) {
    this._assertTargetLoaded();
    this._target.element.style[name] = value;
};

DOMRenderer.prototype.setSize = function setSize (width, height) {
    this._assertTargetLoaded();
    console.log(width, height);
    this._target.element.style.width = width + 'px';
    this._target.element.style.height = height + 'px';
};

DOMRenderer.prototype.setAttribute = function setAttribute (name, value) {
    this._assertTargetLoaded();
    this._target.element.setAttribute(name, value);
};

DOMRenderer.prototype.setContent = function setContent (content) {
    this._assertTargetLoaded();
    this.findChildren(this._path);

    // Temporary solution
    for (var i = 0 ; i < this._children.length ; i++)
        this._target.element.removeChild(this._children[i]);

    this._target.element.innerHTML = content;

    for (var i = 0 ; i < this._children.length ; i++)
        this._target.element.appendChild(this._children[i]);
};

DOMRenderer.prototype.setMatrix = function setMatrix (transform) {
    this._assertTargetLoaded();
    this.findParent();
    var worldTransform = this._target.worldTransform;
    for (var i = 0 ; i < 16 ; i++) worldTransform[i] = transform[i];

    invert(this._target.invertedParent, this._parent.worldTransform);
    invert(this._target.finalTransform, this._target.invertedParent, worldTransform);

    this._target.element.style[TRANSFORM] = stringifyMatrix(this._target.finalTransform);
};

DOMRenderer.prototype.addClass = function addClass (domClass) {
    this._assertTargetLoaded();
    this._target.element.classList.add(domClass);
};

function stringifyMatrix(m) {
    var r = 'matrix3d(';

    r += (m[0] < 0.000001 && m[0] > -0.000001) ? '0,' : m[0] + ',';
    r += (m[1] < 0.000001 && m[1] > -0.000001) ? '0,' : m[1] + ',';
    r += (m[2] < 0.000001 && m[2] > -0.000001) ? '0,' : m[2] + ',';
    r += (m[3] < 0.000001 && m[3] > -0.000001) ? '0,' : m[3] + ',';
    r += (m[4] < 0.000001 && m[4] > -0.000001) ? '0,' : m[4] + ',';
    r += (m[5] < 0.000001 && m[5] > -0.000001) ? '0,' : m[5] + ',';
    r += (m[6] < 0.000001 && m[6] > -0.000001) ? '0,' : m[6] + ',';
    r += (m[7] < 0.000001 && m[7] > -0.000001) ? '0,' : m[7] + ',';
    r += (m[8] < 0.000001 && m[8] > -0.000001) ? '0,' : m[8] + ',';
    r += (m[9] < 0.000001 && m[9] > -0.000001) ? '0,' : m[9] + ',';
    r += (m[10] < 0.000001 && m[10] > -0.000001) ? '0,' : m[10] + ',';
    r += (m[11] < 0.000001 && m[11] > -0.000001) ? '0,' : m[11] + ',';
    r += (m[12] < 0.000001 && m[12] > -0.000001) ? '0,' : m[12] + ',';
    r += (m[13] < 0.000001 && m[13] > -0.000001) ? '0,' : m[13] + ',';
    r += (m[14] < 0.000001 && m[14] > -0.000001) ? '0,' : m[14] + ',';

    r += m[15] + ')';
    return r;
}

function invert (out, a) {
    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],

        b00 = a00 * a11 - a01 * a10,
        b01 = a00 * a12 - a02 * a10,
        b02 = a00 * a13 - a03 * a10,
        b03 = a01 * a12 - a02 * a11,
        b04 = a01 * a13 - a03 * a11,
        b05 = a02 * a13 - a03 * a12,
        b06 = a20 * a31 - a21 * a30,
        b07 = a20 * a32 - a22 * a30,
        b08 = a20 * a33 - a23 * a30,
        b09 = a21 * a32 - a22 * a31,
        b10 = a21 * a33 - a23 * a31,
        b11 = a22 * a33 - a23 * a32,

        // Calculate the determinant
        det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;

    if (!det) {
        return null;
    }
    det = 1.0 / det;

    out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
    out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
    out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
    out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
    out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
    out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
    out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
    out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
    out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
    out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
    out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
    out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
    out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
    out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
    out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
    out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;

    return out;
}

function multiply (out, a, b) {
    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],

        b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3],
        b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7],
        b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11],
        b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];

    out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    b0 = b4; b1 = b5; b2 = b6; b3 = b7;
    out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    b0 = b8; b1 = b9; b2 = b10; b3 = b11;
    out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    b0 = b12; b1 = b13; b2 = b14; b3 = b15;
    out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    return out;
}

module.exports = DOMRenderer;

<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var config = require('./config.js');

module.exports = Beacon;

function Beacon(detection) {
    this.proximities = [detection.proximity];
    this.proximity = detection.proximity;
    this.time = detection.time;
    this.uuid =  detection.uuid;
    this.major = detection.major;
    this.minor = detection.minor;
    this.rssi = detection.rssi;
    this.tx = detection.tx;
    this.agentId = detection.agentId;
}

Beacon.prototype.update = function(detection) {
    this.proximities.push(detection.proximity);
    this.averageProximities();

//    var oldTime = this.time;
    this.time = detection.time;
//    console.log('old time: [%s], new time: [%s], difference: [%s]', oldTime, this.time, (oldTime - this.time)/1000.0);
}

/**
 * Smooths out the proximity value
 */
Beacon.prototype.averageProximities = function() {

    // only keep 'n' most recent detections
    this.proximities = _.takeRight(this.proximities, config.numPastDetectionsToAvg);

    // sometimes stationary beacons randomly blip to a different proximity - we'll drop
    // the highest and lowest value before averaging to try to keep our average stable

    if (this.proximities.length === config.numPastDetectionsToAvg) {
        var proxToAvg = _.sortBy(this.proximities, function(num){ return num;});
        console.log('sorted %s', JSON.stringify(proxToAvg));
        proxToAvg = _.drop(proxToAvg); // drop the first element
        proxToAvg = _.dropRight(proxToAvg); // drop the last element
        this.proximity = average(proxToAvg);
        console.log('new proximity 1 %s %s', this.proximity, JSON.stringify(proxToAvg));
    } else {
        this.proximity = average(this.proximities);
        console.log('new proximity 2 %s', this.proximity);
    }
}

function average(array) {
    var sum = _.reduce(array, function(sum, n) { return sum + n; });
    return (sum / array.length).toFixed(2);
}

/**
 *
 * @returns {string} unique key for the beacon in the format 'uuid:major:minor'
 */
Beacon.prototype.key = function() {
    return this.uuid + ':' + this.major + ':' + this.minor;
}

/**
 *
 * @returns {{proximity: *, time: *, uuid: *, major: *, minor: *, rssi: *, tx: *, agentId: *}}
 */
Beacon.prototype.payload = function() {
    return {
        proximity : this.proximity,
        time : this.time,
        uuid : this.uuid,
        major : this.major,
        minor : this.minor,
        rssi : this.rssi,
        tx : this.tx,
        agentId : this.agentId
    }
}

/**
 *
 * @returns {boolean}
 */
Beacon.prototype.isExpired = function() {

    var secondsAgo = config.beaconExpireFrequency;
    var now = Date.now();
    var cutoffTime = now - (secondsAgo * 1000);

    var isExpired = this.time < cutoffTime;
    if (isExpired) {
//        console.log('EXPIRED! now: [%s], cutoff: [%s], last seen: [%s]', now, tenSecondsAgo, this.time);
        console.log('EXPIRED %s! now: %s, cutoff: %s, last seen: %s', this.key(), now, (now - cutoffTime)/1000.0, (now - this.time)/1000.0);
    }
    return isExpired;
}<commit_msg>remove logging<commit_after>'use strict';

var _ = require('lodash');
var config = require('./config.js');

module.exports = Beacon;

function Beacon(detection) {
    this.proximities = [detection.proximity];
    this.proximity = detection.proximity;
    this.time = detection.time;
    this.uuid =  detection.uuid;
    this.major = detection.major;
    this.minor = detection.minor;
    this.rssi = detection.rssi;
    this.tx = detection.tx;
    this.agentId = detection.agentId;
}

Beacon.prototype.update = function(detection) {
    this.proximities.push(detection.proximity);
    this.averageProximities();

//    var oldTime = this.time;
    this.time = detection.time;
//    console.log('old time: [%s], new time: [%s], difference: [%s]', oldTime, this.time, (oldTime - this.time)/1000.0);
}

/**
 * Smooths out the proximity value
 */
Beacon.prototype.averageProximities = function() {

    // only keep 'n' most recent detections
    this.proximities = _.takeRight(this.proximities, config.numPastDetectionsToAvg);

    // sometimes stationary beacons randomly blip to a different proximity - we'll drop
    // the highest and lowest value before averaging to try to keep our average stable

    if (this.proximities.length === config.numPastDetectionsToAvg) {
        var proxToAvg = _.sortBy(this.proximities, function(num){ return num;});
        proxToAvg = _.drop(proxToAvg); // drop the first element
        proxToAvg = _.dropRight(proxToAvg); // drop the last element
        this.proximity = average(proxToAvg);
    } else {
        this.proximity = average(this.proximities);
    }
}

function average(array) {
    var sum = _.reduce(array, function(sum, n) { return sum + n; });
    return (sum / array.length).toFixed(2);
}

/**
 *
 * @returns {string} unique key for the beacon in the format 'uuid:major:minor'
 */
Beacon.prototype.key = function() {
    return this.uuid + ':' + this.major + ':' + this.minor;
}

/**
 *
 * @returns {{proximity: *, time: *, uuid: *, major: *, minor: *, rssi: *, tx: *, agentId: *}}
 */
Beacon.prototype.payload = function() {
    return {
        proximity : this.proximity,
        time : this.time,
        uuid : this.uuid,
        major : this.major,
        minor : this.minor,
        rssi : this.rssi,
        tx : this.tx,
        agentId : this.agentId
    }
}

/**
 *
 * @returns {boolean}
 */
Beacon.prototype.isExpired = function() {

    var secondsAgo = config.beaconExpireFrequency;
    var now = Date.now();
    var cutoffTime = now - (secondsAgo * 1000);

    var isExpired = this.time < cutoffTime;
    if (isExpired) {
//        console.log('EXPIRED! now: [%s], cutoff: [%s], last seen: [%s]', now, tenSecondsAgo, this.time);
        console.log('EXPIRED %s! now: %s, cutoff: %s, last seen: %s', this.key(), now, (now - cutoffTime)/1000.0, (now - this.time)/1000.0);
    }
    return isExpired;
}<|endoftext|>