<commit_before>import { run } from '@ember/runloop';
import { defer, reject, resolve } from 'rsvp';
import { module } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { find, render, click, settled, waitUntil } from '@ember/test-helpers';
import { test, testBS4, defaultButtonClass } from '../../helpers/bootstrap-test';

import hbs from 'htmlbars-inline-precompile';
import setupNoDeprecations from '../../helpers/setup-no-deprecations';

module('Integration | Component | bs-button', function(hooks) {
  setupRenderingTest(hooks);
  setupNoDeprecations(hooks);

  hooks.beforeEach(function() {
    this.actions = {};
    this.send = (actionName, ...args) => this.actions[actionName].apply(this, args);
  });

  test('button has correct default markup', async function(assert) {
    await render(hbs`{{#bs-button}}Test{{/bs-button}}`);

    assert.dom('button').hasClass('btn', 'button has btn class');
    assert.dom('button').hasClass(defaultButtonClass(), 'button has type class');
  });

  test('button has correct size', async function(assert) {
    await render(hbs`{{#bs-button size="lg"}}Test{{/bs-button}}`);

    assert.dom('button').hasClass('btn-lg', 'button has size class');
  });

  test('button has correct type', async function(assert) {
    await render(hbs`{{#bs-button type="primary"}}Test{{/bs-button}}`);

    assert.dom('button').hasClass('btn', 'button has btn class');
    assert.dom('button').hasClass('btn-primary', 'button has type class');
    assert.dom('button').doesNotHaveClass('btn-outline-primary', 'button does not have outline class');
  });

  test('button can be active', async function(assert) {
    await render(hbs`{{#bs-button active=true}}Test{{/bs-button}}`);

    assert.dom('button').hasClass('active', 'button has active class');
  });

  test('button can be block', async function(assert) {
    await render(hbs`{{#bs-button block=true}}Test{{/bs-button}}`);

    assert.dom('button').hasClass('btn-block', 'button has block class');
  });

  test('button has HTML attributes', async function(assert) {
    await render(hbs`{{#bs-button id="test" disabled=true title="title"}}Test{{/bs-button}}`);

    assert.equal(this.element.querySelector('button').getAttribute('id'), 'test');
    assert.equal(this.element.querySelector('button').getAttribute('disabled'), '');
    assert.equal(this.element.querySelector('button').getAttribute('title'), 'title');
  });

  test('button has default label', async function(assert) {
    await render(hbs`{{bs-button defaultText="test"}}`);
    assert.dom('button').hasText('test');
  });

  test('button has default type "button"', async function(assert) {
    await render(hbs`{{bs-button}}`);
    assert.equal(this.element.querySelector('button').type, 'button');
  });

  test('buttonType property allows changing button type', async function(assert) {
    await render(hbs`{{bs-button buttonType="submit"}}`);
    assert.equal(this.element.querySelector('button').type, 'submit');
  });

  test('button with icon property shows icon', async function(assert) {
    await render(hbs`{{bs-button icon="fas fa-check"}}`);

    assert.dom('button i').hasClass('fas');
    assert.dom('button i').hasClass('fa-check');
  });

  testBS4('button with outline property shows as outline', async function(assert) {
    await render(hbs`{{bs-button type="primary" outline=true}}`);
    assert.dom('button').hasClass('btn-outline-primary');
    assert.dom('button').doesNotHaveClass('btn-primary');
  });

  test('button with iconActive and iconInactive properties shows icon depending on active state', async function(assert) {
    this.set('active', false);
    await render(hbs`{{bs-button active=active iconInactive="fas fa-plus" iconActive="fas fa-minus"}}`);

    assert.dom('button i').hasClass('fas');
    assert.dom('button i').hasClass('fa-plus');

    this.set('active', true);

    assert.dom('button i').hasClass('fas');
    assert.dom('button i').hasClass('fa-minus');

    this.set('active', false);

    assert.dom('button i').hasClass('fas');
    assert.dom('button i').hasClass('fa-plus');
  });

  test('clicking a button sends onClick action with "value" property as a parameter', async function(assert) {
    let action = this.spy();
    this.actions.testAction = action;
    await render(hbs`{{bs-button onClick=(action "testAction") value="dummy"}}`);

    await click('button');
    assert.ok(action.calledWith('dummy'), 'onClick action has been called with button value');
  });

  test('button text is changed according to button state', async function(assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(
      hbs`{{bs-button
      defaultText="default text"
      pendingText="text for pending state"
      fulfilledText="text for fulfilled state"
      rejectedText="text for rejected state"
      onClick=clickAction
    }}`);
    assert.dom('button').hasText('default text');

    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'text for pending state';
    });

    deferredClickAction.resolve();
    await settled();
    assert.dom('button').hasText('text for fulfilled state');

    deferredClickAction = defer();
    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'text for pending state';
    });

    deferredClickAction.reject();
    await settled();
    assert.dom('button').hasText('text for rejected state');
  });

  test('button text remains to default if no state text wasn\'t set', async function (assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(
      hbs`{{bs-button
      defaultText="default text"
      onClick=clickAction
    }}`);
    assert.dom('button').hasText('default text');

    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'default text';
    });

    deferredClickAction.resolve();
    await settled();
    assert.dom('button').hasText('default text');

    deferredClickAction = defer();
    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'default text';
    });

    deferredClickAction.reject();
    await settled();
    assert.dom('button').hasText('default text');
  });

  test('setting reset to true resets button text', async function(assert) {
    this.set('clickAction', () => {
      return resolve();
    });

    await render(hbs`
      {{bs-button defaultText="default text" fulfilledText="text for fulfilled state" reset=reset onClick=clickAction}}
    `);
    assert.dom('button').hasText('default text');

    await click('button');
    assert.dom('button').hasText('text for fulfilled state');

    run(() => this.set('reset', true));
    assert.dom('button').hasText('default text');
  });

  test('button is disabled while in pending state', async function(assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(hbs`{{bs-button onClick=clickAction}}`);
    assert.dom('button').isNotDisabled();

    await click('button');
    assert.dom('button').isDisabled();

    deferredClickAction.resolve();
    await settled();
    assert.dom('button').isNotDisabled();
  });

  test('button is not disabled while in pending state if preventConcurrency is false', async function(assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(hbs`<BsButton @onClick={{clickAction}} @preventConcurrency={{false}} />`);
    await click('button');
    assert.dom('button').isNotDisabled();

    deferredClickAction.resolve();
    await settled();
  });

  test('setting @disabled property to false prevents button from being disabled while in pending state', async function(assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(hbs`<BsButton @disabled={{false}} @onClick={{clickAction}} />`);
    await click('button');
    assert.dom('button').isNotDisabled();

    deferredClickAction.resolve();
    await settled();
  });

  test('setting disabled HTML attribute to false prevents button from being disabled while in pending state', async function(assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(hbs`<BsButton @onClick={{clickAction}} disabled={{false}} />`);
    await click('button');
    assert.dom('button').isNotDisabled();

    deferredClickAction.resolve();
    await settled();
  });

  test('isPending, isFulfilled and isRejected properties are yielded', async function (assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });
    await render(hbs`{{#bs-button reset=reset onClick=clickAction as |button|}}
      {{#if button.isPending}}isPending{{/if}}
      {{#if button.isFulfilled}}isFulfilled{{/if}}
      {{#if button.isRejected}}isRejected{{/if}}
    {{/bs-button}}`);
    assert.dom('button').hasText('');

    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'isPending';
    });

    deferredClickAction.resolve();
    await settled();
    assert.dom('button').hasText('isFulfilled');

    deferredClickAction = defer();
    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'isPending';
    });

    deferredClickAction.reject();
    await settled();
    assert.dom('button').hasText('isRejected');

    run(() => this.set('reset', true));

    assert.dom('button').hasText('');
  });

  test('isSettled shorthand is yielded', async function (assert) {
    this.set('clickAction', () => {
      return resolve();
    });
    await render(hbs`{{#bs-button reset=reset onClick=clickAction as |button|}}
      {{#if button.isSettled}}isSettled{{/if}}
    {{/bs-button}}`);

    assert.dom('button').hasText('');

    await click('button');
    assert.dom('button').hasText('isSettled');

    run(() => this.set('reset', true));
    assert.dom('button').hasText('');

    this.set('clickAction', () => {
      return reject();
    });
    await click('button');
    assert.dom('button').hasText('isSettled');
  });

  test('clicking a button with onClick action will prevent event to bubble up', async function(assert) {
    let buttonClick = this.spy();
    this.actions.buttonClick = buttonClick;
    let parentClick = this.spy();
    this.actions.parentClick = parentClick;

    await render(
      hbs`<div {{action "parentClick"}}>{{#bs-button onClick=(action "buttonClick")}}Button{{/bs-button}}</div>`
    );

    await click('button');
    assert.ok(buttonClick.called);
    assert.notOk(parentClick.called);
  });

  test('clicking a button without onClick action will cause event to bubble up', async function(assert) {
    let parentClick = this.spy();
    this.actions.parentClick = parentClick;

    await render(hbs`<div {{action "parentClick"}}>{{#bs-button}}Button{{/bs-button}}</div>`);

    await click('button');
    assert.ok(parentClick.called);
  });

  test('clicking a button with onClick action and bubble=true will cause event to bubble up', async function(assert) {
    let buttonClick = this.spy();
    this.actions.buttonClick = buttonClick;
    let parentClick = this.spy();
    this.actions.parentClick = parentClick;

    await render(
      hbs`<div {{action "parentClick"}}>{{#bs-button bubble=true onClick=(action "buttonClick")}}Button{{/bs-button}}</div>`
    );

    await click('button');
    assert.ok(buttonClick.called);
    assert.ok(parentClick.called);
  });

  test('prevents onClick action to be fired concurrently', async function(assert) {
    let deferredClickAction = defer();
    let clickActionHasBeenExecuted = false;
    this.set('clickAction', () => {
      clickActionHasBeenExecuted = true;
      return deferredClickAction.promise;
    });

    await render(hbs`{{bs-button onClick=clickAction}}`);
    click('button');
    await waitUntil(() => clickActionHasBeenExecuted);

    this.set('clickAction', () => {
      assert.ok(false, 'onClick action is not executed concurrently');
    });
    await click('button');

    deferredClickAction.resolve();
    await settled();

    this.set('clickAction', () => {
      assert.step('onClick action');
    });
    await click('button');
    assert.verifySteps(['onClick action'], 'onClick action is fired again after pending click action is settled');
  });

  test('preventConcurrency=false allows onClick action to be fired concurrently', async function(assert) {
    let deferredClickAction = defer();
    let clickActionExecutionCount = 0;

    this.set('clickAction', () => {
      clickActionExecutionCount++;
      return deferredClickAction.promise;
    });
    await render(hbs`{{bs-button onClick=clickAction preventConcurrency=false}}`);

    await click('button');
    assert.equal(clickActionExecutionCount, 1);

    await click('button');
    assert.equal(clickActionExecutionCount, 2);

    deferredClickAction.resolve();
  });
});
<commit_msg>Skip angle bracket test for Ember versions without native support<commit_after>import { run } from '@ember/runloop';
import { defer, reject, resolve } from 'rsvp';
import { module, skip } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { find, render, click, settled, waitUntil } from '@ember/test-helpers';
import { test, testBS4, defaultButtonClass } from '../../helpers/bootstrap-test';
import { gte } from 'ember-compatibility-helpers';

import hbs from 'htmlbars-inline-precompile';
import setupNoDeprecations from '../../helpers/setup-no-deprecations';

module('Integration | Component | bs-button', function(hooks) {
  setupRenderingTest(hooks);
  setupNoDeprecations(hooks);

  hooks.beforeEach(function() {
    this.actions = {};
    this.send = (actionName, ...args) => this.actions[actionName].apply(this, args);
  });

  test('button has correct default markup', async function(assert) {
    await render(hbs`{{#bs-button}}Test{{/bs-button}}`);

    assert.dom('button').hasClass('btn', 'button has btn class');
    assert.dom('button').hasClass(defaultButtonClass(), 'button has type class');
  });

  test('button has correct size', async function(assert) {
    await render(hbs`{{#bs-button size="lg"}}Test{{/bs-button}}`);

    assert.dom('button').hasClass('btn-lg', 'button has size class');
  });

  test('button has correct type', async function(assert) {
    await render(hbs`{{#bs-button type="primary"}}Test{{/bs-button}}`);

    assert.dom('button').hasClass('btn', 'button has btn class');
    assert.dom('button').hasClass('btn-primary', 'button has type class');
    assert.dom('button').doesNotHaveClass('btn-outline-primary', 'button does not have outline class');
  });

  test('button can be active', async function(assert) {
    await render(hbs`{{#bs-button active=true}}Test{{/bs-button}}`);

    assert.dom('button').hasClass('active', 'button has active class');
  });

  test('button can be block', async function(assert) {
    await render(hbs`{{#bs-button block=true}}Test{{/bs-button}}`);

    assert.dom('button').hasClass('btn-block', 'button has block class');
  });

  test('button has HTML attributes', async function(assert) {
    await render(hbs`{{#bs-button id="test" disabled=true title="title"}}Test{{/bs-button}}`);

    assert.equal(this.element.querySelector('button').getAttribute('id'), 'test');
    assert.equal(this.element.querySelector('button').getAttribute('disabled'), '');
    assert.equal(this.element.querySelector('button').getAttribute('title'), 'title');
  });

  test('button has default label', async function(assert) {
    await render(hbs`{{bs-button defaultText="test"}}`);
    assert.dom('button').hasText('test');
  });

  test('button has default type "button"', async function(assert) {
    await render(hbs`{{bs-button}}`);
    assert.equal(this.element.querySelector('button').type, 'button');
  });

  test('buttonType property allows changing button type', async function(assert) {
    await render(hbs`{{bs-button buttonType="submit"}}`);
    assert.equal(this.element.querySelector('button').type, 'submit');
  });

  test('button with icon property shows icon', async function(assert) {
    await render(hbs`{{bs-button icon="fas fa-check"}}`);

    assert.dom('button i').hasClass('fas');
    assert.dom('button i').hasClass('fa-check');
  });

  testBS4('button with outline property shows as outline', async function(assert) {
    await render(hbs`{{bs-button type="primary" outline=true}}`);
    assert.dom('button').hasClass('btn-outline-primary');
    assert.dom('button').doesNotHaveClass('btn-primary');
  });

  test('button with iconActive and iconInactive properties shows icon depending on active state', async function(assert) {
    this.set('active', false);
    await render(hbs`{{bs-button active=active iconInactive="fas fa-plus" iconActive="fas fa-minus"}}`);

    assert.dom('button i').hasClass('fas');
    assert.dom('button i').hasClass('fa-plus');

    this.set('active', true);

    assert.dom('button i').hasClass('fas');
    assert.dom('button i').hasClass('fa-minus');

    this.set('active', false);

    assert.dom('button i').hasClass('fas');
    assert.dom('button i').hasClass('fa-plus');
  });

  test('clicking a button sends onClick action with "value" property as a parameter', async function(assert) {
    let action = this.spy();
    this.actions.testAction = action;
    await render(hbs`{{bs-button onClick=(action "testAction") value="dummy"}}`);

    await click('button');
    assert.ok(action.calledWith('dummy'), 'onClick action has been called with button value');
  });

  test('button text is changed according to button state', async function(assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(
      hbs`{{bs-button
      defaultText="default text"
      pendingText="text for pending state"
      fulfilledText="text for fulfilled state"
      rejectedText="text for rejected state"
      onClick=clickAction
    }}`);
    assert.dom('button').hasText('default text');

    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'text for pending state';
    });

    deferredClickAction.resolve();
    await settled();
    assert.dom('button').hasText('text for fulfilled state');

    deferredClickAction = defer();
    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'text for pending state';
    });

    deferredClickAction.reject();
    await settled();
    assert.dom('button').hasText('text for rejected state');
  });

  test('button text remains to default if no state text wasn\'t set', async function (assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(
      hbs`{{bs-button
      defaultText="default text"
      onClick=clickAction
    }}`);
    assert.dom('button').hasText('default text');

    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'default text';
    });

    deferredClickAction.resolve();
    await settled();
    assert.dom('button').hasText('default text');

    deferredClickAction = defer();
    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'default text';
    });

    deferredClickAction.reject();
    await settled();
    assert.dom('button').hasText('default text');
  });

  test('setting reset to true resets button text', async function(assert) {
    this.set('clickAction', () => {
      return resolve();
    });

    await render(hbs`
      {{bs-button defaultText="default text" fulfilledText="text for fulfilled state" reset=reset onClick=clickAction}}
    `);
    assert.dom('button').hasText('default text');

    await click('button');
    assert.dom('button').hasText('text for fulfilled state');

    run(() => this.set('reset', true));
    assert.dom('button').hasText('default text');
  });

  test('button is disabled while in pending state', async function(assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(hbs`{{bs-button onClick=clickAction}}`);
    assert.dom('button').isNotDisabled();

    await click('button');
    assert.dom('button').isDisabled();

    deferredClickAction.resolve();
    await settled();
    assert.dom('button').isNotDisabled();
  });

  test('button is not disabled while in pending state if preventConcurrency is false', async function(assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(hbs`<BsButton @onClick={{clickAction}} @preventConcurrency={{false}} />`);
    await click('button');
    assert.dom('button').isNotDisabled();

    deferredClickAction.resolve();
    await settled();
  });

  test('setting @disabled property to false prevents button from being disabled while in pending state', async function(assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(hbs`<BsButton @disabled={{false}} @onClick={{clickAction}} />`);
    await click('button');
    assert.dom('button').isNotDisabled();

    deferredClickAction.resolve();
    await settled();
  });

  (gte('3.4.0') ? test : skip)('setting disabled HTML attribute to false prevents button from being disabled while in pending state', async function(assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });

    await render(hbs`<BsButton @onClick={{clickAction}} disabled={{false}} />`);
    await click('button');
    assert.dom('button').isNotDisabled();

    deferredClickAction.resolve();
    await settled();
  });

  test('isPending, isFulfilled and isRejected properties are yielded', async function (assert) {
    let deferredClickAction = defer();
    this.set('clickAction', () => {
      return deferredClickAction.promise;
    });
    await render(hbs`{{#bs-button reset=reset onClick=clickAction as |button|}}
      {{#if button.isPending}}isPending{{/if}}
      {{#if button.isFulfilled}}isFulfilled{{/if}}
      {{#if button.isRejected}}isRejected{{/if}}
    {{/bs-button}}`);
    assert.dom('button').hasText('');

    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'isPending';
    });

    deferredClickAction.resolve();
    await settled();
    assert.dom('button').hasText('isFulfilled');

    deferredClickAction = defer();
    click('button');
    await waitUntil(() => {
      return find('button').textContent.trim() === 'isPending';
    });

    deferredClickAction.reject();
    await settled();
    assert.dom('button').hasText('isRejected');

    run(() => this.set('reset', true));

    assert.dom('button').hasText('');
  });

  test('isSettled shorthand is yielded', async function (assert) {
    this.set('clickAction', () => {
      return resolve();
    });
    await render(hbs`{{#bs-button reset=reset onClick=clickAction as |button|}}
      {{#if button.isSettled}}isSettled{{/if}}
    {{/bs-button}}`);

    assert.dom('button').hasText('');

    await click('button');
    assert.dom('button').hasText('isSettled');

    run(() => this.set('reset', true));
    assert.dom('button').hasText('');

    this.set('clickAction', () => {
      return reject();
    });
    await click('button');
    assert.dom('button').hasText('isSettled');
  });

  test('clicking a button with onClick action will prevent event to bubble up', async function(assert) {
    let buttonClick = this.spy();
    this.actions.buttonClick = buttonClick;
    let parentClick = this.spy();
    this.actions.parentClick = parentClick;

    await render(
      hbs`<div {{action "parentClick"}}>{{#bs-button onClick=(action "buttonClick")}}Button{{/bs-button}}</div>`
    );

    await click('button');
    assert.ok(buttonClick.called);
    assert.notOk(parentClick.called);
  });

  test('clicking a button without onClick action will cause event to bubble up', async function(assert) {
    let parentClick = this.spy();
    this.actions.parentClick = parentClick;

    await render(hbs`<div {{action "parentClick"}}>{{#bs-button}}Button{{/bs-button}}</div>`);

    await click('button');
    assert.ok(parentClick.called);
  });

  test('clicking a button with onClick action and bubble=true will cause event to bubble up', async function(assert) {
    let buttonClick = this.spy();
    this.actions.buttonClick = buttonClick;
    let parentClick = this.spy();
    this.actions.parentClick = parentClick;

    await render(
      hbs`<div {{action "parentClick"}}>{{#bs-button bubble=true onClick=(action "buttonClick")}}Button{{/bs-button}}</div>`
    );

    await click('button');
    assert.ok(buttonClick.called);
    assert.ok(parentClick.called);
  });

  test('prevents onClick action to be fired concurrently', async function(assert) {
    let deferredClickAction = defer();
    let clickActionHasBeenExecuted = false;
    this.set('clickAction', () => {
      clickActionHasBeenExecuted = true;
      return deferredClickAction.promise;
    });

    await render(hbs`{{bs-button onClick=clickAction}}`);
    click('button');
    await waitUntil(() => clickActionHasBeenExecuted);

    this.set('clickAction', () => {
      assert.ok(false, 'onClick action is not executed concurrently');
    });
    await click('button');

    deferredClickAction.resolve();
    await settled();

    this.set('clickAction', () => {
      assert.step('onClick action');
    });
    await click('button');
    assert.verifySteps(['onClick action'], 'onClick action is fired again after pending click action is settled');
  });

  test('preventConcurrency=false allows onClick action to be fired concurrently', async function(assert) {
    let deferredClickAction = defer();
    let clickActionExecutionCount = 0;

    this.set('clickAction', () => {
      clickActionExecutionCount++;
      return deferredClickAction.promise;
    });
    await render(hbs`{{bs-button onClick=clickAction preventConcurrency=false}}`);

    await click('button');
    assert.equal(clickActionExecutionCount, 1);

    await click('button');
    assert.equal(clickActionExecutionCount, 2);

    deferredClickAction.resolve();
  });
});
<|endoftext|>


<commit_before> /*
 * HexGL
 * @author Thibaut 'BKcore' Despoulain <http://bkcore.com>
 * @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
 *          To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/.
 */

var bkcore = bkcore || {};
bkcore.hexgl = bkcore.hexgl || {};

bkcore.hexgl.CameraChase = function(opts)
{
	this.dir = new THREE.Vector3(0,0,1);
	this.up = new THREE.Vector3(0,1,0);
	this.target = new THREE.Vector3();
	this.speedOffset = 0;
	this.speedOffsetMax = 10;
	this.speedOffsetStep = 0.05;

	this.modes = {
		CHASE: 0,
		ORBIT: 1
	}
	this.mode = this.modes.CHASE;

	this.camera = opts.camera;
	this.targetObject = opts.target;
	this.cameraCube = opts.cameraCube == undefined ? null : opts.cameraCube;

	this.yoffset = opts.yoffest == undefined ? 8.0 : opts.yoffest;
	this.zoffset = opts.zoffset == undefined ? 10.0 : opts.zoffset;
	this.viewOffset = opts.viewOffset == undefined ? 10.0 : opts.viewOffset;
	this.orbitOffset = 12;
	this.lerp = opts.lerp == undefined ? 0.5 : opts.lerp;
	this.time = 0.0;
}

bkcore.hexgl.CameraChase.prototype.update = function(dt, ratio)
{
	if(this.mode == this.modes.CHASE)
	{
		this.dir.set(0,0,1);
		this.up.set(0,1,0);

		this.targetObject.matrix.rotateAxis(this.up);
		this.targetObject.matrix.rotateAxis(this.dir);

		this.speedOffset += (this.speedOffsetMax*ratio - this.speedOffset) * Math.min(1, 0.3*dt);

		this.target.copy(this.targetObject.position);
		this.target.subSelf(this.dir.multiplyScalar(this.zoffset + this.speedOffset));
		this.target.addSelf(this.up.multiplyScalar(this.yoffset));
		this.target.y += -this.up.y + this.yoffset;
		this.camera.position.copy(this.target);

		this.camera.lookAt(this.dir.normalize().multiplyScalar(this.viewOffset).addSelf(this.targetObject.position));
	}
	else if(this.mode == this.modes.ORBIT)
	{
		this.time += dt*.008;
		this.dir.set(
			Math.cos(this.time)*this.orbitOffset,
			this.yoffset/2,
			Math.sin(this.time)*this.orbitOffset
		);
		this.target.copy(this.targetObject.position).addSelf(this.dir);
		this.camera.position.copy(this.target);
		this.camera.lookAt(this.targetObject.position);
	}

	if(this.cameraCube != null)
		this.cameraCube.rotation.copy(this.camera.rotation);
}<commit_msg>add secret top view<commit_after> /*
 * HexGL
 * @author Thibaut 'BKcore' Despoulain <http://bkcore.com>
 * @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
 *          To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/.
 */

var bkcore = bkcore || {};
bkcore.hexgl = bkcore.hexgl || {};

bkcore.hexgl.CameraChase = function(opts)
{
	this.dir = new THREE.Vector3(0,0,1);
	this.up = new THREE.Vector3(0,1,0);
	this.target = new THREE.Vector3();
	this.speedOffset = 0;
	this.speedOffsetMax = 10;
	this.speedOffsetStep = 0.05;

	this.modes = {
		CHASE: 0,
		ORBIT: 1
	}
	this.mode = this.modes.CHASE;

	this.camera = opts.camera;
	this.targetObject = opts.target;
	this.cameraCube = opts.cameraCube == undefined ? null : opts.cameraCube;

	this.yoffset = opts.yoffest == undefined ? 8.0 : opts.yoffest;
	this.zoffset = opts.zoffset == undefined ? 10.0 : opts.zoffset;
	this.viewOffset = opts.viewOffset == undefined ? 10.0 : opts.viewOffset;
	this.orbitOffset = 12;
	this.lerp = opts.lerp == undefined ? 0.5 : opts.lerp;
	this.time = 0.0;
}

bkcore.hexgl.CameraChase.prototype.update = function(dt, ratio)
{
	if(this.mode == this.modes.CHASE)
	{
		this.dir.set(0,0,1);
		this.up.set(0,1,0);

		this.targetObject.matrix.rotateAxis(this.up);
		this.targetObject.matrix.rotateAxis(this.dir);

		this.speedOffset += (this.speedOffsetMax*ratio - this.speedOffset) * Math.min(1, 0.3*dt);

		this.target.copy(this.targetObject.position);
		this.target.subSelf(this.dir.multiplyScalar(this.zoffset + this.speedOffset));
		this.target.addSelf(this.up.multiplyScalar(this.yoffset));
		this.target.y += -this.up.y + this.yoffset;
		this.camera.position.copy(this.target);

		this.camera.lookAt(this.dir.normalize().multiplyScalar(this.viewOffset).addSelf(this.targetObject.position));
	}
	else if(this.mode == this.modes.ORBIT)
	{
		this.time += dt*.008;
		this.dir.set(
			Math.cos(this.time)*this.orbitOffset,
			this.yoffset/2,
			Math.sin(this.time)*this.orbitOffset
		);
		this.target.copy(this.targetObject.position).addSelf(this.dir);
		this.camera.position.copy(this.target);
		this.camera.lookAt(this.targetObject.position);
	}

	else if (this.mode == this.modes.TOP){
        this.time += dt*.008;
        this.dir.set(
            -1000,
            5000,
            953
        );
        this.target.copy(this.targetObject.position).addSelf(this.dir);
        this.camera.position.copy(this.target);
        this.camera.lookAt(this.targetObject.position);
    }

    if(this.cameraCube != null)
        this.cameraCube.rotation.copy(this.camera.rotation);

}
<|endoftext|>


<commit_before>const compose = require('koa-compose');
const debug = require('debug')('oidc-provider:revocation');

const PARAM_LIST = new Set(['token', 'token_type_hint']);

const { InvalidRequestError } = require('../helpers/errors');
const presence = require('../helpers/validate_presence');
const instance = require('../helpers/weak_cache');
const authAndParams = require('../shared/chains/client_auth');

module.exports = function revocationAction(provider) {
  const { grantTypeHandlers } = instance(provider);

  function getAccessToken(token) {
    return provider.AccessToken.find(token);
  }

  async function getClientCredentials(token) {
    /* istanbul ignore if */
    if (!grantTypeHandlers.has('client_credentials')) return undefined;
    return provider.ClientCredentials.find(token);
  }

  async function getRefreshToken(token) {
    /* istanbul ignore if */
    if (!grantTypeHandlers.has('refresh_token')) return undefined;
    return provider.RefreshToken.find(token);
  }

  function findResult(results) {
    return results.find(found => !!found);
  }

  return compose([

    authAndParams(provider, PARAM_LIST, 'revocation'),

    async function validateTokenPresence(ctx, next) {
      presence(ctx, ['token']);
      await next();
    },

    async function renderTokenResponse(ctx, next) {
      ctx.status = 200;
      ctx.body = '';
      debug(
        'uuid=%s client=%s token=%s',
        ctx.oidc.uuid,
        ctx.oidc.client.clientId,
        ctx.oidc.params.token,
      );
      await next();
    },

    async function revokeToken(ctx, next) {
      let tryhard;
      const { params } = ctx.oidc;

      switch (params.token_type_hint) {
        case 'access_token':
          tryhard = getAccessToken(params.token)
            .then((result) => {
              if (result) return result;
              return Promise.all([
                getClientCredentials(params.token),
                getRefreshToken(params.token),
              ]).then(findResult);
            });
          break;
        case 'client_credentials':
          tryhard = getClientCredentials(params.token)
            .then((result) => {
              if (result) return result;
              return Promise.all([
                getAccessToken(params.token),
                getRefreshToken(params.token),
              ]).then(findResult);
            });
          break;
        case 'refresh_token':
          tryhard = getRefreshToken(params.token)
            .then((result) => {
              if (result) return result;
              return Promise.all([
                getAccessToken(params.token),
                getClientCredentials(params.token),
              ]).then(findResult);
            });
          break;
        default:
          tryhard = Promise.all([
            getAccessToken(params.token),
            getClientCredentials(params.token),
            getRefreshToken(params.token),
          ]).then(findResult);
      }

      let token;
      try {
        token = await tryhard;
      } catch (err) {
        if (err.message !== 'invalid_token') {
          throw err;
        }
      }

      if (!token) return;

      if (token.clientId !== ctx.oidc.client.clientId) {
        ctx.throw(new InvalidRequestError('this token does not belong to you'));
      }

      await token.destroy();

      await next();
    },
  ]);
};
<commit_msg>simplify revocation, theres no more invalid_token throw from basetoken.find()<commit_after>const compose = require('koa-compose');
const debug = require('debug')('oidc-provider:revocation');

const PARAM_LIST = new Set(['token', 'token_type_hint']);

const { InvalidRequestError } = require('../helpers/errors');
const presence = require('../helpers/validate_presence');
const instance = require('../helpers/weak_cache');
const authAndParams = require('../shared/chains/client_auth');

module.exports = function revocationAction(provider) {
  const { grantTypeHandlers } = instance(provider);

  function getAccessToken(token) {
    return provider.AccessToken.find(token);
  }

  async function getClientCredentials(token) {
    /* istanbul ignore if */
    if (!grantTypeHandlers.has('client_credentials')) return undefined;
    return provider.ClientCredentials.find(token);
  }

  async function getRefreshToken(token) {
    /* istanbul ignore if */
    if (!grantTypeHandlers.has('refresh_token')) return undefined;
    return provider.RefreshToken.find(token);
  }

  function findResult(results) {
    return results.find(found => !!found);
  }

  return compose([

    authAndParams(provider, PARAM_LIST, 'revocation'),

    async function validateTokenPresence(ctx, next) {
      presence(ctx, ['token']);
      await next();
    },

    async function renderTokenResponse(ctx, next) {
      ctx.status = 200;
      ctx.body = '';
      debug(
        'uuid=%s client=%s token=%s',
        ctx.oidc.uuid,
        ctx.oidc.client.clientId,
        ctx.oidc.params.token,
      );
      await next();
    },

    async function revokeToken(ctx, next) {
      let token;
      const { params } = ctx.oidc;

      switch (params.token_type_hint) {
        case 'access_token':
          token = await getAccessToken(params.token)
            .then((result) => {
              if (result) return result;
              return Promise.all([
                getClientCredentials(params.token),
                getRefreshToken(params.token),
              ]).then(findResult);
            });
          break;
        case 'client_credentials':
          token = await getClientCredentials(params.token)
            .then((result) => {
              if (result) return result;
              return Promise.all([
                getAccessToken(params.token),
                getRefreshToken(params.token),
              ]).then(findResult);
            });
          break;
        case 'refresh_token':
          token = await getRefreshToken(params.token)
            .then((result) => {
              if (result) return result;
              return Promise.all([
                getAccessToken(params.token),
                getClientCredentials(params.token),
              ]).then(findResult);
            });
          break;
        default:
          token = await Promise.all([
            getAccessToken(params.token),
            getClientCredentials(params.token),
            getRefreshToken(params.token),
          ]).then(findResult);
      }

      if (!token) return;

      if (token.clientId !== ctx.oidc.client.clientId) {
        ctx.throw(new InvalidRequestError('this token does not belong to you'));
      }

      await token.destroy();

      await next();
    },
  ]);
};
<|endoftext|>


<commit_before>const instance = require('../helpers/weak_cache');
const { json: parseBody } = require('../shared/selective_body');
const noCache = require('../shared/no_cache');
const paramsMiddleware = require('../shared/assemble_params');
const presence = require('../helpers/validate_presence');
const { InvalidRequest, InvalidClient } = require('../helpers/errors');

const PARAM_LIST = new Set(['client_id', 'origin']);

module.exports = {
  get: async function checkSessionIframe(ctx, next) {
    const removeHeaders = !instance(ctx.oidc.provider).configuration('features.sessionManagement.keepHeaders');
    if (removeHeaders) {
      ctx.response.remove('X-Frame-Options');
      const csp = ctx.response.get('Content-Security-Policy');
      if (csp.includes('frame-ancestors')) {
        ctx.response.set('Content-Security-Policy', csp.replace(/ ?frame-ancestors [^;]+;/, ''));
      }
    }

    ctx.type = 'html';
    ctx.body = `<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Session Management - OP iframe</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsSHA/2.3.1/sha256.js" integrity="sha256-NyuvLfsvfCfE+ceV6/W19H+qVp3M8c9FzAgj72CW39w=" crossorigin="anonymous"></script>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch&rum=0"></script>
</head>
<body>

<script type="application/javascript">
(function () {
var originCheckResult;

function shab64u(clientId, origin, state, salt) {
  var shasum = new jsSHA('SHA-256', 'TEXT');
  shasum.update(clientId)
  shasum.update(' ')
  shasum.update(origin)
  shasum.update(' ')
  shasum.update(state);

  if (salt) {
    shasum.update(' ');
    shasum.update(salt);
  }

  return shasum.getHash('B64').replace(/=/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');
}

function calculate(clientId, origin, actual, salt, cb) {
  try {
    if (originCheckResult.clientId !== clientId || originCheckResult.origin !== origin) {
      throw new Error('client_id and/or origin mismatch');
    }
    var opbs = getOPBrowserState(clientId);
    var stat = 'changed';

    if (opbs) {
      var expected = shab64u(clientId, origin, opbs, salt);

      if (actual === expected) {
        stat = 'unchanged';
      }

      cb(stat);
    } else if ('hasStorageAccess' in document) {
      document.hasStorageAccess().then(function (hasAccess) {
        if (!hasAccess) {
          cb('error');
        } else {
          cb(stat);
        }
      }, cb.bind(undefined, 'error'));
    } else {
      cb(stat);
    }
  } catch (err) {
    cb('error');
  }
}

function check(clientId, origin, actual, salt, cb) {
  if (!originCheckResult) {
    fetch(location.pathname, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json; charset=utf-8',
      },
      body: JSON.stringify({ client_id: clientId, origin: origin }),
      redirect: 'error',
    }).then(function (response) {
      if (response.ok) {
        originCheckResult = {
          origin: origin,
          clientId: clientId,
        };
        calculate(clientId, origin, actual, salt, cb);
      } else {
        throw new Error('invalid client_id and/or origin');
      }
    }).catch(cb.bind(undefined, 'error'));
  } else {
    calculate(clientId, origin, actual, salt, cb);
  }
}

function receiveMessage(e) {
  if (typeof e.data !== 'string') {
    return;
  }
  var parts = e.data.split(' ');
  var clientId = parts[0];
  var actual = parts[1];
  if (parts.length !== 2 || !clientId || !actual) {
    return;
  }
  var actualParts = actual.split('.');
  var sessionStr = actualParts[0];
  var salt = actualParts[1];
  if (!sessionStr || actualParts.length > 2) {
    return;
  }
  check(clientId, e.origin, sessionStr, salt, function (stat) {
    e.source.postMessage(stat, e.origin);
  });
}

function getOPBrowserState(clientId) {
  var cookie = readCookie('${ctx.oidc.provider.cookieName('state')}.' + clientId);
  if (cookie === null) {
    cookie = readCookie('${ctx.oidc.provider.cookieName('state')}.' + clientId + '.legacy');
  }
  return cookie;
}

function readCookie(name) {
  var nameEQ = name + '=';
  var ca = document.cookie.split(';');
  for (var i=0; i < ca.length; i++) {
    var c = ca[i];
    while (c.charAt(0) === ' ') c = c.substring(1, c.length);
    if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
  }
  return null;
}

window.addEventListener('message', receiveMessage, false);
})();
</script>

</body>
</html>`;
    await next();
  },
  post: [
    noCache,
    parseBody,
    paramsMiddleware.bind(undefined, PARAM_LIST),
    async function checkClientOrigin(ctx, next) {
      presence(ctx, 'origin', 'client_id');
      const { client_id: clientId, origin } = ctx.oidc.params;
      [clientId, origin].forEach((value) => {
        if (typeof value !== 'string') {
          throw new InvalidRequest('only string parameter values are expected');
        }
      });
      const client = await ctx.oidc.provider.Client.find(clientId);
      ctx.oidc.entity('Client', client);
      if (!client) {
        throw new InvalidClient();
      }
      if (!client.checkSessionOriginAllowed(origin)) {
        throw new InvalidRequest('origin not allowed', 403);
      }
      ctx.status = 204;
      await next();
    },
  ],
};
<commit_msg>chore: update check session fetch polyfill<commit_after>const instance = require('../helpers/weak_cache');
const { json: parseBody } = require('../shared/selective_body');
const noCache = require('../shared/no_cache');
const paramsMiddleware = require('../shared/assemble_params');
const presence = require('../helpers/validate_presence');
const { InvalidRequest, InvalidClient } = require('../helpers/errors');

const PARAM_LIST = new Set(['client_id', 'origin']);

module.exports = {
  get: async function checkSessionIframe(ctx, next) {
    const removeHeaders = !instance(ctx.oidc.provider).configuration('features.sessionManagement.keepHeaders');
    if (removeHeaders) {
      ctx.response.remove('X-Frame-Options');
      const csp = ctx.response.get('Content-Security-Policy');
      if (csp.includes('frame-ancestors')) {
        ctx.response.set('Content-Security-Policy', csp.replace(/ ?frame-ancestors [^;]+;/, ''));
      }
    }

    ctx.type = 'html';
    ctx.body = `<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Session Management - OP iframe</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsSHA/2.3.1/sha256.js" integrity="sha256-NyuvLfsvfCfE+ceV6/W19H+qVp3M8c9FzAgj72CW39w=" crossorigin="anonymous"></script>
<script src="https://polyfill.io/v3/polyfill.min.js?flags=gated&features=fetch"></script>
</head>
<body>

<script type="application/javascript">
(function () {
var originCheckResult;

function shab64u(clientId, origin, state, salt) {
  var shasum = new jsSHA('SHA-256', 'TEXT');
  shasum.update(clientId)
  shasum.update(' ')
  shasum.update(origin)
  shasum.update(' ')
  shasum.update(state);

  if (salt) {
    shasum.update(' ');
    shasum.update(salt);
  }

  return shasum.getHash('B64').replace(/=/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');
}

function calculate(clientId, origin, actual, salt, cb) {
  try {
    if (originCheckResult.clientId !== clientId || originCheckResult.origin !== origin) {
      throw new Error('client_id and/or origin mismatch');
    }
    var opbs = getOPBrowserState(clientId);
    var stat = 'changed';

    if (opbs) {
      var expected = shab64u(clientId, origin, opbs, salt);

      if (actual === expected) {
        stat = 'unchanged';
      }

      cb(stat);
    } else if ('hasStorageAccess' in document) {
      document.hasStorageAccess().then(function (hasAccess) {
        if (!hasAccess) {
          cb('error');
        } else {
          cb(stat);
        }
      }, cb.bind(undefined, 'error'));
    } else {
      cb(stat);
    }
  } catch (err) {
    cb('error');
  }
}

function check(clientId, origin, actual, salt, cb) {
  if (!originCheckResult) {
    fetch(location.pathname, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json; charset=utf-8',
      },
      body: JSON.stringify({ client_id: clientId, origin: origin }),
      redirect: 'error',
    }).then(function (response) {
      if (response.ok) {
        originCheckResult = {
          origin: origin,
          clientId: clientId,
        };
        calculate(clientId, origin, actual, salt, cb);
      } else {
        throw new Error('invalid client_id and/or origin');
      }
    }).catch(cb.bind(undefined, 'error'));
  } else {
    calculate(clientId, origin, actual, salt, cb);
  }
}

function receiveMessage(e) {
  if (typeof e.data !== 'string') {
    return;
  }
  var parts = e.data.split(' ');
  var clientId = parts[0];
  var actual = parts[1];
  if (parts.length !== 2 || !clientId || !actual) {
    return;
  }
  var actualParts = actual.split('.');
  var sessionStr = actualParts[0];
  var salt = actualParts[1];
  if (!sessionStr || actualParts.length > 2) {
    return;
  }
  check(clientId, e.origin, sessionStr, salt, function (stat) {
    e.source.postMessage(stat, e.origin);
  });
}

function getOPBrowserState(clientId) {
  var cookie = readCookie('${ctx.oidc.provider.cookieName('state')}.' + clientId);
  if (cookie === null) {
    cookie = readCookie('${ctx.oidc.provider.cookieName('state')}.' + clientId + '.legacy');
  }
  return cookie;
}

function readCookie(name) {
  var nameEQ = name + '=';
  var ca = document.cookie.split(';');
  for (var i=0; i < ca.length; i++) {
    var c = ca[i];
    while (c.charAt(0) === ' ') c = c.substring(1, c.length);
    if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
  }
  return null;
}

window.addEventListener('message', receiveMessage, false);
})();
</script>

</body>
</html>`;
    await next();
  },
  post: [
    noCache,
    parseBody,
    paramsMiddleware.bind(undefined, PARAM_LIST),
    async function checkClientOrigin(ctx, next) {
      presence(ctx, 'origin', 'client_id');
      const { client_id: clientId, origin } = ctx.oidc.params;
      [clientId, origin].forEach((value) => {
        if (typeof value !== 'string') {
          throw new InvalidRequest('only string parameter values are expected');
        }
      });
      const client = await ctx.oidc.provider.Client.find(clientId);
      ctx.oidc.entity('Client', client);
      if (!client) {
        throw new InvalidClient();
      }
      if (!client.checkSessionOriginAllowed(origin)) {
        throw new InvalidRequest('origin not allowed', 403);
      }
      ctx.status = 204;
      await next();
    },
  ],
};
<|endoftext|>


<commit_before>const express = require('express')
const http = require('http')
const apicache = require('apicache')
const settings = require(__dirname + '/settings.js');

let app = express()
let cache = apicache.middleware
let walkcache = []

app.use(express.static('site'));

app.get('/api', cache(settings.api_cache_msec), (req, res) => {
	console.log('API: new request')
	getData((data) => res.json(data));
})

app.listen(settings.listen_port, () => {
  console.log('Server up on port', settings.listen_port);
});

const getData = (cb) => {
	return http.get(settings.api_url, (response) => {
		let data = '';
		response.on('data', (chunk) => data += chunk);
		response.on('end', () => {
			const json = JSON.parse(data);
			flatten(json, cb);
		});
		response.on('error', (err) => console.log(err));
	}).on('error', (err) => console.log(err));
}

const getOSRM	= (coordinates) => {
	const findCoordinates = (element) => {
		return element.coordinates[0] === coordinates[0] &&
			element.coordinates[1] === coordinates[1];
	}

	if (walkcache.find(findCoordinates)) {
		console.log('OSRM: cached', coordinates)
		return walkcache.find(findCoordinates).duration
	}

	console.log('OSRM: new request for', coordinates)
	const url = settings.osrm_api_url +
		coordinates[0] + ',' +
		coordinates[1] + '?overview=false';

	let duration = 0;
	const request = http.get(url, (response) => {
		let data = '';
		response.on('data', (chunk) => data += chunk);
		response.on('end', () => {
			duration = JSON.parse(data).routes[0].duration;
			if (!walkcache.find(findCoordinates)) {
				walkcache.push({ coordinates: coordinates, duration: duration })
			}
		});
		response.on('error', (err) => console.log(err));
	}).on('error', (err) => console.log(err));

	return duration;
}

const flatten = (json, cb) => {
	let data = [];
	json.data.monitors.map((monitor, i) => {
		monitor.lines.map(line => {
			line.departures.departure.map(departure => {
				let walkDuration = getOSRM(monitor.locationStop.geometry.coordinates);
				let walkStatus = '';
				if (walkDuration * 0.7 > departure.departureTime.countdown * 60) {
					walkStatus = 'too late';
				} else if (walkDuration > departure.departureTime.countdown * 60) {
					walkStatus = 'hurry';
				} else if (walkDuration + 3 * 60 > departure.departureTime.countdown * 60) {
					walkStatus = 'soon';
				}
				data.push({
					'stop': monitor.locationStop.properties.title,
					'coordinates': monitor.locationStop.geometry.coordinates,
					'line': line.name,
					'towards': departure.vehicle ? departure.vehicle.towards : line.towards,
					'barrierFree': line.barrierFree,
					'timePlanned': departure.departureTime.timePlanned,
					'timeReal': departure.departureTime.timeReal ? departure.departureTime.timeReal : departure.departureTime.timePlanned,
					'countdown': departure.departureTime.countdown,
					'walkDuration': walkDuration,
					'walkStatus': walkStatus
				});
			})
		})
	})

	data.sort((a, b) => {
		return (a.timeReal < b.timeReal) ? -1 : ((a.timeReal > b.timeReal) ? 1 : 0);
	})
	cb(data);
}
<commit_msg>changed walkStatus definitions<commit_after>const express = require('express')
const http = require('http')
const apicache = require('apicache')
const settings = require(__dirname + '/settings.js');

let app = express()
let cache = apicache.middleware
let walkcache = []

app.use(express.static('site'));

app.get('/api', cache(settings.api_cache_msec), (req, res) => {
	console.log('API: new request')
	getData((data) => res.json(data));
})

app.listen(settings.listen_port, () => {
  console.log('Server up on port', settings.listen_port);
});

const getData = (cb) => {
	return http.get(settings.api_url, (response) => {
		let data = '';
		response.on('data', (chunk) => data += chunk);
		response.on('end', () => {
			const json = JSON.parse(data);
			flatten(json, cb);
		});
		response.on('error', (err) => console.log(err));
	}).on('error', (err) => console.log(err));
}

const getOSRM	= (coordinates) => {
	const findCoordinates = (element) => {
		return element.coordinates[0] === coordinates[0] &&
			element.coordinates[1] === coordinates[1];
	}

	if (walkcache.find(findCoordinates)) {
		console.log('OSRM: cached', coordinates)
		return walkcache.find(findCoordinates).duration
	}

	console.log('OSRM: new request for', coordinates)
	const url = settings.osrm_api_url +
		coordinates[0] + ',' +
		coordinates[1] + '?overview=false';

	let duration = 0;
	const request = http.get(url, (response) => {
		let data = '';
		response.on('data', (chunk) => data += chunk);
		response.on('end', () => {
			duration = JSON.parse(data).routes[0].duration;
			if (!walkcache.find(findCoordinates)) {
				walkcache.push({ coordinates: coordinates, duration: duration })
			}
		});
		response.on('error', (err) => console.log(err));
	}).on('error', (err) => console.log(err));

	return duration;
}

const flatten = (json, cb) => {
	let data = [];
	json.data.monitors.map((monitor, i) => {
		monitor.lines.map(line => {
			line.departures.departure.map(departure => {
				let walkDuration = getOSRM(monitor.locationStop.geometry.coordinates);
				let walkStatus = '';
				if (walkDuration * 0.9 > departure.departureTime.countdown * 60) {
					walkStatus = 'too late';
				} else if (walkDuration + 2 * 60 > departure.departureTime.countdown * 60) {
					walkStatus = 'hurry';
				} else if (walkDuration + 5 * 60 > departure.departureTime.countdown * 60) {
					walkStatus = 'soon';
				}
				data.push({
					'stop': monitor.locationStop.properties.title,
					'coordinates': monitor.locationStop.geometry.coordinates,
					'line': line.name,
					'towards': departure.vehicle ? departure.vehicle.towards : line.towards,
					'barrierFree': line.barrierFree,
					'timePlanned': departure.departureTime.timePlanned,
					'timeReal': departure.departureTime.timeReal ? departure.departureTime.timeReal : departure.departureTime.timePlanned,
					'countdown': departure.departureTime.countdown,
					'walkDuration': walkDuration,
					'walkStatus': walkStatus
				});
			})
		})
	})

	data.sort((a, b) => {
		return (a.timeReal < b.timeReal) ? -1 : ((a.timeReal > b.timeReal) ? 1 : 0);
	})
	cb(data);
}
<|endoftext|>


<commit_before>const http = require('http');

const PORT = 8080;

http.createServer()
  .on('request', (req, res) => {
    console.log(`Received request for ${req.url}`);
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.write('Hello World!\n');
    res.end(`Received request for ${req.url}`);
  })
  .on('listening', (error) => {
    if (error) {
      console.error(error);
    } else {
      console.log(`Server running at http://localhost:${PORT}/`);
    }
  })
  .listen(PORT);
<commit_msg>final, shortest  version<commit_after>const http = require('http');

const PORT = 8080;

http.createServer((req, res) => {
    console.log(`Received request for ${req.url}`);
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.write('Hello World!\n');
    res.end(`Received request for ${req.url}`);
  })
  .listen(PORT, () => {
    console.log(`Server running at http://localhost:${PORT}/`);
  });
<|endoftext|>


<commit_before>var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var host = process.env.IP;

io.on('connection', (socket) => {
  console.log('client connected');
});

http.listen(8081, function(){
  console.log(`listening on ${host}:8081`);
});
<commit_msg>Removes host as not all environments broadcast ip<commit_after>var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var host = process.env.IP;

io.on('connection', (socket) => {
  console.log('client connected');
});

http.listen(8081, function(){
  console.log(`listening on :8081`);
});
<|endoftext|>


<commit_before>/*

Jquery Ajax enabled Multiselect control

Written By: Alan Lai
Date:       8/25/2011

Parameters:

dataUrl:        url to a Json feed that returns objects {id = [id value], label = [label value]}
minLength:      minimum length before a search is executed
placeholder:    placeholder text for the search box
debug:          debug flag to provide more information
showOptions:    show the entire list when clicking into the text box

Callbacks:

onSelected:     event raised when a new item is selected (parameters: multiselect, id and label)
onRemoved:     event raised when a selected item is removed (parameters: multiselect, id and label)

*/

(function ($) {

    $.fn.extend({

        multiSelector: function (options) {

            var settings = $.extend({

                dataUrl: '',
                minLength: 2,
                placeholder: '',
                debug: false,
                showOptions: false,

                onSelected: undefined,
                onRemove: undefined

            }, options); // end of settings

            return this.each(function (index, item) {

                // get the main controls
                var $multiselect = $(this);
                $multiselect.hide();
                var $container = $("<div>").addClass("ac-container").addClass("ac-gradientbackground");
                $container.insertAfter($multiselect);

                // initialize all the necessary controls and containers
                init($multiselect, $container);
                bindEvents($multiselect, $container);

                preloadItems($multiselect, $container);

            });

            // initialize the controls
            function init($multiselect, $container) {

                // create the controls
                var $searchContainer = $("<div>").addClass("ac-searchContainer");
                var $searchBox = $("<input>").addClass("ac-searchBox").attr("placeholder", settings.placeholder);
                var $loadingIcon = $("<div>").html(" ").addClass("ac-loading").hide();
                var $selectedContainer = $("<div>").addClass("ac-selectedContainer");

                // append the controls
                $searchContainer.append($searchBox);
                $searchContainer.append($loadingIcon);
                $container.append($searchContainer);
                $container.append($selectedContainer);

                // initialize the search results box
                var $optionsBox = $("<div>").addClass("ac-optionsbox").hide();
                var $optionsList = $("<ul>").addClass("ac-optionslist").addClass("ui-menu ui-autocomplete ui-widget ui-widget-content");
                $optionsBox.append($optionsList);
                $optionsBox.insertAfter($searchBox);

                $container.append($("<div>").css("clear", "both"));

                if (settings.debug) initDebug($container);

                configureSearch($multiselect, $container);
            }

            function preloadItems($multiselect, $container) {

                var selected = $multiselect.find("option:selected");
                $.each(selected, function (index, item) {

                    var id = $(item).val();
                    var label = $(item).text();
                    onSelected($container, $multiselect, id, label);

                });

            }

            function bindEvents($multiselect, $container) {

                // detect click outside of our controls
                $('body').click(function (event) {

                    var $target = $(event.target);

                    if ($target.parents(".ac-optionsbox").length == 0 && !$target.hasClass("ac-searchBox")) {
                        $container.find(".ac-optionsbox").hide();
                    }

                });

                // capture down arrow from the textbox
                $container.find(".ac-searchBox").keydown(function (event) {

                    // get the options box next to the search box
                    var $optionBox = $(this).siblings(".ac-optionsbox");

                    // up/down and at least one item is eligible to be selected
                    if ((event.keyCode == 40 || event.keyCode == 38) && $optionBox.find("a").length > 0) {

                        var $selected = $optionBox.find(".ui-state-hover");
                        // any of them have a ui-state-hover
                        if ($selected.length > 0) {

                            // scroll up one
                            if (event.keyCode == 38) {

                                $selected.prev().addClass("ui-state-hover");
                                $selected.removeClass("ui-state-hover");

                            }

                            // scroll down one
                            if (event.keyCode == 40) {

                                $selected.next().addClass("ui-state-hover");
                                $selected.removeClass("ui-state-hover");

                            }

                        }
                        // nothing selected in the options box
                        else {
                            $optionBox.find("li").removeClass("ui-state-hover");

                            // set the first one to be highlighted
                            $optionBox.find("li:first").addClass("ui-state-hover");
                        }

                    }

                    // enter key
                    if (event.keyCode == 13) {

                        var $selected = $optionBox.find(".ui-state-hover a");
                        onSelected($container, $multiselect, $selected.data("id"), $selected.html());

                        $optionBox.hide();
                        $(this).val("");

                    }

                });

                $container.find(".ac-searchBox").keypress(function (event) { if (event.keyCode == 13) { return false; } });

            }

            function initDebug($container) {

                var $debugList = $("<ul>").addClass("ac-debug-list");

                $debugList.append($("<li>").addClass("ac-debug-item").html("Term Length:").append($("<span>").addClass("ac-debug-text")));

                var $debugContainer = $("<div>").addClass("ac-debug-container");
                $debugContainer.append($debugList);
                $container.append($debugContainer);

            }

            // =================================================
            // search events on the text box
            // =================================================
            function configureSearch($multiselect, $container) {

                var $searchBox = $container.find(".ac-searchBox");

                $searchBox.keyup(function (event) {

                    if (event.keyCode != 38 && event.keyCode != 40 && event.keyCode != 39 && event.keyCode != 41) {

                        var searchTerm = $searchBox.val();

                        if (settings.debug) { $container.find(".ac-debug-termLength").html('[' + searchTerm + ']' + searchTerm.length); }

                        if (searchTerm.length >= settings.minLength) {

                            onSearch(searchTerm, $multiselect, $container);

                        }
                    }

                });

                if (settings.showOptions) {
                    // show all
                    $searchBox.click(function () {

                        var results = $multiselect.find("option:not(:selected)");
                        var available = $.map(results, function (item, index) {

                            return { id: $(item).val(), label: $(item).text() };

                        });

                        displaySearchResults(available, $container, $multiselect);

                    });
                }

            }
            // =================================================

            // event when a search is being executed
            function onSearch(searchTerm, $multiselect, $container) {

                // execute remote search
                if (settings.dataUrl != '') {
                    remoteSearch(searchTerm, $multiselect, $container);
                }
                // execute a local search
                else {
                    localSearch(searchTerm, $multiselect, $container);
                }

            }

            // execute a search against the remote datasource
            function remoteSearch(searchTerm, $multiselect, $container) {

                $.getJSON(settings.dataUrl, { searchTerm: searchTerm }, function (results) {

                    displaySearchResults(results, $container, $multiselect);

                });

            }

            // execute a search against the multiselect values
            function localSearch(searchTerm, $multiselect, $container) {

                // find the items in the multiselect
                var results = $multiselect.find("option").filter(function () {

                    var id = $(this).val().toLowerCase();
                    var txt = $(this).text().toLowerCase();

                    return (id.indexOf(searchTerm) >= 0 || txt.indexOf(searchTerm) >= 0);

                });

                var available = $.map(results, function (item, index) {

                    return { Id: $(item).val(), Label: $(item).text() };

                });

                displaySearchResults(available, $container, $multiselect);
            }

            function displaySearchResults(results, $container, $multiselect) {

                // check if the result box is visible (show if not)
                var $optionsBox = $container.find(".ac-optionsbox");
                if (!$optionsBox.is(":visible")) { $optionsBox.show(); }

                var $optionsList = $optionsBox.find(".ac-optionslist");
                $optionsList.empty();

                $.each(results, function (index, item) {

                    var link;
                    if (item.id == undefined) {
                        link = $("<a>").data("id", item.Id).html(item.Label).addClass("ac-option");
                    }
                    if (item.Id == undefined) {
                        link = $("<a>").data("id", item.id).html(item.label).addClass("ac-option");
                    }

                    link.click(function () {

                        var id = $(this).data("id");
                        var label = $(this).html();

                        onSelected($container, $multiselect, id, label);
                        $container.find(".ac-optionsbox").hide();

                    });

                    var li = $("<li>").addClass("ui-menu-item");
                    li.hover(
                    // mouse over
                        function () {
                            $(this).siblings("li.ui-menu-item").removeClass("ui-state-hover");
                            $(this).addClass("ui-state-hover");
                        },
                    // mouse out
                        function () {
                            $(this).removeClass("ui-state-hover");
                        });
                    li.append(link);

                    $optionsList.append(li);

                });

                if ($optionsList.children().length == 0) {
                    $optionsList.append($("<li>").html("No results found."));
                }

            }

            // event when an object is selected to the list
            function onSelected($container, $multiselect, id, label) {

                $container = $container.find(".ac-selectedContainer");

                // create the objects
                var $selected = $("<span>").addClass("ac-selected").data("id", id).addClass("ac-gradientbackground");
                $selected.append($("<span>").html(label).css("display", "inline-block"));
                $selected.append($("<span>").addClass("ui-icon ui-icon-circle-close").css("margin", "-3px 4px").css("display", "inline-block"));
                $selected.data("id", id);

                // check if the option exists yet
                var $selectedOption = $multiselect.find('option[value="' + id + '"]');

                if ($selectedOption.length < 1) {
                    // add the value into the multiselect control
                    $multiselect.append($("<option>").val(id).html(label).attr("selected", ""));
                }
                else {
                    $selectedOption.attr("selected", "");
                }

                var $selectedDisplayObj = $container.children().filter(function (index) { return $(this).data("id") == id; });

                // do not add the object if it already exists
                if ($selectedDisplayObj.length < 1) {
                    // add to the selected container
                    $container.append($selected);

                    // attach a click event
                    $selected.click(function () {

                        // remove the object from the multiselect
                        $multiselect.find('option[value="' + id + '"]').removeAttr("selected");

                        // remove from the container
                        $(this).remove();

                        if (settings.onRemoved != undefined) {
                            settings.onRemoved($multiselect, id, label);
                        }

                    });
                }

                if (settings.onSelected != undefined) {
                    settings.onSelected($multiselect, id, label);
                }

            }

        } // end of multiSelector

    }); // end of fn.extend

})(jQuery);<commit_msg>fixed defect that was not blanking the search box after selecting a value<commit_after>/*

Jquery Ajax enabled Multiselect control

Written By: Alan Lai
Date:       8/25/2011

Parameters:

dataUrl:        url to a Json feed that returns objects {id = [id value], label = [label value]}
minLength:      minimum length before a search is executed
placeholder:    placeholder text for the search box
debug:          debug flag to provide more information
showOptions:    show the entire list when clicking into the text box

Callbacks:

onSelected:     event raised when a new item is selected (parameters: multiselect, id and label)
onRemoved:     event raised when a selected item is removed (parameters: multiselect, id and label)

*/

(function ($) {

    $.fn.extend({

        multiSelector: function (options) {

            var settings = $.extend({

                dataUrl: '',
                minLength: 2,
                placeholder: '',
                debug: false,
                showOptions: false,

                onSelected: undefined,
                onRemove: undefined

            }, options); // end of settings

            return this.each(function (index, item) {

                // get the main controls
                var $multiselect = $(this);
                $multiselect.hide();
                var $container = $("<div>").addClass("ac-container").addClass("ac-gradientbackground");
                $container.insertAfter($multiselect);

                // initialize all the necessary controls and containers
                init($multiselect, $container);
                bindEvents($multiselect, $container);

                preloadItems($multiselect, $container);

            });

            // initialize the controls
            function init($multiselect, $container) {

                // create the controls
                var $searchContainer = $("<div>").addClass("ac-searchContainer");
                var $searchBox = $("<input>").addClass("ac-searchBox").attr("placeholder", settings.placeholder);
                var $loadingIcon = $("<div>").html(" ").addClass("ac-loading").hide();
                var $selectedContainer = $("<div>").addClass("ac-selectedContainer");

                // append the controls
                $searchContainer.append($searchBox);
                $searchContainer.append($loadingIcon);
                $container.append($searchContainer);
                $container.append($selectedContainer);

                // initialize the search results box
                var $optionsBox = $("<div>").addClass("ac-optionsbox").hide();
                var $optionsList = $("<ul>").addClass("ac-optionslist").addClass("ui-menu ui-autocomplete ui-widget ui-widget-content");
                $optionsBox.append($optionsList);
                $optionsBox.insertAfter($searchBox);

                $container.append($("<div>").css("clear", "both"));

                if (settings.debug) initDebug($container);

                configureSearch($multiselect, $container);
            }

            function preloadItems($multiselect, $container) {

                var selected = $multiselect.find("option:selected");
                $.each(selected, function (index, item) {

                    var id = $(item).val();
                    var label = $(item).text();
                    onSelected($container, $multiselect, id, label);

                });

            }

            function bindEvents($multiselect, $container) {

                // detect click outside of our controls
                $('body').click(function (event) {

                    var $target = $(event.target);

                    if ($target.parents(".ac-optionsbox").length == 0 && !$target.hasClass("ac-searchBox")) {
                        $container.find(".ac-optionsbox").hide();
                    }

                });

                // capture down arrow from the textbox
                $container.find(".ac-searchBox").keydown(function (event) {

                    // get the options box next to the search box
                    var $optionBox = $(this).siblings(".ac-optionsbox");

                    // up/down and at least one item is eligible to be selected
                    if ((event.keyCode == 40 || event.keyCode == 38) && $optionBox.find("a").length > 0) {

                        var $selected = $optionBox.find(".ui-state-hover");
                        // any of them have a ui-state-hover
                        if ($selected.length > 0) {

                            // scroll up one
                            if (event.keyCode == 38) {

                                $selected.prev().addClass("ui-state-hover");
                                $selected.removeClass("ui-state-hover");

                            }

                            // scroll down one
                            if (event.keyCode == 40) {

                                $selected.next().addClass("ui-state-hover");
                                $selected.removeClass("ui-state-hover");

                            }

                        }
                        // nothing selected in the options box
                        else {
                            $optionBox.find("li").removeClass("ui-state-hover");

                            // set the first one to be highlighted
                            $optionBox.find("li:first").addClass("ui-state-hover");
                        }

                    }

                    // enter key
                    if (event.keyCode == 13) {

                        var $selected = $optionBox.find(".ui-state-hover a");
                        onSelected($container, $multiselect, $selected.data("id"), $selected.html());

                        $optionBox.hide();
                        $(this).val("");

                    }

                });

                $container.find(".ac-searchBox").keypress(function (event) { if (event.keyCode == 13) { return false; } });

            }

            function initDebug($container) {

                var $debugList = $("<ul>").addClass("ac-debug-list");

                $debugList.append($("<li>").addClass("ac-debug-item").html("Term Length:").append($("<span>").addClass("ac-debug-text")));

                var $debugContainer = $("<div>").addClass("ac-debug-container");
                $debugContainer.append($debugList);
                $container.append($debugContainer);

            }

            // =================================================
            // search events on the text box
            // =================================================
            function configureSearch($multiselect, $container) {

                var $searchBox = $container.find(".ac-searchBox");

                $searchBox.keyup(function (event) {

                    if (event.keyCode != 38 && event.keyCode != 40 && event.keyCode != 39 && event.keyCode != 41) {

                        var searchTerm = $searchBox.val();

                        if (settings.debug) { $container.find(".ac-debug-termLength").html('[' + searchTerm + ']' + searchTerm.length); }

                        if (searchTerm.length >= settings.minLength) {

                            onSearch(searchTerm, $multiselect, $container);

                        }
                    }

                });

                if (settings.showOptions) {
                    // show all
                    $searchBox.click(function () {

                        var results = $multiselect.find("option:not(:selected)");
                        var available = $.map(results, function (item, index) {

                            return { id: $(item).val(), label: $(item).text() };

                        });

                        displaySearchResults(available, $container, $multiselect);

                    });
                }

            }
            // =================================================

            // event when a search is being executed
            function onSearch(searchTerm, $multiselect, $container) {

                // execute remote search
                if (settings.dataUrl != '') {
                    remoteSearch(searchTerm, $multiselect, $container);
                }
                // execute a local search
                else {
                    localSearch(searchTerm, $multiselect, $container);
                }

            }

            // execute a search against the remote datasource
            function remoteSearch(searchTerm, $multiselect, $container) {

                $.getJSON(settings.dataUrl, { searchTerm: searchTerm }, function (results) {

                    displaySearchResults(results, $container, $multiselect);

                });

            }

            // execute a search against the multiselect values
            function localSearch(searchTerm, $multiselect, $container) {

                // find the items in the multiselect
                var results = $multiselect.find("option").filter(function () {

                    var id = $(this).val().toLowerCase();
                    var txt = $(this).text().toLowerCase();

                    return (id.indexOf(searchTerm) >= 0 || txt.indexOf(searchTerm) >= 0);

                });

                var available = $.map(results, function (item, index) {

                    return { Id: $(item).val(), Label: $(item).text() };

                });

                displaySearchResults(available, $container, $multiselect);
            }

            function displaySearchResults(results, $container, $multiselect) {

                // check if the result box is visible (show if not)
                var $optionsBox = $container.find(".ac-optionsbox");
                if (!$optionsBox.is(":visible")) { $optionsBox.show(); }

                var $optionsList = $optionsBox.find(".ac-optionslist");
                $optionsList.empty();

                $.each(results, function (index, item) {

                    var link;
                    if (item.id == undefined) {
                        link = $("<a>").data("id", item.Id).html(item.Label).addClass("ac-option");
                    }
                    if (item.Id == undefined) {
                        link = $("<a>").data("id", item.id).html(item.label).addClass("ac-option");
                    }

                    link.click(function () {

                        var id = $(this).data("id");
                        var label = $(this).html();

                        onSelected($container, $multiselect, id, label);
                        $container.find(".ac-optionsbox").hide();

                    });

                    var li = $("<li>").addClass("ui-menu-item");
                    li.hover(
                    // mouse over
                        function () {
                            $(this).siblings("li.ui-menu-item").removeClass("ui-state-hover");
                            $(this).addClass("ui-state-hover");
                        },
                    // mouse out
                        function () {
                            $(this).removeClass("ui-state-hover");
                        });
                    li.append(link);

                    $optionsList.append(li);

                });

                if ($optionsList.children().length == 0) {
                    $optionsList.append($("<li>").html("No results found."));
                }

            }

            // event when an object is selected to the list
            function onSelected($container, $multiselect, id, label) {

                $container = $container.find(".ac-selectedContainer");

                // create the objects
                var $selected = $("<span>").addClass("ac-selected").data("id", id).addClass("ac-gradientbackground");
                $selected.append($("<span>").html(label).css("display", "inline-block"));
                $selected.append($("<span>").addClass("ui-icon ui-icon-circle-close").css("margin", "-3px 4px").css("display", "inline-block"));
                $selected.data("id", id);

                // check if the option exists yet
                var $selectedOption = $multiselect.find('option[value="' + id + '"]');

                if ($selectedOption.length < 1) {
                    // add the value into the multiselect control
                    $multiselect.append($("<option>").val(id).html(label).attr("selected", ""));
                }
                else {
                    $selectedOption.attr("selected", "");
                }

                var $selectedDisplayObj = $container.children().filter(function (index) { return $(this).data("id") == id; });

                // do not add the object if it already exists
                if ($selectedDisplayObj.length < 1) {
                    // add to the selected container
                    $container.append($selected);

                    // blank the search box
                    $container.parents(".ac-container").find(".ac-searchBox").val("");

                    // attach a click event
                    $selected.click(function () {

                        // remove the object from the multiselect
                        $multiselect.find('option[value="' + id + '"]').removeAttr("selected");

                        // remove from the container
                        $(this).remove();

                        if (settings.onRemoved != undefined) {
                            settings.onRemoved($multiselect, id, label);
                        }

                    });
                }

                if (settings.onSelected != undefined) {
                    settings.onSelected($multiselect, id, label);
                }

            }

        } // end of multiSelector

    }); // end of fn.extend

})(jQuery);<|endoftext|>


<commit_before>
module.exports = groups;
groups.list = list;

groups.usage = "\nfhc storeitemgroups list"
              +"\nfhc storeitemgroups create <group-name> <group-description>"
              +"\nfhc storeitemgroups delete <group-guid>"
              +"\nfhc storeitemgroups read <group-guid>"
              +"\nfhc storeitemgroups update <group-guid> <group-name> <group-description>"
              +"\nfhc storeitemgroups addusers <group-guid> <user-email>*"
              +"\nfhc storeitemgroups removeusers <group-guid> <user-email>*"
              +"\nfhc storeitemgroups addstoreitems <group-guid> <storeitem-id>*"
              +"\nfhc storeitemgroups removestoreitems <group-guid> <storeitem-id>*"

var log = require("./utils/log");
var fhc = require("./fhc");
var fhreq = require("./utils/request");
var common = require("./common");
var util = require('util');
var async = require('async');
var path = require('path');
var ini = require('./utils/ini');
var Table = require('cli-table');

// Main groups entry point
function groups (args, cb) {
  if (args.length == 0){
    return list(cb);
  }

  var action = args[0];
  if (action == 'list') {
    return list(cb);
  }else if (action === 'create'){
    if (args.length !== 3) return cb("Invalid arguments for 'create':" + groups.usage);
    return create(args[1], args[2], cb);
  }else if (action === 'read'){
    if (args.length !== 2) return cb("Invalid arguments for 'read':" + groups.usage);
    return read(args[1], cb);
  }else if (action === 'update'){
    if (args.length !== 4) return cb("Invalid arguments for 'update':" + groups.usage);
    return update(args[1], args[2], args[3], cb);
  }else if (action === 'delete'){
    if (args.length !== 2) return cb("Invalid arguments for 'delete':" + groups.usage);
    return deleteGroup(args[1], cb);
  }else if (action === 'addusers'){
    if (args.length <= 2) return cb("Invalid arguments for 'addusers':" + groups.usage);
    var act = args.shift();
    var id = args.shift();
    return addusers(id, args, cb);
  }else if (action === 'removeusers'){
    if (args.length <= 2) return cb("Invalid arguments for 'removeusers':" + groups.usage);
    var act = args.shift();
    var id = args.shift();
    return removeusers(id, args, cb);
  }else if (action === 'addstoreitems'){
    if (args.length <= 2) return cb("Invalid arguments for 'addstoreitems':" + groups.usage);
    var act = args.shift();
    var id = args.shift();
    return addstoreitems(id, args, cb);
  }else if (action === 'removestoreitems'){
    if (args.length <= 2) return cb("Invalid arguments for 'removestoreitems':" + groups.usage);
    var act = args.shift();
    var id = args.shift();
    return removestoreitems(id, args, cb);
  }else if (args.length == 1){
    var appId = fhc.appId(args[0]);
    return read(appId, cb);
  }else{
    return cb("Unknown command '" + action + "'. Usage: " + groups.usage);
  }
}

// list groups
function list(cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/list", {}, "Error listing groups: ", function(err, groups){
    if(err) return cb(err);
    return cb(undefined, groups);
  });
}

// create user
function create(name, desc, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/create", {"name": name, "description": desc}, "Error creating group: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// delete group
function deleteGroup(id, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/delete", {"guid": id}, "Error deleting group: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// read group
function read(id, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/read", {"guid": id}, "Error reading group: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// update group
function update(id, name, desc, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/update", {"guid": id, "description": desc, "name": name}, "Error updating group: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// add users to a group
function addusers(id, users, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/addusers", {"guid": id,  "users": users}, "Error adding users: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// remove users from a group
function removeusers(id, users, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/removeusers", {"guid": id,  "users": users}, "Error removing users: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// add store items to a group
function addstoreitems(id, storeitems, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/addstoreitems", {"guid": id, "storeitems": storeitems}, "Error adding apps: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// remove store items from a group
function removestoreitems(id, storeitems, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/removestoreitems", {"guid": id,  "storeitems": storeitems}, "Error removing apps: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// bash completion
groups.completion = function (opts, cb) {
  var argv = opts.conf.argv.remain;
  if (argv[1] !== "admin-storeitemgroups") argv.unshift("admin-storeitemgroups");
  if (argv.length === 2) {
    var cmds = ["list", "read", "create", "update", "delete", "addapps", "removeapps", "addusers", "removeusers"];
    if (opts.partialWord !== "l") cmds.push("list");
    return cb(undefined, cmds);
  }
  var action = argv[2];
  switch (action) {
    case "addapps":
      common.getAppIds(cb);
      break;
    default: return cb(null, []);
  }
};
<commit_msg>add function to storeitemgroup manager to list group users<commit_after>
module.exports = groups;
groups.list = list;

groups.usage = "\nfhc storeitemgroups list"
              +"\nfhc storeitemgroups create <group-name> <group-description>"
              +"\nfhc storeitemgroups delete <group-guid>"
              +"\nfhc storeitemgroups read <group-guid>"
              +"\nfhc storeitemgroups readByName <group-name>"
              +"\nfhc storeitemgroups update <group-guid> <group-name> <group-description>"
              +"\nfhc storeitemgroups addusers <group-guid> <user-email>*"
              +"\nfhc storeitemgroups removeusers <group-guid> <user-email>*"
              +"\nfhc storeitemgroups addstoreitems <group-guid> <storeitem-id>*"
              +"\nfhc storeitemgroups removestoreitems <group-guid> <storeitem-id>*"

var log = require("./utils/log");
var fhc = require("./fhc");
var fhreq = require("./utils/request");
var common = require("./common");
var util = require('util');
var async = require('async');
var path = require('path');
var ini = require('./utils/ini');
var Table = require('cli-table');

// Main groups entry point
function groups (args, cb) {
  if (args.length == 0){
    return list(cb);
  }

  var action = args[0];
  if (action == 'list') {
    return list(cb);
  }else if (action === 'create'){
    if (args.length !== 3) return cb("Invalid arguments for 'create':" + groups.usage);
    return create(args[1], args[2], cb);
  }else if (action === 'read'){
    if (args.length !== 2) return cb("Invalid arguments for 'read':" + groups.usage);
    return read(args[1], cb);
  }else if (action === 'readByName'){
    if (args.length !== 2) return cb("Invalid arguments for 'readByName':" + groups.usage);
    return readByName(args[1], cb);
  }else if (action === 'update'){
    if (args.length !== 4) return cb("Invalid arguments for 'update':" + groups.usage);
    return update(args[1], args[2], args[3], cb);
  }else if (action === 'delete'){
    if (args.length !== 2) return cb("Invalid arguments for 'delete':" + groups.usage);
    return deleteGroup(args[1], cb);
  }else if (action === 'addusers'){
    if (args.length <= 2) return cb("Invalid arguments for 'addusers':" + groups.usage);
    var act = args.shift();
    var id = args.shift();
    return addusers(id, args, cb);
  }else if (action === 'removeusers'){
    if (args.length <= 2) return cb("Invalid arguments for 'removeusers':" + groups.usage);
    var act = args.shift();
    var id = args.shift();
    return removeusers(id, args, cb);
  }else if (action === 'addstoreitems'){
    if (args.length <= 2) return cb("Invalid arguments for 'addstoreitems':" + groups.usage);
    var act = args.shift();
    var id = args.shift();
    return addstoreitems(id, args, cb);
  }else if (action === 'removestoreitems'){
    if (args.length <= 2) return cb("Invalid arguments for 'removestoreitems':" + groups.usage);
    var act = args.shift();
    var id = args.shift();
    return removestoreitems(id, args, cb);
  }else if (args.length == 1){
    var appId = fhc.appId(args[0]);
    return read(appId, cb);
  }else{
    return cb("Unknown command '" + action + "'. Usage: " + groups.usage);
  }
}

// list groups
function list(cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/list", {}, "Error listing groups: ", function(err, groups){
    if(err) return cb(err);
    return cb(undefined, groups);
  });
}

// create user
function create(name, desc, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/create", {"name": name, "description": desc}, "Error creating group: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// delete group
function deleteGroup(id, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/delete", {"guid": id}, "Error deleting group: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// read group
function read(id, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/read", {"guid": id}, "Error reading group: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

function readByName(name, cb) {
  list(function(err, groups){
    if(err) return cb(err);
    for(var i=0;i<groups.list.length;i++){
      var group = groups.list[i];
      if(group.name === name){
          common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/read", {"guid": group.guid}, "Error reading group: ", function(err, data){
          if(err) return cb(err);
          var storeitems = data.storeitems;
          var users = data.users;
          var retStoreItems = [];
          var retUsers = [];
          async.parallel([
            function(callback){
              common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitem/list", {}, "Error listing storeitems: ", function(sierr, sidata){
                if(sierr) return callback(sierr);
                sidata.list.forEach(function(item){
                  if(storeitems.indexOf(item.guid) > -1){
                    retStoreItems.push(item.name);
                  }
                });
                callback(undefined, 0);
              })
            },
            function(callback){
              common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/user/list", {}, "Error listing users: ", function(uerr, udata){
                if(uerr) return callback(uerr);
                udata.list.forEach(function(item){
                  if(users.indexOf(item.guid) > -1){
                    retUsers.push({email: item.fields.email, name:item.fields.name});
                  }
                });
                callback(undefined, 0);
              })
            }
          ], function(err, results){
            if(err) return cb(err);
            data.storeitems = retStoreItems;
            data.users = retUsers;
            return cb(undefined, data);
          });
        });
      } else {
        if(i == groups.list.length - 1){
          return cb("Can not find group with name " + name);
        } else {
          continue;
        }
      }
    }
  })
}

// update group
function update(id, name, desc, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/update", {"guid": id, "description": desc, "name": name}, "Error updating group: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// add users to a group
function addusers(id, users, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/addusers", {"guid": id,  "users": users}, "Error adding users: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// remove users from a group
function removeusers(id, users, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/removeusers", {"guid": id,  "users": users}, "Error removing users: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// add store items to a group
function addstoreitems(id, storeitems, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/addstoreitems", {"guid": id, "storeitems": storeitems}, "Error adding apps: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// remove store items from a group
function removestoreitems(id, storeitems, cb) {
  common.doApiCall(fhreq.getFeedHenryUrl(), "/box/srv/1.1/admin/storeitemgroup/removestoreitems", {"guid": id,  "storeitems": storeitems}, "Error removing apps: ", function(err, data){
    if(err) return cb(err);
    return cb(undefined, data);
  });
}

// bash completion
groups.completion = function (opts, cb) {
  var argv = opts.conf.argv.remain;
  if (argv[1] !== "admin-storeitemgroups") argv.unshift("admin-storeitemgroups");
  if (argv.length === 2) {
    var cmds = ["list", "read", "create", "update", "delete", "addapps", "removeapps", "addusers", "removeusers"];
    if (opts.partialWord !== "l") cmds.push("list");
    return cb(undefined, cmds);
  }
  var action = argv[2];
  switch (action) {
    case "addapps":
      common.getAppIds(cb);
      break;
    default: return cb(null, []);
  }
};
<|endoftext|>