<commit_before>'use strict';

var test = require('tape');
var request = require('request');
var fs = require('fs');
var path = require('path');
var getOptions = require('./tools/get-options');
var gm = require('gm');
var statusMsg485 = require('../server_app/config/statusmsgs')['485'];

if (!fs.existsSync('./test-images-diffs')) fs.mkdirSync('./test-images-diffs');
if (!fs.existsSync('./test-images')) fs.mkdirSync('./test-images');

var userFileName = process.argv[2];

if (!userFileName) runAll();
else runSingle(userFileName);

function runAll () {
    test('testing mocks', function (t) {

        console.error('### beginning pixel comparison tests ###');
        var files = fs.readdirSync('./mocks');

        t.plan(files.length - 1); // -1 is for font-wishlist...
        for (var i = 0; i < files.length; i ++) {
            testMock(files[i], t);
        }

    });
}

function runSingle (userFileName) {
    test('testing single mock: ' + userFileName, function (t) {
        t.plan(1);
        testMock(userFileName, t);
    });
}

function testMock (fileName, t) {
    if (path.extname(fileName) !== '.json') return;
    if (fileName === 'font-wishlist.json' && !userFileName) return;

    var figure = require('./mocks/' + fileName);
    var bodyMock = {
        figure: figure,
        format: 'png',
        scale: 1
    };

    var imageFileName = fileName.split('.')[0] + '.png';
    var savedImagePath = 'test-images/' + imageFileName;
    var diffPath = 'test-images-diffs/' + 'diff-' + imageFileName;
    var savedImageStream = fs.createWriteStream(savedImagePath);
    var options = getOptions(bodyMock, 'http://localhost:9010/');
    var statusCode;

    function checkImage () {
        var options = {
            file: diffPath,
            highlightColor: 'purple',
            tolerance: 0.0
        };

        if(statusCode === 485) {
            console.error(imageFileName, '-', statusMsg485, '- skip');
        }
        else {
            gm.compare(savedImagePath, 'test-images-baseline/' + imageFileName, options, onEqualityCheck);
        }
    }

    function onEqualityCheck (err, isEqual) {
        if (err) return console.error(err, imageFileName);
        if (isEqual) {
            fs.unlinkSync(diffPath);
            console.error(imageFileName + ' is pixel perfect');
        }

        t.ok(isEqual, savedImagePath + ' should be pixel perfect');
    }

    request(options)
        .on('response', function(response) {
            statusCode = response.statusCode;
        })
        .pipe(savedImageStream)
        .on('close', checkImage);
}
<commit_msg>make px compare test output a diff blank file when no image is generated:<commit_after>'use strict';

var test = require('tape');
var request = require('request');
var fs = require('fs');
var path = require('path');
var getOptions = require('./tools/get-options');
var gm = require('gm');
var statusMsg485 = require('../server_app/config/statusmsgs')['485'];

if (!fs.existsSync('./test-images-diffs')) fs.mkdirSync('./test-images-diffs');
if (!fs.existsSync('./test-images')) fs.mkdirSync('./test-images');

var userFileName = process.argv[2];

var touch = function(fileName) {
    fs.closeSync(fs.openSync(fileName, 'w'));
};

if (!userFileName) runAll();
else runSingle(userFileName);

function runAll () {
    test('testing mocks', function (t) {

        console.error('### beginning pixel comparison tests ###');
        var files = fs.readdirSync('./mocks');

        t.plan(files.length - 1); // -1 is for font-wishlist...
        for (var i = 0; i < files.length; i ++) {
            testMock(files[i], t);
        }

    });
}

function runSingle (userFileName) {
    test('testing single mock: ' + userFileName, function (t) {
        t.plan(1);
        testMock(userFileName, t);
    });
}

function testMock (fileName, t) {
    if (path.extname(fileName) !== '.json') return;
    if (fileName === 'font-wishlist.json' && !userFileName) return;

    var figure = require('./mocks/' + fileName);
    var bodyMock = {
        figure: figure,
        format: 'png',
        scale: 1
    };

    var imageFileName = fileName.split('.')[0] + '.png';
    var savedImagePath = 'test-images/' + imageFileName;
    var diffPath = 'test-images-diffs/' + 'diff-' + imageFileName;
    var savedImageStream = fs.createWriteStream(savedImagePath);
    var options = getOptions(bodyMock, 'http://localhost:9010/');
    var statusCode;

    function checkImage () {
        var options = {
            file: diffPath,
            highlightColor: 'purple',
            tolerance: 0.0
        };

        if(statusCode === 485) {
            console.error(imageFileName, '-', statusMsg485, '- skip');
        }
        else {
            gm.compare(savedImagePath, 'test-images-baseline/' + imageFileName, options, onEqualityCheck);
        }
    }

    function onEqualityCheck (err, isEqual) {
        if (err) {
            touch(diffPath);
            return console.error(err, imageFileName);
        }
        if (isEqual) {
            fs.unlinkSync(diffPath);
            console.error(imageFileName + ' is pixel perfect');
        }

        t.ok(isEqual, savedImagePath + ' should be pixel perfect');
    }

    request(options)
        .on('response', function(response) {
            statusCode = response.statusCode;
        })
        .pipe(savedImageStream)
        .on('close', checkImage);
}
<|endoftext|>


<commit_before>/*!
 * VisualEditor ContentEditable MWMapsNode class.
 *
 * @copyright 2011-2015 VisualEditor Team and others; see http://ve.mit-license.org
 */
/* globals require */
var kartoLive = require( 'ext.kartographer.live' ),
	kartoEditing = require( 'ext.kartographer.editing' );

/**
 * ContentEditable paragraph node.
 *
 * @class
 * @extends ve.ce.MWBlockExtensionNode
 * @mixins ve.ce.ResizableNode
 *
 * @constructor
 * @param {ve.dm.MWMapsNode} model Model to observe
 * @param {Object} [config] Configuration options
 */
ve.ce.MWMapsNode = function VeCeMWMaps( model, config ) {
	config = config || {};

	// Parent constructor
	ve.ce.MWMapsNode.super.apply( this, arguments );

	// Mixin constructors
	ve.ce.ResizableNode.call( this, this.$element, config );

	this.$imageLoader = null;
	this.geoJson = null;

	this.updateGeoJson = $.debounce( 300, $.proxy( this.updateGeoJson, this ) );

	// Events
	this.model.connect( this, { attributeChange: 'onAttributeChange' } );
	this.connect( this, { focus: 'onMapFocus' } );

	// Ensure we have the styles to render the map node
	mw.loader.using( 'ext.kartographer' );

	// DOM changes
	this.$element
		.empty()
		.addClass( 've-ce-mwMapsNode' )
		.css( this.model.getCurrentDimensions() );
};

/* Inheritance */

OO.inheritClass( ve.ce.MWMapsNode, ve.ce.MWBlockExtensionNode );

OO.mixinClass( ve.ce.MWMapsNode, ve.ce.ResizableNode );

/* Static Properties */

ve.ce.MWMapsNode.static.name = 'mwMaps';

ve.ce.MWMapsNode.static.tagName = 'div';

ve.ce.MWMapsNode.static.primaryCommandName = 'mwMaps';

/* Methods */

/**
 * A map requires interactive rendering
 *
 * Maps without GeoJSON can be rendered as static
 *
 * @return {boolean} Maps requires interactive rendering
 */
ve.ce.MWMapsNode.prototype.requiresInteractive = function () {
	var mwData = this.model.getAttribute( 'mw' );

	return mwData.body.extsrc;
};

/**
 * Update the rendering of the 'align', src', 'width' and 'height' attributes
 * when they change in the model.
 *
 * @method
 * @param {string} key Attribute key
 * @param {string} from Old value
 * @param {string} to New value
 */
ve.ce.MWMapsNode.prototype.onAttributeChange = function () {
	this.update();
};

/**
 * @inheritdoc
 */
ve.ce.MWMapsNode.prototype.onSetup = function () {
	ve.ce.MWMapsNode.super.prototype.onSetup.call( this );

	this.update();
};

/**
 * Update the map rendering
 */
ve.ce.MWMapsNode.prototype.update = function () {
	var requiresInteractive = this.requiresInteractive(),
		align = ve.getProp( this.model.getAttribute( 'mw' ), 'attrs', 'align' ) ||
			( this.model.doc.getDir() === 'ltr' ? 'right' : 'left' ),
		alignClasses = {
			left: 'floatleft',
			center: 'center',
			right: 'floatright'
		};

	if ( requiresInteractive ) {
		if ( !this.map && this.getRoot() ) {
			mw.loader.using( 'ext.kartographer.live' ).then( this.setupMap.bind( this ) );
		} else if ( this.map ) {
			this.map.invalidateSize();
			this.updateGeoJson();
		}
	} else {
		if ( this.map ) {
			// Node was previously interactive
			this.map.remove();
			this.map = null;
		}
		this.updateStatic();
		$( '<img>' ).attr( 'src', this.model.getUrl( 1000, 1000 ) );
	}
	this.$element
		.removeClass( 'floatleft center floatright' )
		.addClass( alignClasses[ align ] );
};

/**
 * Setup an interactive map
 */
ve.ce.MWMapsNode.prototype.setupMap = function () {
	var mwData = this.model.getAttribute( 'mw' ),
		mwAttrs = mwData && mwData.attrs,
		latitude = +mwAttrs.latitude,
		longitude = +mwAttrs.longitude,
		zoom = +mwAttrs.zoom,
		node = this;

	this.MWMap = kartoLive.MWMap( this.$element[ 0 ], {
		latitude: latitude,
		longitude: longitude,
		zoom: zoom
		// TODO: Support style editing
	} );
	this.MWMap.ready( function ( map ) {
		node.map = map;

		node.updateGeoJson();

		// Disable interaction
		map.dragging.disable();
		map.touchZoom.disable();
		map.doubleClickZoom.disable();
		map.scrollWheelZoom.disable();
		map.keyboard.disable();
	} );
};

/**
 * Update the GeoJSON layer from the current model state
 */
ve.ce.MWMapsNode.prototype.updateGeoJson = function () {
	var mwData = this.model.getAttribute( 'mw' ),
		geoJson = mwData && mwData.body.extsrc;

	if ( geoJson !== this.geoJson ) {
		kartoEditing.updateKartographerLayer( this.map, mwData && mwData.body.extsrc );
		this.geoJson = geoJson;
	}
};

/**
 * Update the static rendering
 */
ve.ce.MWMapsNode.prototype.updateStatic = function ( width, height ) {
	var url, node = this;

	if ( !this.model.getCurrentDimensions().width ) {
		return;
	}

	if ( this.$imageLoader ) {
		this.$imageLoader.off();
		this.$imageLoader = null;
	}

	url = this.model.getUrl( width, height );

	this.$imageLoader = this.$( '<img>' ).on( 'load', function () {
		node.$element.css( 'backgroundImage', 'url(' + url + ')' );
	} ).attr( 'src', url );
};

/**
 * @inheritdoc ve.ce.ResizableNode
 */
ve.ce.MWMapsNode.prototype.onResizableResizing = function () {
	// Mixin method
	ve.ce.ResizableNode.prototype.onResizableResizing.apply( this, arguments );

	if ( !this.requiresInteractive() ) {
		this.updateStatic( 1000, 1000 );
	} else if ( this.map ) {
		this.map.invalidateSize();
	}
};

/**
 * @inheritdoc ve.ce.ResizableNode
 */
ve.ce.MWMapsNode.prototype.getAttributeChanges = function ( width, height ) {
	var mwData = ve.copy( this.model.getAttribute( 'mw' ) );

	mwData.attrs.width = width.toString();
	mwData.attrs.height = height.toString();

	return { mw: mwData };
};

/**
 * Handle focus events
 */
ve.ce.MWMapsNode.prototype.onMapFocus = function () {
	$( '<img>' ).attr( 'src', this.model.getUrl( 1000, 1000 ) );
};

/* Registration */

ve.ce.nodeFactory.register( ve.ce.MWMapsNode );
<commit_msg>Fix VE not updating map position on "done"<commit_after>/*!
 * VisualEditor ContentEditable MWMapsNode class.
 *
 * @copyright 2011-2015 VisualEditor Team and others; see http://ve.mit-license.org
 */
/* globals require */
var kartoLive = require( 'ext.kartographer.live' ),
	kartoEditing = require( 'ext.kartographer.editing' );

/**
 * ContentEditable paragraph node.
 *
 * @class
 * @extends ve.ce.MWBlockExtensionNode
 * @mixins ve.ce.ResizableNode
 *
 * @constructor
 * @param {ve.dm.MWMapsNode} model Model to observe
 * @param {Object} [config] Configuration options
 */
ve.ce.MWMapsNode = function VeCeMWMaps( model, config ) {
	config = config || {};

	// Parent constructor
	ve.ce.MWMapsNode.super.apply( this, arguments );

	// Mixin constructors
	ve.ce.ResizableNode.call( this, this.$element, config );

	this.$imageLoader = null;
	this.geoJson = null;
	this.mapData = {};

	this.updateMapPosition = $.debounce( 300, $.proxy( this.updateMapPosition, this ) );
	this.updateGeoJson = $.debounce( 300, $.proxy( this.updateGeoJson, this ) );

	// Events
	this.model.connect( this, { attributeChange: 'onAttributeChange' } );
	this.connect( this, { focus: 'onMapFocus' } );

	// Ensure we have the styles to render the map node
	mw.loader.using( 'ext.kartographer' );

	// DOM changes
	this.$element
		.empty()
		.addClass( 've-ce-mwMapsNode' )
		.css( this.model.getCurrentDimensions() );
};

/* Inheritance */

OO.inheritClass( ve.ce.MWMapsNode, ve.ce.MWBlockExtensionNode );

OO.mixinClass( ve.ce.MWMapsNode, ve.ce.ResizableNode );

/* Static Properties */

ve.ce.MWMapsNode.static.name = 'mwMaps';

ve.ce.MWMapsNode.static.tagName = 'div';

ve.ce.MWMapsNode.static.primaryCommandName = 'mwMaps';

/* Methods */

/**
 * A map requires interactive rendering
 *
 * Maps without GeoJSON can be rendered as static
 *
 * @return {boolean} Maps requires interactive rendering
 */
ve.ce.MWMapsNode.prototype.requiresInteractive = function () {
	var mwData = this.model.getAttribute( 'mw' );

	return mwData.body.extsrc;
};

/**
 * Update the rendering of the 'align', src', 'width' and 'height' attributes
 * when they change in the model.
 *
 * @method
 * @param {string} key Attribute key
 * @param {string} from Old value
 * @param {string} to New value
 */
ve.ce.MWMapsNode.prototype.onAttributeChange = function () {
	this.update();
};

/**
 * @inheritdoc
 */
ve.ce.MWMapsNode.prototype.onSetup = function () {
	ve.ce.MWMapsNode.super.prototype.onSetup.call( this );

	this.update();
};

/**
 * Update the map rendering
 */
ve.ce.MWMapsNode.prototype.update = function () {
	var requiresInteractive = this.requiresInteractive(),
		align = ve.getProp( this.model.getAttribute( 'mw' ), 'attrs', 'align' ) ||
			( this.model.doc.getDir() === 'ltr' ? 'right' : 'left' ),
		alignClasses = {
			left: 'floatleft',
			center: 'center',
			right: 'floatright'
		};

	if ( requiresInteractive ) {
		if ( !this.map && this.getRoot() ) {
			mw.loader.using( 'ext.kartographer.live' ).then( this.setupMap.bind( this ) );
		} else if ( this.map ) {
			this.updateMapPosition();
			this.updateGeoJson();
		}
	} else {
		if ( this.map ) {
			// Node was previously interactive
			this.map.remove();
			this.map = null;
		}
		this.updateStatic();
		$( '<img>' ).attr( 'src', this.model.getUrl( 1000, 1000 ) );
	}
	this.$element
		.removeClass( 'floatleft center floatright' )
		.addClass( alignClasses[ align ] )
		.css( this.model.getCurrentDimensions() );
};

/**
 * Setup an interactive map
 */
ve.ce.MWMapsNode.prototype.setupMap = function () {
	var mwData = this.model.getAttribute( 'mw' ),
		mwAttrs = mwData && mwData.attrs,
		latitude = +mwAttrs.latitude,
		longitude = +mwAttrs.longitude,
		zoom = +mwAttrs.zoom,
		node = this;

	this.MWMap = kartoLive.MWMap( this.$element[ 0 ], {
		latitude: latitude,
		longitude: longitude,
		zoom: zoom
		// TODO: Support style editing
	} );
	this.MWMap.ready( function ( map ) {
		node.map = map;

		node.updateGeoJson();

		// Disable interaction
		map.dragging.disable();
		map.touchZoom.disable();
		map.doubleClickZoom.disable();
		map.scrollWheelZoom.disable();
		map.keyboard.disable();
	} );
};

/**
 * Update the GeoJSON layer from the current model state
 */
ve.ce.MWMapsNode.prototype.updateGeoJson = function () {
	var mwData = this.model.getAttribute( 'mw' ),
		geoJson = mwData && mwData.body.extsrc;

	if ( geoJson !== this.geoJson ) {
		kartoEditing.updateKartographerLayer( this.map, mwData && mwData.body.extsrc );
		this.geoJson = geoJson;
	}
};

/**
 * Updates the map position (center and zoom) from the current model state.
 */
ve.ce.MWMapsNode.prototype.updateMapPosition = function () {
	var mwData = this.model.getAttribute( 'mw' ),
		mapData = this.mapData,
		updatedData = mwData && mwData.attrs;

	if (
		mapData.latitude !== updatedData.latitude ||
		mapData.longitude !== updatedData.longitude ||
		mapData.zoom !== updatedData.zoom
	) {
		this.map.setView( [ updatedData.latitude, updatedData.longitude ], updatedData.zoom );
		mapData.latitude = updatedData.latitude;
		mapData.longitude = updatedData.longitude;
		mapData.zoom = updatedData.zoom;
	} else {
		this.map.invalidateSize();
	}
};

/**
 * Update the static rendering
 */
ve.ce.MWMapsNode.prototype.updateStatic = function ( width, height ) {
	var url, node = this;

	if ( !this.model.getCurrentDimensions().width ) {
		return;
	}

	if ( this.$imageLoader ) {
		this.$imageLoader.off();
		this.$imageLoader = null;
	}

	url = this.model.getUrl( width, height );

	this.$imageLoader = this.$( '<img>' ).on( 'load', function () {
		node.$element.css( 'backgroundImage', 'url(' + url + ')' );
	} ).attr( 'src', url );
};

/**
 * @inheritdoc ve.ce.ResizableNode
 */
ve.ce.MWMapsNode.prototype.onResizableResizing = function () {
	// Mixin method
	ve.ce.ResizableNode.prototype.onResizableResizing.apply( this, arguments );

	if ( !this.requiresInteractive() ) {
		this.updateStatic( 1000, 1000 );
	} else if ( this.map ) {
		this.map.invalidateSize();
	}
};

/**
 * @inheritdoc ve.ce.ResizableNode
 */
ve.ce.MWMapsNode.prototype.getAttributeChanges = function ( width, height ) {
	var mwData = ve.copy( this.model.getAttribute( 'mw' ) );

	mwData.attrs.width = width.toString();
	mwData.attrs.height = height.toString();

	return { mw: mwData };
};

/**
 * Handle focus events
 */
ve.ce.MWMapsNode.prototype.onMapFocus = function () {
	$( '<img>' ).attr( 'src', this.model.getUrl( 1000, 1000 ) );
};

/* Registration */

ve.ce.nodeFactory.register( ve.ce.MWMapsNode );
<|endoftext|>


<commit_before>import React from 'react';

import Immutable from 'immutable';
import styled from 'styled-components';

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import {
  Models,
  Types
} from 'lattice';

import LoadingSpinner from '../../../components/asynccontent/LoadingSpinner';
import StyledFlexContainerStacked from '../../../components/flex/StyledFlexContainerStacked';

import * as OrgsUtils from '../utils/OrgsUtils';

import {
  AddButton,
  SearchIcon,
  StyledElement,
  StyledInput,
  StyledListItem
} from './StyledListGroupComponents';

import StyledSectionHeading from './StyledSectionHeading';

import {
  addMemberToOrganizationRequest,
  clearUserSearchResults,
  removeMemberFromOrganizationRequest
} from '../actions/OrganizationActionFactory';

import {
  updateAclRequest
} from '../../permissions/PermissionsActionFactory';

import {
  searchAllUsersRequest
} from '../../principals/PrincipalsActionFactory';

const {
  AclBuilder,
  AclDataBuilder,
  AceBuilder,
  PrincipalBuilder
} = Models;

const {
  ActionTypes,
  PermissionTypes,
  PrincipalTypes
} = Types;

const SearchContainer = styled.div`
  min-height: 150px;
  padding-bottom: 50px;
  width: 400px;
`;

const SearchSpinner = styled(StyledElement)`
  padding: 0;
  flex: 0;
`;

function mapStateToProps(state) {

  const isSearchingUsers = state.getIn(['organizations', 'isSearchingUsers']);
  const usersSearchResults = state.getIn(['organizations', 'usersSearchResults'], Immutable.Map());

  return {
    isSearchingUsers,
    usersSearchResults
  };
}

function mapDispatchToProps(dispatch) {

  const actions = {
    addMemberToOrganizationRequest,
    clearUserSearchResults,
    removeMemberFromOrganizationRequest,
    searchAllUsersRequest,
    updateAclRequest
  };

  return {
    actions: bindActionCreators(actions, dispatch)
  };
}

class OrganizationAddMembersSectionComponent extends React.Component {

  static propTypes = {
    actions: React.PropTypes.shape({
      addMemberToOrganizationRequest: React.PropTypes.func.isRequired,
      clearUserSearchResults: React.PropTypes.func.isRequired,
      removeMemberFromOrganizationRequest: React.PropTypes.func.isRequired,
      searchAllUsersRequest: React.PropTypes.func.isRequired,
      updateAclRequest: React.PropTypes.func.isRequired
    }).isRequired,
    isSearchingUsers: React.PropTypes.bool.isRequired,
    organization: React.PropTypes.instanceOf(Immutable.Map).isRequired,
    usersSearchResults: React.PropTypes.instanceOf(Immutable.Map).isRequired
  }

  constructor(props) {

    super(props);

    this.state = {
      searchInputValue: ''
    };
  }

  search = (searchQuery) => {

    if (!searchQuery) {
      return;
    }

    this.props.actions.searchAllUsersRequest(searchQuery);
  }

  handleOnChangeSearchInput = (event) => {

    this.setState({
      searchInputValue: event.target.value
    });
  }

  handleOnKeyDownSearchInput = (event) => {

    switch (event.keyCode) {
      case 8: { // 'Backspace' key code
        this.props.actions.clearUserSearchResults();
        break;
      }
      case 13: // 'Enter' key code
        if (event.target.value.length === 0) {
          this.props.actions.clearUserSearchResults();
        }
        else {
          this.search(event.target.value);
        }
        break;
      default:
        break;
    }
  }

  renderSearchBox = () => {

    // TODO: think about refactoring into a shared styled compontent
    return (

    );
  }

  handleOnClickAddMember = (userId) => {

    if (!userId) {
      // TODO: this shouldn't happen, how do we handle it?
      return;
    }

    this.props.actions.addMemberToOrganizationRequest(
      this.props.organization.get('id'),
      userId
    );

    const principal = (new PrincipalBuilder())
      .setType(PrincipalTypes.USER)
      .setId(userId)
      .build();

    const ace = (new AceBuilder())
      .setPermissions([PermissionTypes.READ])
      .setPrincipal(principal)
      .build();

    const acl = (new AclBuilder())
      .setAclKey([this.props.organization.get('id')])
      .setAces([ace])
      .build();

    const aclData = (new AclDataBuilder())
      .setAction(ActionTypes.ADD)
      .setAcl(acl)
      .build();

    this.props.actions.updateAclRequest(aclData);
  }

  renderSearchResults = () => {

    if (this.props.isSearchingUsers) {
      return null;
    }

    // // TODO: only show this when a search was performed and the results are empty
    if (this.props.usersSearchResults.isEmpty()) {
      return No users found.;
    }

    const users = [];

    this.props.usersSearchResults.forEach((user) => {

      const userId :string = user.get('user_id');
      const label = OrgsUtils.getUserNameLabelValue(user);

      const userListItem = (

          <AddButton
              onClick={() => {
                this.handleOnClickAddMember(userId);
              }} />
        </StyledListItem>
      );

      users.push(userListItem);
    });

    return users;
  }

  render() {

    const isOwner = this.props.organization.get('isOwner', false);

    if (!isOwner) {
      return null;
    }

    return (

        <SearchContainer>
          { this.renderSearchBox() }
          { this.renderSearchResults() }
        </SearchContainer>
      </StyledFlexContainerStacked>
    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(OrganizationAddMembersSectionComponent);
<commit_msg>update member search instructions in orgs<commit_after>import React from 'react';

import Immutable from 'immutable';
import styled from 'styled-components';

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import {
  Models,
  Types
} from 'lattice';

import LoadingSpinner from '../../../components/asynccontent/LoadingSpinner';
import StyledFlexContainerStacked from '../../../components/flex/StyledFlexContainerStacked';

import * as OrgsUtils from '../utils/OrgsUtils';

import {
  AddButton,
  SearchIcon,
  StyledElement,
  StyledInput,
  StyledListItem
} from './StyledListGroupComponents';

import StyledSectionHeading from './StyledSectionHeading';

import {
  addMemberToOrganizationRequest,
  clearUserSearchResults,
  removeMemberFromOrganizationRequest
} from '../actions/OrganizationActionFactory';

import {
  updateAclRequest
} from '../../permissions/PermissionsActionFactory';

import {
  searchAllUsersRequest
} from '../../principals/PrincipalsActionFactory';

const {
  AclBuilder,
  AclDataBuilder,
  AceBuilder,
  PrincipalBuilder
} = Models;

const {
  ActionTypes,
  PermissionTypes,
  PrincipalTypes
} = Types;

const SearchContainer = styled.div`
  min-height: 150px;
  padding-bottom: 50px;
  width: 400px;
`;

const SearchSpinner = styled(StyledElement)`
  padding: 0;
  flex: 0;
`;

function mapStateToProps(state) {

  const isSearchingUsers = state.getIn(['organizations', 'isSearchingUsers']);
  const usersSearchResults = state.getIn(['organizations', 'usersSearchResults'], Immutable.Map());

  return {
    isSearchingUsers,
    usersSearchResults
  };
}

function mapDispatchToProps(dispatch) {

  const actions = {
    addMemberToOrganizationRequest,
    clearUserSearchResults,
    removeMemberFromOrganizationRequest,
    searchAllUsersRequest,
    updateAclRequest
  };

  return {
    actions: bindActionCreators(actions, dispatch)
  };
}

class OrganizationAddMembersSectionComponent extends React.Component {

  static propTypes = {
    actions: React.PropTypes.shape({
      addMemberToOrganizationRequest: React.PropTypes.func.isRequired,
      clearUserSearchResults: React.PropTypes.func.isRequired,
      removeMemberFromOrganizationRequest: React.PropTypes.func.isRequired,
      searchAllUsersRequest: React.PropTypes.func.isRequired,
      updateAclRequest: React.PropTypes.func.isRequired
    }).isRequired,
    isSearchingUsers: React.PropTypes.bool.isRequired,
    organization: React.PropTypes.instanceOf(Immutable.Map).isRequired,
    usersSearchResults: React.PropTypes.instanceOf(Immutable.Map).isRequired
  }

  constructor(props) {

    super(props);

    this.state = {
      searchInputValue: ''
    };
  }

  search = (searchQuery) => {

    if (!searchQuery) {
      return;
    }

    this.props.actions.searchAllUsersRequest(searchQuery);
  }

  handleOnChangeSearchInput = (event) => {

    this.setState({
      searchInputValue: event.target.value
    });
  }

  handleOnKeyDownSearchInput = (event) => {

    switch (event.keyCode) {
      case 8: { // 'Backspace' key code
        this.props.actions.clearUserSearchResults();
        break;
      }
      case 13: // 'Enter' key code
        if (event.target.value.length === 0) {
          this.props.actions.clearUserSearchResults();
        }
        else {
          this.search(event.target.value);
        }
        break;
      default:
        break;
    }
  }

  renderSearchBox = () => {

    // TODO: think about refactoring into a shared styled compontent
    return (

    );
  }

  handleOnClickAddMember = (userId) => {

    if (!userId) {
      // TODO: this shouldn't happen, how do we handle it?
      return;
    }

    this.props.actions.addMemberToOrganizationRequest(
      this.props.organization.get('id'),
      userId
    );

    const principal = (new PrincipalBuilder())
      .setType(PrincipalTypes.USER)
      .setId(userId)
      .build();

    const ace = (new AceBuilder())
      .setPermissions([PermissionTypes.READ])
      .setPrincipal(principal)
      .build();

    const acl = (new AclBuilder())
      .setAclKey([this.props.organization.get('id')])
      .setAces([ace])
      .build();

    const aclData = (new AclDataBuilder())
      .setAction(ActionTypes.ADD)
      .setAcl(acl)
      .build();

    this.props.actions.updateAclRequest(aclData);
  }

  renderSearchResults = () => {

    if (this.props.isSearchingUsers) {
      return null;
    }

    // // TODO: only show this when a search was performed and the results are empty
    if (this.props.usersSearchResults.isEmpty()) {
      return No users found.;
    }

    const users = [];

    this.props.usersSearchResults.forEach((user) => {

      const userId :string = user.get('user_id');
      const label = OrgsUtils.getUserNameLabelValue(user);

      const userListItem = (

          <AddButton
              onClick={() => {
                this.handleOnClickAddMember(userId);
              }} />
        </StyledListItem>
      );

      users.push(userListItem);
    });

    return users;
  }

  render() {

    const isOwner = this.props.organization.get('isOwner', false);

    if (!isOwner) {
      return null;
    }

    return (

        <SearchContainer>
          { this.renderSearchBox() }
          { this.renderSearchResults() }
        </SearchContainer>
      </StyledFlexContainerStacked>
    );
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(OrganizationAddMembersSectionComponent);
<|endoftext|>


<commit_before>/* global bindingsResolver, ChannelDefinition, SubscriptionDefinition, _postal, prevPostal, global */
/*jshint -W020 */
var fireSub = function ( subDef, envelope ) {
    if ( !subDef.inactive && _postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
        subDef.callback.call( subDef.context || this, envelope.data, envelope );
    }
};
var pubInProgress = 0;
var unSubQueue = [];
var clearUnSubQueue = function () {
    while ( unSubQueue.length ) {
        _postal.unsubscribe(unSubQueue.shift());
    }
};
_postal = {
	configuration : {
		resolver        : bindingsResolver,
		DEFAULT_CHANNEL : "/",
		SYSTEM_CHANNEL  : "postal"
	},
    subscriptions : {},
    wireTaps : [],

	ChannelDefinition      : ChannelDefinition,
	SubscriptionDefinition : SubscriptionDefinition,

	channel : function ( channelName ) {
		return new ChannelDefinition( channelName );
	},

	subscribe : function ( options ) {
        var subDef = new SubscriptionDefinition( options.channel || this.configuration.DEFAULT_CHANNEL, options.topic, options.callback );
        var channel = this.subscriptions[subDef.channel];
        var subs;
        this.publish( {
            channel : this.configuration.SYSTEM_CHANNEL,
            topic   : "subscription.created",
            data    : {
                event   : "subscription.created",
                channel : subDef.channel,
                topic   : subDef.topic
            }
        } );
        if ( !channel ) {
            channel = this.subscriptions[subDef.channel] = {};
        }
        subs = this.subscriptions[subDef.channel][subDef.topic];
        if ( !subs ) {
            subs = this.subscriptions[subDef.channel][subDef.topic] = [];
        }
        subs.push( subDef );
        return subDef;
	},

	publish : function ( envelope ) {
        ++pubInProgress;
		envelope.channel = envelope.channel || this.configuration.DEFAULT_CHANNEL;
        envelope.timeStamp = new Date();
        _.each( this.wireTaps, function ( tap ) {
            tap( envelope.data, envelope );
        } );
        if ( this.subscriptions[envelope.channel] ) {
            _.each( this.subscriptions[envelope.channel], function ( subscribers ) {
                var idx = 0, len = subscribers.length, subDef;
                while ( idx < len ) {
                    if ( subDef = subscribers[idx++] ) {
                        fireSub( subDef, envelope );
                    }
                }
            } );
        }
        if ( --pubInProgress === 0 ) {
            clearUnSubQueue();
        }
	},

    unsubscribe: function( subDef ) {
        if ( pubInProgress ) {
            unSubQueue.push( subDef );
            return;
        }
        if ( this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic] ) {
            var len = this.subscriptions[subDef.channel][subDef.topic].length,
                idx = 0;
            while ( idx < len ) {
                if ( this.subscriptions[subDef.channel][subDef.topic][idx] === subDef ) {
                    this.subscriptions[subDef.channel][subDef.topic].splice( idx, 1 );
                    break;
                }
                idx += 1;
            }
        }
        this.publish( {
            channel : this.configuration.SYSTEM_CHANNEL,
            topic   : "subscription.removed",
            data    : {
                event   : "subscription.removed",
                channel : subDef.channel,
                topic   : subDef.topic
            }
        });
    },

	addWireTap : function ( callback ) {
        var self = this;
        self.wireTaps.push( callback );
        return function () {
            var idx = self.wireTaps.indexOf( callback );
            if ( idx !== -1 ) {
                self.wireTaps.splice( idx, 1 );
            }
        };
	},

    noConflict: function() {
        if(typeof window === "undefined" || (typeof window !== "undefined" && typeof define === "function" && define.amd )) {
            throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");
        }
        global.postal = prevPostal;
        return this;
    },

    getSubscribersFor : function () {
        var channel = arguments[ 0 ],
            tpc = arguments[ 1 ];
        if ( arguments.length === 1 ) {
            channel = arguments[ 0 ].channel || this.configuration.DEFAULT_CHANNEL;
            tpc = arguments[ 0 ].topic;
        }
        if ( this.subscriptions[ channel ] &&
            Object.prototype.hasOwnProperty.call( this.subscriptions[ channel ], tpc ) ) {
            return this.subscriptions[ channel ][ tpc ];
        }
        return [];
    },

    reset : function () {
        if ( this.subscriptions ) {
            _.each( this.subscriptions, function ( channel ) {
                _.each( channel, function ( topic ) {
                    while ( topic.length ) {
                        topic.pop().unsubscribe();
                    }
                } );
            } );
            this.subscriptions = {};
        }
        this.configuration.resolver.reset();
    }
};
_postal.subscriptions[_postal.configuration.SYSTEM_CHANNEL] = {};<commit_msg>Fixes #65<commit_after>/* global bindingsResolver, ChannelDefinition, SubscriptionDefinition, _postal, prevPostal, global */
/*jshint -W020 */
var fireSub = function ( subDef, envelope ) {
    if ( !subDef.inactive && _postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
        subDef.callback.call( subDef.context || this, envelope.data, envelope );
    }
};
var pubInProgress = 0;
var unSubQueue = [];
var clearUnSubQueue = function () {
    while ( unSubQueue.length ) {
        _postal.unsubscribe(unSubQueue.shift());
    }
};
_postal = {
	configuration : {
		resolver        : bindingsResolver,
		DEFAULT_CHANNEL : "/",
		SYSTEM_CHANNEL  : "postal"
	},
    subscriptions : {},
    wireTaps : [],

	ChannelDefinition      : ChannelDefinition,
	SubscriptionDefinition : SubscriptionDefinition,

	channel : function ( channelName ) {
		return new ChannelDefinition( channelName );
	},

	subscribe : function ( options ) {
        var subDef = new SubscriptionDefinition( options.channel || this.configuration.DEFAULT_CHANNEL, options.topic, options.callback );
        var channel = this.subscriptions[subDef.channel];
        var subs;
        this.publish( {
            channel : this.configuration.SYSTEM_CHANNEL,
            topic   : "subscription.created",
            data    : {
                event   : "subscription.created",
                channel : subDef.channel,
                topic   : subDef.topic
            }
        } );
        if ( !channel ) {
            channel = this.subscriptions[subDef.channel] = {};
        }
        subs = this.subscriptions[subDef.channel][subDef.topic];
        if ( !subs ) {
            subs = this.subscriptions[subDef.channel][subDef.topic] = [];
        }
        subs.push( subDef );
        return subDef;
	},

	publish : function ( envelope ) {
        ++pubInProgress;
		envelope.channel = envelope.channel || this.configuration.DEFAULT_CHANNEL;
        envelope.timeStamp = new Date();
        _.each( this.wireTaps, function ( tap ) {
            tap( envelope.data, envelope );
        } );
        if ( this.subscriptions[envelope.channel] ) {
            _.each( this.subscriptions[envelope.channel], function ( subscribers ) {
                var idx = 0, len = subscribers.length, subDef;
                while ( idx < len ) {
                    if ( subDef = subscribers[idx++] ) {
                        fireSub( subDef, envelope );
                    }
                }
            } );
        }
        if ( --pubInProgress === 0 ) {
            clearUnSubQueue();
        }
	},

    unsubscribe: function( subDef ) {
        if ( pubInProgress ) {
            unSubQueue.push( subDef );
            return;
        }
        if ( this.subscriptions[subDef.channel] && this.subscriptions[subDef.channel][subDef.topic] ) {
            var len = this.subscriptions[subDef.channel][subDef.topic].length,
                idx = 0;
            while ( idx < len ) {
                if ( this.subscriptions[subDef.channel][subDef.topic][idx] === subDef ) {
                    this.subscriptions[subDef.channel][subDef.topic].splice( idx, 1 );
                    break;
                }
                idx += 1;
            }
        }
        this.publish( {
            channel : this.configuration.SYSTEM_CHANNEL,
            topic   : "subscription.removed",
            data    : {
                event   : "subscription.removed",
                channel : subDef.channel,
                topic   : subDef.topic
            }
        });
    },

	addWireTap : function ( callback ) {
        var self = this;
        self.wireTaps.push( callback );
        return function () {
            var idx = self.wireTaps.indexOf( callback );
            if ( idx !== -1 ) {
                self.wireTaps.splice( idx, 1 );
            }
        };
	},

    noConflict: function() {
        if(typeof window === "undefined" || (typeof window !== "undefined" && typeof define === "function" && define.amd )) {
            throw new Error("noConflict can only be used in browser clients which aren't using AMD modules");
        }
        global.postal = prevPostal;
        return this;
    },

    getSubscribersFor : function () {
        var channel = arguments[ 0 ],
            tpc = arguments[ 1 ];
        if ( arguments.length === 1 ) {
            channel = arguments[ 0 ].channel || this.configuration.DEFAULT_CHANNEL;
            tpc = arguments[ 0 ].topic || arguments[ 0 ];
        }
        if ( this.subscriptions[ channel ] &&
            Object.prototype.hasOwnProperty.call( this.subscriptions[ channel ], tpc ) ) {
            return this.subscriptions[ channel ][ tpc ];
        }
        return [];
    },

    reset : function () {
        if ( this.subscriptions ) {
            _.each( this.subscriptions, function ( channel ) {
                _.each( channel, function ( topic ) {
                    while ( topic.length ) {
                        topic.pop().unsubscribe();
                    }
                } );
            } );
            this.subscriptions = {};
        }
        this.configuration.resolver.reset();
    }
};
_postal.subscriptions[_postal.configuration.SYSTEM_CHANNEL] = {};<|endoftext|>


<commit_before>module.exports = {
    "env": {
        "browser": true,
        "commonjs": true,
        "es6": true,
    },
    "globals": {
        "_gaq": false,
        "process": false,
        "ActiveXObject": false,
        "VERSION": false,
        // Build globals
        "__dirname": false,
        // Test globals
        "after": false,
        "afterEach": false,
        "assert": false,
        "before": false,
        "beforeEach": false,
        "describe": false,
        "expect": false,
        "it": false,
        "sinon": false,
        "xit": false
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "sourceType": "module"
    },
    "rules": {
        "indent": [
            "error",
            2
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "never"
        ],
        "no-var": "error"
    }
};
<commit_msg>add PLAIN_HTML5_ONLY to eslint globals<commit_after>module.exports = {
    "env": {
        "browser": true,
        "commonjs": true,
        "es6": true,
    },
    "globals": {
        "_gaq": false,
        "process": false,
        "ActiveXObject": false,
        "VERSION": false,
        "PLAIN_HTML5_ONLY": false,
        // Build globals
        "__dirname": false,
        // Test globals
        "after": false,
        "afterEach": false,
        "assert": false,
        "before": false,
        "beforeEach": false,
        "describe": false,
        "expect": false,
        "it": false,
        "sinon": false,
        "xit": false
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "sourceType": "module"
    },
    "rules": {
        "indent": [
            "error",
            2
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "never"
        ],
        "no-var": "error"
    }
};
<|endoftext|>


<commit_before>'use strict';

angular.module('products').controller('TabController', function(){
    this.tab = 1;

    this.setTab = function(newValue){
      this.tab = newValue;
    };

    this.isSet = function(tabName){
      return this.tab === tabName;
    };
  });

// products controller
angular.module('products').controller('productsController', ['$scope', '$stateParams', '$location', 'Authentication', 'products',
  function ($scope, $stateParams, $location, Authentication, products) {
    $scope.authentication = Authentication;
    $scope.loadInfo = true;
    // Create new product
    $scope.create = function () {

        // Redirect after save
        product.$save(function (response) {
          $location.path('products/' + response._id);

          // Clear form fields
          $scope.sku = '';
        }, function (errorResponse) {
          $scope.error = errorResponse.data.message;
        });
      };

    // Remove existing product
    $scope.remove = function (product) {
      if (product) {
        product.$remove();

        for (var i in $scope.products) {
          if ($scope.products[i] === product) {
            $scope.products.splice(i, 1);
          }
        }
      } else {
        $scope.product.$remove(function () {
          $location.path('products');
        });
      }
    };

    // Update existing product
    $scope.update = function () {
      var product = $scope.product;

      product.$update(function () {
        $location.path('products/' + product._id);
      }, function (errorResponse) {
        $scope.error = errorResponse.data.message;
      });
    };

    $scope.totalDisplayed = 20;

    $scope.loadMore = function () {
      $scope.totalDisplayed += 20;
    };

    // Find a list of products
    $scope.find = function () {
      $scope.products = products.query(function(){
        $scope.loadInfo = false;
      });
      console.log($scope.products);
    };

    // Find existing product
    $scope.findOne = function () {
      $scope.product = products.get({
        productId: $stateParams.productId
      });
    };
  }]);
<commit_msg>added an s<commit_after>'use strict';

angular.module('products').controller('TabController', function(){
    this.tab = 1;

    this.setTab = function(newValue){
      this.tab = newValue;
    };

    this.isSet = function(tabName){
      return this.tab === tabName;
    };
  });

// products controller
angular.module('products').controller('productsController', ['$scope', '$stateParams', '$location', 'Authentication', 'products',
  function ($scope, $stateParams, $location, Authentication, products) {
    $scope.authentication = Authentication;
    $scope.loadInfo = true;
    // Create new product
    $scope.create = function () {

        // Redirect after save
        product.$save(function (response) {
          $location.path('products/' + response._id);

          // Clear form fields
          $scope.sku = '';
        }, function (errorResponse) {
          $scope.error = errorResponse.data.message;
        });
      };

    // Remove existing product
    $scope.remove = function (product) {
      if (product) {
        product.$remove();

        for (var i in $scope.products) {
          if ($scope.products[i] === product) {
            $scope.products.splice(i, 1);
          }
        }
      } else {
        $scope.product.$remove(function () {
          $location.path('products');
        });
      }
    };

    // Update existing product
    $scope.update = function () {
      var product = $scope.products;

      product.$update(function () {
        $location.path('products/' + product._id);
      }, function (errorResponse) {
        $scope.error = errorResponse.data.message;
      });
    };

    $scope.totalDisplayed = 20;

    $scope.loadMore = function () {
      $scope.totalDisplayed += 20;
    };

    // Find a list of products
    $scope.find = function () {
      $scope.products = products.query(function(){
        $scope.loadInfo = false;
      });
      console.log($scope.products);
    };

    // Find existing product
    $scope.findOne = function () {
      $scope.product = products.get({
        productId: $stateParams.productId
      });
    };
  }]);
<|endoftext|>


<commit_before>module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        js_dir: "src",
        project_dir: ".",
        ci_branch: process.env.TRAVIS_BRANCH,
        gitinfo: {
        },
        clean: [
            "build", "release"
        ],
        concat: {
            "options": {"separator": "\n"},
            "build": {
                "src": require("./config/js.json"),
                "dest": "build/debug/js/fm-js-all.js"
            }
        },
        uglify: {
            files: {
                src: "build/debug/js/fm-js-all.js",
                dest: "build/production/js/fm-js-all.js"
            }
        },
        copy: {
            resources: {
                files: [
                    {
                        expand: true,
                        src: require("./config/resources.json"),
                        dest: 'build/debug',
                        filter: 'isFile'
                    },
                    {
                        expand: true,
                        src: require("./config/resources.json"),
                        dest: 'build/production',
                        filter: 'isFile'
                    }
                ]
            }
        },
        exec: {
            run_jsdoc2: {
                command: 'mkdir -p build && node node_modules/jsdoc-toolkit/ -r=99 src/ -t=node_modules/jsdoc-toolkit/templates/jsdoc -d=build/apidoc',
                stdout: true
            },
            prepare_release: {
                command: 'bash bin/_prepare_release',
                stdout: true
            },
            commit_release: {
                command: 'bash bin/_commit_release',
                stdout: true
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-exec');

    grunt.registerTask(
        'create_releases_list',
        'Parse JSON table with releases and add new one.',
        function() {
            function getVersion() {
                var version = grunt.config('ci_branch');

                grunt.log.writeln("Version: " + version + "/ " + version.substring(1));

                if (!version || version.substring(0, 1) != 'v') {
                    grunt.log.error('This is not release tag. Abortiong ...');
                    return false;
                }
                var x = version.substring(1).split('.');
                var verArr = {
                    major: parseInt(x[0]) || -1,
                    minor: parseInt(x[1]) || -1,
                    fix: x[2] || ""
                };
                if (verArr.major == -1 || verArr.minor == -1) {
                    grunt.log.error('This is not release tag. Abortiong (2) ...');
                    return false;
                }
                verArr.str = '' + verArr.major + "." + verArr.minor + "." + verArr.fix;
                verArr.filename = 'fm-js-' + verArr.str;

                verArr.date = new Date().getTime();
                verArr.relID =
                    verArr.major * 10000000 +
                    verArr.minor * 100000 +
                    (
                        verArr.fix.substring(0, 3) == 'rel' ?
                        (parseInt(verArr.fix.substring(3)) || 0) * 1000 :
                        (
                        verArr.fix.substring(0, 2) == 'rc' ?
                        (parseInt(verArr.fix.substring(2)) || 0) * 10: (parseInt(verArr.fix) || 0)
                        )
                    )
                ;

                return verArr;
            }

            // get git branch name and parse version numbers
            var ver = getVersion();
            if (ver == false) {
                return false;
            }

            grunt.log.writeln('Version info: ' + JSON.stringify(ver));

            // load releases table
            var relarr = grunt.file.readJSON("release/gh-pages/data/releases.json");
            if (!relarr || relarr.constructor.toString().indexOf("Array") == -1) {
                relarr = [];
            }
            grunt.log.writeln('Old release table: ' + JSON.stringify(relarr));
            relObjs = {};
            for (var rel in relarr) {
                relObjs[relarr[rel].relid] = relarr[rel];
            }
            grunt.log.writeln('Old release objects: ' + JSON.stringify(relObjs));

            //modify release table
            if (ver.fix.substring(0, 3) == 'rel') {
                relObjs["99999999"] = {
                    version: "latest stable (recomended download)",
                    relid: 99999999,
                    link: "releases/" + ver.filename + ".zip",
                    doc: "apidoc/" + ver.filename + "/index.html",
                    deployed: rel.date
                };
            } else {
                relObjs["99999990"] = {
                    version: "latest unstable",
                    relid: 99999990,
                    link: "releases/" + ver.filename + ".zip",
                    doc: "apidoc/" + ver.filename + "/index.html",
                    deployed: rel.date
                };
            }
            relObjs['' + ver.relID] = {
                version: ver.str,
                relid: ver.relID,
                link: "releases/" + ver.filename + ".zip",
                doc: "apidoc/" + ver.filename + "/index.html",
                deployed: rel.date
            };
            grunt.log.writeln('New release objects: ' + JSON.stringify(relObjs));

            relarr = [];
            for (var rel in relObjs) {
                relarr.push(relObjs[rel]);
            }
            relarr.sort(function(a, b) {
                return b.relid - a.relid
            });

            // save release table back
            grunt.log.writeln('New release table: ' + JSON.stringify(relarr));
            //grunt.file.write('release/gh-pages/data/releases.json', JSON.stringify(relarr));

            return true;
        });

    grunt.registerTask('build', ['clean', 'concat', 'uglify', 'copy:resources']);
    grunt.registerTask('apidoc', ['exec:run_jsdoc2']);
    grunt.registerTask('publish', [
        'exec:prepare_release', 'create_releases_list','exec:commit_release'
    ]);

    grunt.registerTask('default', ['build', 'apidoc']);
    grunt.registerTask('ci', ['default', 'publish']);
};

<commit_msg>travis ci<commit_after>module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        js_dir: "src",
        project_dir: ".",
        ci_branch: process.env.TRAVIS_BRANCH,
        gitinfo: {
        },
        clean: [
            "build", "release"
        ],
        concat: {
            "options": {"separator": "\n"},
            "build": {
                "src": require("./config/js.json"),
                "dest": "build/debug/js/fm-js-all.js"
            }
        },
        uglify: {
            files: {
                src: "build/debug/js/fm-js-all.js",
                dest: "build/production/js/fm-js-all.js"
            }
        },
        copy: {
            resources: {
                files: [
                    {
                        expand: true,
                        src: require("./config/resources.json"),
                        dest: 'build/debug',
                        filter: 'isFile'
                    },
                    {
                        expand: true,
                        src: require("./config/resources.json"),
                        dest: 'build/production',
                        filter: 'isFile'
                    }
                ]
            }
        },
        exec: {
            run_jsdoc2: {
                command: 'mkdir -p build && node node_modules/jsdoc-toolkit/ -r=99 src/ -t=node_modules/jsdoc-toolkit/templates/jsdoc -d=build/apidoc',
                stdout: true
            },
            prepare_release: {
                command: 'bash bin/_prepare_release',
                stdout: true
            },
            commit_release: {
                command: 'bash bin/_commit_release',
                stdout: true
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-exec');

    grunt.registerTask(
        'create_releases_list',
        'Parse JSON table with releases and add new one.',
        function() {
            function getVersion() {
                var version = grunt.config('ci_branch');

                grunt.log.writeln("Version: " + version + "/ " + version.substring(1));

                if (!version || version.substring(0, 1) != 'v') {
                    grunt.log.error('This is not release tag. Abortiong ...');
                    return false;
                }
                var x = version.substring(1).split('.');
                var verArr = {
                    major: parseInt(x[0]),
                    minor: parseInt(x[1]),
                    fix: x[2] || ""
                };
                grunt.log.writeln('Ver array: ' + JSON.stringify(verArr));

                if (isNaN(verArr.major) || isNaN(verArr.minor)) {
                    grunt.log.error('This is not release tag. Abortiong (2) ...');
                    return false;
                }
                verArr.str = '' + verArr.major + "." + verArr.minor + "." + verArr.fix;
                verArr.filename = 'fm-js-' + verArr.str;

                verArr.date = new Date().getTime();
                verArr.relID =
                    verArr.major * 10000000 +
                    verArr.minor * 100000 +
                    (
                        verArr.fix.substring(0, 3) == 'rel' ?
                        (parseInt(verArr.fix.substring(3)) || 0) * 1000 :
                        (
                        verArr.fix.substring(0, 2) == 'rc' ?
                        (parseInt(verArr.fix.substring(2)) || 0) * 10: (parseInt(verArr.fix) || 0)
                        )
                    )
                ;

                return verArr;
            }

            // get git branch name and parse version numbers
            var ver = getVersion();
            if (ver == false) {
                return false;
            }

            grunt.log.writeln('Version info: ' + JSON.stringify(ver));

            // load releases table
            var relarr = grunt.file.readJSON("release/gh-pages/data/releases.json");
            if (!relarr || relarr.constructor.toString().indexOf("Array") == -1) {
                relarr = [];
            }
            grunt.log.writeln('Old release table: ' + JSON.stringify(relarr));
            relObjs = {};
            for (var rel in relarr) {
                relObjs[relarr[rel].relid] = relarr[rel];
            }
            grunt.log.writeln('Old release objects: ' + JSON.stringify(relObjs));

            //modify release table
            if (ver.fix.substring(0, 3) == 'rel') {
                relObjs["99999999"] = {
                    version: "latest stable (recomended download)",
                    relid: 99999999,
                    link: "releases/" + ver.filename + ".zip",
                    doc: "apidoc/" + ver.filename + "/index.html",
                    deployed: rel.date
                };
            } else {
                relObjs["99999990"] = {
                    version: "latest unstable",
                    relid: 99999990,
                    link: "releases/" + ver.filename + ".zip",
                    doc: "apidoc/" + ver.filename + "/index.html",
                    deployed: rel.date
                };
            }
            relObjs['' + ver.relID] = {
                version: ver.str,
                relid: ver.relID,
                link: "releases/" + ver.filename + ".zip",
                doc: "apidoc/" + ver.filename + "/index.html",
                deployed: rel.date
            };
            grunt.log.writeln('New release objects: ' + JSON.stringify(relObjs));

            relarr = [];
            for (var rel in relObjs) {
                relarr.push(relObjs[rel]);
            }
            relarr.sort(function(a, b) {
                return b.relid - a.relid
            });

            // save release table back
            grunt.log.writeln('New release table: ' + JSON.stringify(relarr));
            //grunt.file.write('release/gh-pages/data/releases.json', JSON.stringify(relarr));

            return true;
        });

    grunt.registerTask('build', ['clean', 'concat', 'uglify', 'copy:resources']);
    grunt.registerTask('apidoc', ['exec:run_jsdoc2']);
    grunt.registerTask('publish', [
        'exec:prepare_release', 'create_releases_list','exec:commit_release'
    ]);

    grunt.registerTask('default', ['build', 'apidoc']);
    grunt.registerTask('ci', ['default', 'publish']);
};

<|endoftext|>


<commit_before>import { Router } from 'express'
import bodyParser from 'body-parser'
import Decision from './classes/Decision.js'
var router = Router()

router.use(bodyParser.urlencoded({ extended: true }))

router.post('/createDecision', function(req, res) {
  console.log(req.body)
})

export default router<commit_msg>Add TODO to createDecision route<commit_after>import { Router } from 'express'
import bodyParser from 'body-parser'
import Decision from './classes/Decision.js'
var router = Router()

router.use(bodyParser.urlencoded({ extended: true }))

router.post('/createDecision', function(req, res) {
  // TODO These 4 fields (IUN, Version, unitIds, organizationId) should be set
  // by the current implementation of Diavgeia
  new Decision(req.body,'60Β3ΩΡΙ-ΒΝ3','b4ae1411-f81d-437b-9c63-a8b7d4ed866b',['6105'],'93302').generateN3()
})

export default router<|endoftext|>


<commit_before>'use strict';

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    nodeunit: {
      files: ['test/**/*_test.js'],
    },
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      gruntfile: {
        src: 'Gruntfile.js'
      },
      lib: {
        src: ['lib/**/*.js', '!lib/assets/**/*.js']
      },
      test: {
        src: ['test/**/*.js','!test/fixtures/**/*.js']
      },
      assets: {
        src: ['lib/assets/scripts/*.js']
      }
    },
    uglify: {
      'assets' : {
        files : {
          'lib/assets/scripts/bundles/core-bundle.js' : [
            'lib/assets/scripts/vendor/jquery-1.8.3.min.js',
            'lib/assets/scripts/vendor/lodash.min.js',
            'lib/assets/scripts/vendor/raphael-min.js',
            'lib/assets/scripts/vendor/morris.min.js',
            'lib/assets/scripts/vendor/jquery.fittext.js',
            'lib/assets/scripts/vendor/bootstrap-tooltip.js',
            'lib/assets/scripts/vendor/bootstrap-popover.js'
          ],
          'lib/assets/scripts/bundles/codemirror.js' : [
            'lib/assets/scripts/vendor/codemirror/codemirror.js',
            'lib/assets/scripts/vendor/codemirror/javascript.js',
            'lib/assets/scripts/vendor/codemirror/util/searchcursor.js'
          ]
        }
      },
    },
    watch: {
      gruntfile: {
        files: '<%= jshint.gruntfile.src %>',
        tasks: ['jshint:gruntfile']
      },
      lib: {
        files: '<%= jshint.lib.src %>',
        tasks: ['jshint:lib', 'nodeunit']
      },
      test: {
        files: '<%= jshint.test.src %>',
        tasks: ['jshint:test', 'nodeunit']
      },
    },
    casperjs : {
      test : {
        src: 'test/casper-overview.js',
        options : {
          test : true,
          direct : true
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-casper');

  grunt.registerTask('runtest',function(){
    var done = this.async();

    grunt.util.spawn({
        cmd : './bin/plato',
        args : [
          '-q',
          '-dtmp',
          '-ttest report',
          'test/fixtures/a.js','test/fixtures/b.js','test/fixtures/empty.js'
        ]
      },
      function(err, result, code){
        console.log(result.stdout);
        console.log(result.stderr);
        if (err || code !== 0) {
          grunt.fatal('Running plato binary failed');
        }
        done();
      }
    );
  });

  grunt.registerTask('runbin',function(){
    var done = this.async();

    grunt.util.spawn({
        cmd : './bin/plato',
        args : [
          '-q',
          '-r',
          '-l.jshintrc',
          '-xvendor|bundles',
          '-dreports',
          '-tPlato report',
          'lib/'
        ]
      },
      function(err, result, code){
        console.log(result.stdout);
        if (err || code !== 0) {
          console.log(err);
          grunt.fatal('Running plato binary failed');
        }
        done();
      }
    );
  });

  grunt.registerTask('optimize', ['uglify']);
  // Default task.
  grunt.registerTask('test', ['jshint', 'nodeunit', 'runtest', 'runbin'/*, 'casperjs'*/]);
  grunt.registerTask('default', ['test']);

};
<commit_msg>+ fix minor issues in Gruntfile.js<commit_after>'use strict';

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    nodeunit: {
      files: ['test/**/*_test.js']
    },
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      gruntfile: {
        src: 'Gruntfile.js'
      },
      lib: {
        src: ['lib/**/*.js', '!lib/assets/**/*.js']
      },
      test: {
        src: ['test/**/*.js','!test/fixtures/**/*.js']
      },
      assets: {
        src: ['lib/assets/scripts/*.js']
      }
    },
    uglify: {
      'assets' : {
        files : {
          'lib/assets/scripts/bundles/core-bundle.js' : [
            'lib/assets/scripts/vendor/jquery-1.8.3.min.js',
            'lib/assets/scripts/vendor/lodash.min.js',
            'lib/assets/scripts/vendor/raphael-min.js',
            'lib/assets/scripts/vendor/morris.min.js',
            'lib/assets/scripts/vendor/jquery.fittext.js',
            'lib/assets/scripts/vendor/bootstrap-tooltip.js',
            'lib/assets/scripts/vendor/bootstrap-popover.js'
          ],
          'lib/assets/scripts/bundles/codemirror.js' : [
            'lib/assets/scripts/vendor/codemirror/codemirror.js',
            'lib/assets/scripts/vendor/codemirror/javascript.js',
            'lib/assets/scripts/vendor/codemirror/util/searchcursor.js'
          ]
        }
      }
    },
    watch: {
      gruntfile: {
        files: '<%= jshint.gruntfile.src %>',
        tasks: ['jshint:gruntfile']
      },
      lib: {
        files: '<%= jshint.lib.src %>',
        tasks: ['jshint:lib', 'nodeunit']
      },
      test: {
        files: '<%= jshint.test.src %>',
        tasks: ['jshint:test', 'nodeunit']
      }
    },
    casperjs : {
      test : {
        src: 'test/casper-overview.js',
        options : {
          test : true,
          direct : true
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-casper');

  grunt.registerTask('runtest',function(){
    var done = this.async();

    grunt.util.spawn({
        cmd : './bin/plato',
        args : [
          '-q',
          '-dtmp',
          '-ttest report',
          'test/fixtures/a.js','test/fixtures/b.js','test/fixtures/empty.js'
        ]
      },
      function(err, result, code){
        console.log(result.stdout);
        console.log(result.stderr);
        if (err || code !== 0) {
          grunt.fatal('Running plato binary failed');
        }
        done();
      }
    );
  });

  grunt.registerTask('runbin',function(){
    var done = this.async();

    grunt.util.spawn({
        cmd : './bin/plato',
        args : [
          '-q',
          '-r',
          '-l.jshintrc',
          '-xvendor|bundles',
          '-dreports',
          '-tPlato report',
          'lib/'
        ]
      },
      function(err, result, code){
        console.log(result.stdout);
        if (err || code !== 0) {
          console.log(err);
          grunt.fatal('Running plato binary failed');
        }
        done();
      }
    );
  });

  grunt.registerTask('optimize', ['uglify']);
  // Default task.
  grunt.registerTask('test', ['jshint', 'nodeunit', 'runtest', 'runbin'/*, 'casperjs'*/]);
  grunt.registerTask('default', ['test']);

};
<|endoftext|>


<commit_before>var Promise = require("es6-promise").Promise;
module.exports = function(freedom, provider_url, freedomOpts) {
  var Social, ERRCODE;
  var c1 = null;
  var c2 = null;

  var Helper = {
    makeClientState: function(userId, clientId, status) {
      return {
        userId: userId,
        clientId: clientId,
        status: status,
        lastUpdated: jasmine.any(Number),
        lastSeen: jasmine.any(Number)
      };
    },
    makeUserProfile: function(userId) {
      return jasmine.objectContaining({
        userId: userId,
        lastUpdated: jasmine.any(Number)
      });
    },
    errHandler: function(err) {
      console.error(err);
      expect(err).toBeUndefined();
    }
  };

  beforeEach(function(done) {
    var complete = function() {
      c1 = new Social();
      c2 = new Social();
      ERRCODE = c1.ERRCODE;
      done();
    };
    // Only create a freedom module the first time
    // Fails on Firefox otherwise
    if (typeof Social === "undefined") {
      freedom(provider_url, freedomOpts).then(function(constructor) {
        Social = constructor;
        complete();
      });
    } else {
      complete();
    }
  });

  afterEach(function(done) {
    Social.close(c1);
    Social.close(c2);
    c1 = null;
    c2 = null;
    done();
  });

  it("A-B: sends roster updates through the onChange event.", function(done) {
    var c1State, c2State = null;
    var c1StateEvts = [];
    var c1ProfileEvts = [];
    var ranExpectations = false;

    c1.on("onUserProfile", function(info) {
      c1ProfileEvts.push(info);
    });

    c1.on("onClientState", function(info) {
      c1StateEvts.push(info);
      if (c2State !== null) {
        // Only wanna see statuses from clientB
        c1StateEvts = c1StateEvts.filter(function(elt) {
          return elt.clientId == c2State.clientId;
        });
        //Expect to see ONLINE then OFFLINE from clientB
        if (!ranExpectations && c1StateEvts.length >= 2 ) {
          ranExpectations = true;
          expect(c1ProfileEvts).toContain(Helper.makeUserProfile(c2State.userId));
          expect(c1StateEvts).toContain(Helper.makeClientState(c2State.userId, c2State.clientId, "ONLINE"));
          expect(c1StateEvts).toContain(Helper.makeClientState(c2State.userId, c2State.clientId, "OFFLINE"));
          c1.logout().then(done).catch(Helper.errHandler);
        }
      }
    });

    Promise.all([ c1.login({ agent: "jasmine" }), c2.login({ agent: "jasmine" }) ]).then(function (ret) {
      c1State = ret[0];
      c2State = ret[1];
      return c2.logout();
    }).catch(Helper.errHandler);
  });

  it("A-B: sends message between A->B", function(done) {
    var c1State, c2State;
    var c1StateEvts = [];
    var sent = false;
    var msg = "Hello World-" + Math.random();

    var c2Online = function() {
      for(var i = 0; i < c1StateEvts.length; i++) {
        if (typeof c2State !== "undefined" &&
            c1StateEvts[i].clientId == c2State.clientId &&
            c1StateEvts[i].status == "ONLINE") {
          return true;
        }
      }
      return false;
    };

    var trySend = function() {
      if (!sent && c2Online()) {
        sent = true;
        c1.sendMessage(c2State.clientId, msg).then(function(ret) {
          // Message sent
        }).catch(Helper.errHandler);
      }
    };

    c1.on("onClientState", function(info) {
      c1StateEvts.push(info);
      trySend();
    });

    c2.once("onMessage", function(message) {
      expect(message.from).toEqual(Helper.makeClientState(c1State.userId, c1State.clientId, "ONLINE"));
      expect(message.message).toEqual(msg);
      Promise.all([ c1.logout(), c2.logout() ]).then(done).catch(Helper.errHandler);
    });

    Promise.all([ c1.login({ agent: "jasmine" }), c2.login({ agent: "jasmine" }) ]).then(function (ret) {
      c1State = ret[0];
      c2State = ret[1];
      trySend();
    }).catch(Helper.errHandler);
  });

  it("A-B: can return the roster", function(done) {
    var c1State, c2State;
    var triggered = false;
    var c1ProfileEvts = [];
    var c2ProfileEvts = [];
    var c1StateEvts = [];
    var c2StateEvts = [];

    // Checks if we saw user profiles for both clients
    var containsProfiles = function(evts) {
      var saw1 = false, saw2 = false;
      for (var i = 0; i < evts.length; i++) {
        if (evts[i].userId == c1State.userId) {
          saw1 = true;
        }
        if (evts[i].userId == c2State.userId) {
          saw2 = true;
        }
      }
      return saw1 && saw2;
    };

    // Checks if we saw client states for both clients
    var containsClients = function(evts) {
      var saw1 = false, saw2 = false;
      for (var i = 0; i < evts.length; i++) {
        if (evts[i].clientId == c1State.clientId) {
          saw1 = true;
        }
        if (evts[i].clientId == c2State.clientId) {
          saw2 = true;
        }
      }
      return saw1 && saw2;
    };

    // Triggered on every event, waiting until all necessary events are collected
    var tryGetRoster = function(arr, info) {
      if (typeof arr !== "undefined") {
        arr.push(info);
      }
      if (!triggered &&
          typeof c1State !== "undefined" &&
          typeof c2State !== "undefined" &&
          containsProfiles(c1ProfileEvts) &&
          containsProfiles(c2ProfileEvts) &&
          containsClients(c1StateEvts) &&
          containsClients(c2StateEvts)) {
        triggered = true;
        Promise.all([ c1.getUsers(), c2.getUsers(), c1.getClients(), c2.getClients() ]).then(function(ret) {
          expect(ret[0][c1State.userId]).toEqual(Helper.makeUserProfile(c1State.userId));
          expect(ret[0][c2State.userId]).toEqual(Helper.makeUserProfile(c2State.userId));
          expect(ret[1][c1State.userId]).toEqual(Helper.makeUserProfile(c1State.userId));
          expect(ret[1][c2State.userId]).toEqual(Helper.makeUserProfile(c2State.userId));
          expect(ret[2][c1State.clientId]).toEqual(Helper.makeClientState(c1State.userId, c1State.clientId, "ONLINE"));
          expect(ret[2][c2State.clientId]).toEqual(Helper.makeClientState(c2State.userId, c2State.clientId, "ONLINE"));
          expect(ret[3][c1State.clientId]).toEqual(Helper.makeClientState(c1State.userId, c1State.clientId, "ONLINE"));
          expect(ret[3][c2State.clientId]).toEqual(Helper.makeClientState(c2State.userId, c2State.clientId, "ONLINE"));
          return Promise.all([ c1.logout(), c2.logout() ]);
        }).then(done).catch(Helper.errHandler);
      }
    };

    c1.on("onUserProfile", tryGetRoster.bind({}, c1ProfileEvts));
    c2.on("onUserProfile", tryGetRoster.bind({}, c2ProfileEvts));
    c1.on("onClientState", tryGetRoster.bind({}, c1StateEvts));
    c2.on("onClientState", tryGetRoster.bind({}, c2StateEvts));

    Promise.all([ c1.login({ agent: "jasmine" }),  c2.login({ agent: "jasmine" }) ]).then(function(ret) {
      c1State = ret[0];
      c2State = ret[1];
      tryGetRoster();
    }).catch(Helper.errHandler);
  });

};

<commit_msg>Cleaning up social double integration tests. Ready for review<commit_after>var Promise = require("es6-promise").Promise;
module.exports = function(freedom, provider_url, freedomOpts) {
  var Social, ERRCODE;
  var c1 = null;
  var c2 = null;

  var Helper = {
    makeClientState: function(userId, clientId, status) {
      return {
        userId: userId,
        clientId: clientId,
        status: status,
        lastUpdated: jasmine.any(Number),
        lastSeen: jasmine.any(Number)
      };
    },
    makeUserProfile: function(userId) {
      return jasmine.objectContaining({
        userId: userId,
        lastUpdated: jasmine.any(Number)
      });
    },
    errHandler: function(err) {
      console.error(err);
      expect(err).toBeUndefined();
    }
  };

  beforeEach(function(done) {
    var complete = function() {
      c1 = new Social();
      c2 = new Social();
      ERRCODE = c1.ERRCODE;
      done();
    };
    // Only create a freedom module the first time
    // Fails on Firefox otherwise
    if (typeof Social === "undefined") {
      freedom(provider_url, freedomOpts).then(function(constructor) {
        Social = constructor;
        complete();
      });
    } else {
      complete();
    }
  });

  afterEach(function(done) {
    Social.close(c1);
    Social.close(c2);
    c1 = null;
    c2 = null;
    done();
  });

  it("A-B: sends roster updates through the onChange event.", function(done) {
    var c1State, c2State = null;
    var c1StateEvts = [];
    var c1ProfileEvts = [];
    var ranExpectations = false;

    c1.on("onUserProfile", function(info) {
      c1ProfileEvts.push(info);
    });

    c1.on("onClientState", function(info) {
      c1StateEvts.push(info);
      if (c2State !== null) {
        // Only wanna see statuses from clientB
        c1StateEvts = c1StateEvts.filter(function(elt) {
          return elt.clientId == c2State.clientId;
        });
        //Expect to see ONLINE then OFFLINE from clientB
        if (!ranExpectations && c1StateEvts.length >= 2 ) {
          ranExpectations = true;
          expect(c1ProfileEvts).toContain(Helper.makeUserProfile(c2State.userId));
          expect(c1StateEvts).toContain(Helper.makeClientState(c2State.userId, c2State.clientId, "ONLINE"));
          expect(c1StateEvts).toContain(Helper.makeClientState(c2State.userId, c2State.clientId, "OFFLINE"));
          c1.logout().then(done).catch(Helper.errHandler);
        }
      }
    });

    Promise.all([ c1.login({ agent: "jasmine" }), c2.login({ agent: "jasmine" }) ]).then(function (ret) {
      c1State = ret[0];
      c2State = ret[1];
      return c2.logout();
    }).catch(Helper.errHandler);
  });

  it("A-B: sends message between A->B", function(done) {
    var c1State, c2State;
    var c1StateEvts = [];
    var sent = false;
    var msg = "Hello World-" + Math.random();

    var c2Online = function() {
      for(var i = 0; i < c1StateEvts.length; i++) {
        if (typeof c2State !== "undefined" &&
            c1StateEvts[i].clientId == c2State.clientId &&
            c1StateEvts[i].status == "ONLINE") {
          return true;
        }
      }
      return false;
    };

    var trySend = function() {
      if (!sent && c2Online()) {
        sent = true;
        c1.sendMessage(c2State.clientId, msg).then(function(ret) {
          // Message sent
        }).catch(Helper.errHandler);
      }
    };

    c1.on("onClientState", function(info) {
      c1StateEvts.push(info);
      trySend();
    });

    c2.once("onMessage", function(message) {
      expect(message.from).toEqual(Helper.makeClientState(c1State.userId, c1State.clientId, "ONLINE"));
      expect(message.message).toEqual(msg);
      Promise.all([ c1.logout(), c2.logout() ]).then(done).catch(Helper.errHandler);
    });

    Promise.all([ c1.login({ agent: "jasmine" }), c2.login({ agent: "jasmine" }) ]).then(function (ret) {
      c1State = ret[0];
      c2State = ret[1];
      trySend();
    }).catch(Helper.errHandler);
  });

  it("A-B: can return the roster", function(done) {
    var c1State, c2State;
    var triggered = false;
    var c1ProfileEvts = [];
    var c2ProfileEvts = [];
    var c1StateEvts = [];
    var c2StateEvts = [];

    // Checks if we see both users for a set of events
    var seeBoth = function(evts, key) {
      var saw1 = false, saw2 = false;
      for (var i = 0; i < evts.length; i++) {
        if (typeof c1State !== "undefined" && evts[i][key] == c1State[key]) {
          saw1 = true;
        }
        if (typeof c2State !== "undefined" && evts[i][key] == c2State[key]) {
          saw2 = true;
        }
      }
      return saw1 && saw2;
    };

    // Triggered on every event, waiting until all necessary events are collected
    var tryGetRoster = function(arr, info) {
      if (typeof arr !== "undefined") {
        arr.push(info);
      }
      if (!triggered &&
          seeBoth(c1ProfileEvts, "userId") && seeBoth(c2ProfileEvts, "userId") &&
          seeBoth(c1StateEvts, "clientId") && seeBoth(c2StateEvts, "clientId")) {
        triggered = true;
        Promise.all([ c1.getUsers(), c2.getUsers(), c1.getClients(), c2.getClients() ]).then(function(ret) {
          expect(ret[0][c1State.userId]).toEqual(Helper.makeUserProfile(c1State.userId));
          expect(ret[0][c2State.userId]).toEqual(Helper.makeUserProfile(c2State.userId));
          expect(ret[1][c1State.userId]).toEqual(Helper.makeUserProfile(c1State.userId));
          expect(ret[1][c2State.userId]).toEqual(Helper.makeUserProfile(c2State.userId));
          expect(ret[2][c1State.clientId]).toEqual(Helper.makeClientState(c1State.userId, c1State.clientId, "ONLINE"));
          expect(ret[2][c2State.clientId]).toEqual(Helper.makeClientState(c2State.userId, c2State.clientId, "ONLINE"));
          expect(ret[3][c1State.clientId]).toEqual(Helper.makeClientState(c1State.userId, c1State.clientId, "ONLINE"));
          expect(ret[3][c2State.clientId]).toEqual(Helper.makeClientState(c2State.userId, c2State.clientId, "ONLINE"));
          return Promise.all([ c1.logout(), c2.logout() ]);
        }).then(done).catch(Helper.errHandler);
      }
    };

    c1.on("onUserProfile", tryGetRoster.bind({}, c1ProfileEvts));
    c2.on("onUserProfile", tryGetRoster.bind({}, c2ProfileEvts));
    c1.on("onClientState", tryGetRoster.bind({}, c1StateEvts));
    c2.on("onClientState", tryGetRoster.bind({}, c2StateEvts));

    Promise.all([ c1.login({ agent: "jasmine" }),  c2.login({ agent: "jasmine" }) ]).then(function(ret) {
      c1State = ret[0];
      c2State = ret[1];
      tryGetRoster();
    }).catch(Helper.errHandler);
  });

};

<|endoftext|>


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

'use strict';

const Promise = require('bluebird'),
      Friend = require('../models/friend'),
      notification = require('../lib/notification');

const EPOCH_DATE = new Date(1);

// TBD: Instead of FRIENDS and FRIENDSUPDATE, use ADDFRIENDS

exports.sendFriends = async function(user, sessionId) {
    sendFriends(user, sessionId);
};

exports.sendFriendConfirm = async function(user, sessionId) {
    const friendAsDst = await Friend.find({ dstUserId: user.id });

    const friendUsers = friendAsDst.filter(record => record.get('state') === 'pending');

    if (friendUsers.length > 0) {
        // Uses userId property so that related USERS notification is send automatically
        // See lib/notification.js for the details.
        await notification.send(user, sessionId, {
            id: 'FRIENDSCONFIRM',
            friends: friendUsers.map(friendUser => ({ userId: friendUser.gId }))
        });
    }
};

exports.informStateChange = async function(user, eventType) {
    let command = {
        id: 'FRIENDS',
        reset: false,
        friends: [ {
            userId: user.id,
            online: eventType === 'login'
        } ]
    };

    // Zero means the user is currently online
    let ts = 0;

    if (eventType !== 'login') {
        ts = Date.now();
        command.friends[0].last = Math.round(ts / 1000);
    }

    await user.set('lastLogout', ts);

    const friendUsers = await getFriendUsers(user);

    for (let friendUser of friendUsers) {
        await notification.broadcast(friendUser, command);
    }
};

exports.createPending = async function(user, friendUser) {
    await Friend.create({
        srcUserId: user.id,
        dstUserId: friendUser.id,
        state: 'asking'
    });

    await Friend.create({
        srcUserId: friendUser.id,
        dstUserId: user.id,
        state: 'pending'
    });
};

exports.activateFriends = async function(user, friendUser) {
    const srcFriend = await Friend.findFirst({
        srcUserId: user.id,
        dstUserId: friendUser.id
    })

    const dstFriend = await Friend.findFirst({
        srcUserId: friendUser.id,
        dstUserId: user.id
    })

    await srcFriend.set({ state: 'active' });
    await dstFriend.set({ state: 'active' });

    // Inform both parties
    await sendFriends(user);
    await sendFriends(friendUser);
}

exports.removeFriends = async function(user, friendUser) {
    const srcFriend = await Friend.findFirst({
        srcUserId: user.id,
        dstUserId: friendUser.id
    })

    const dstFriend = await Friend.findFirst({
        srcUserId: friendUser.id,
        dstUserId: user.id
    })

    await srcFriend.delete();
    await dstFriend.delete();
}

exports.removeUser = async function(user) {
    const [ friendAsSrc, friendAsDst ] = await Promise.all([
        Friend.find({ srcUserId: user.id }),
        Friend.find({ dstUserId: user.id })
    ]);

    const friends = friendAsSrc.concat(friendAsDst);

    for (let friend of friends) {
        await friend.delete();
    }
};

async function sendFriends(user, sessionId) {
    const command = {
        id: 'FRIENDS',
        reset: true,
        friends: []
    };

    const friendUsers = await getFriendUsers(user);

    for (let friendUser of friendUsers) {
        const last = friendUser.get('lastLogout');
        const online = last === null;

        const friendData = {
            userId: `m${friendUser.id}`,
            online: online
        };

        if (!online) {
            friendData.last = last < EPOCH_DATE ? -1 : Math.floor(last.getTime() / 1000);
        }

        command.friends.push(friendData);
    }

    if (sessionId) {
        await notification.send(user, sessionId, command);
    } else {
        await notification.broadcast(user, command);
    }
}

async function getFriendUsers(user) {
    const [ friendAsSrc, friendAsDst ] = await Promise.all([
        Friend.find({ srcUserId: user.id }),
        Friend.find({ dstUserId: user.id })
    ]);

    return friendAsSrc.concat(friendAsDst).filter(record => record.get('state') === 'active');
}
<commit_msg>Friends service fixes<commit_after>//
//   Copyright 2014 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

'use strict';

const Promise = require('bluebird'),
      UserGId = require('../models/userGId'),
      User = require('../models/user'),
      Friend = require('../models/friend'),
      notification = require('../lib/notification');

const EPOCH_DATE = new Date(1);

// TBD: Instead of FRIENDS and FRIENDSUPDATE, use ADDFRIENDS

exports.sendFriends = async function(user, sessionId) {
    sendFriends(user, sessionId);
};

exports.sendFriendConfirm = async function(user, sessionId) {
    const friendAsDst = await Friend.find({ srcUserId: user.id });
    const friendUsers = friendAsDst.filter(friend => friend.get('state') === 'pending');

    if (friendUsers.length > 0) {
        // Uses userId property so that related USERS notification is send automatically
        // See lib/notification.js for the details.
        const ntf = {
            id: 'FRIENDSCONFIRM',
            friends: friendUsers.map(friendUser => {
                const friendUserGId = UserGId.create({
                    id: friendUser.get('dstUserId'),
                    type: 'mas'
                });

                return { userId: friendUserGId.toString() }
            })
        };

        if (sessionId) {
            await notification.send(user, sessionId, ntf);
        } else {
            await notification.broadcast(user, ntf);
        }
    }
};

exports.informStateChange = async function(user, eventType) {
    let command = {
        id: 'FRIENDS',
        reset: false,
        friends: [ {
            userId: user.id,
            online: eventType === 'login'
        } ]
    };

    // null means the user is currently online
    let ts = null;

    if (eventType !== 'login') {
        ts = Date.now();
        command.friends[0].last = Math.round(ts / 1000);
    }

    await user.set('lastLogout', ts);

    const friendUsers = await getFriendUsers(user);

    for (let friendUser of friendUsers) {
        await notification.broadcast(friendUser, command);
    }
};

exports.createPending = async function(user, friendUser) {
    await Friend.create({
        srcUserId: user.id,
        dstUserId: friendUser.id,
        state: 'asking'
    });

    await Friend.create({
        srcUserId: friendUser.id,
        dstUserId: user.id,
        state: 'pending'
    });
};

exports.activateFriends = async function(user, friendUser) {
    const srcFriend = await Friend.findFirst({
        srcUserId: user.id,
        dstUserId: friendUser.id
    })

    const dstFriend = await Friend.findFirst({
        srcUserId: friendUser.id,
        dstUserId: user.id
    })

    await srcFriend.set({ state: 'active' });
    await dstFriend.set({ state: 'active' });

    // Inform both parties
    await sendFriends(user);
    await sendFriends(friendUser);
}

exports.removeFriends = async function(user, friendUser) {
    const srcFriend = await Friend.findFirst({
        srcUserId: user.id,
        dstUserId: friendUser.id
    })

    const dstFriend = await Friend.findFirst({
        srcUserId: friendUser.id,
        dstUserId: user.id
    })

    await srcFriend.delete();
    await dstFriend.delete();
}

exports.removeUser = async function(user) {
    const [ friendAsSrc, friendAsDst ] = await Promise.all([
        Friend.find({ srcUserId: user.id }),
        Friend.find({ dstUserId: user.id })
    ]);

    const friends = friendAsSrc.concat(friendAsDst);

    for (let friend of friends) {
        await friend.delete();
    }
};

async function sendFriends(user, sessionId) {
    const command = {
        id: 'FRIENDS',
        reset: true,
        friends: []
    };

    const friendUsers = await getFriendUsers(user);

    for (let friendUser of friendUsers) {
        const last = friendUser.get('lastLogout');
        const online = last === null;

        const friendData = {
            userId: `m${friendUser.id}`,
            online: online
        };

        if (!online) {
            friendData.last = last < EPOCH_DATE ? -1 : Math.floor(last.getTime() / 1000);
        }

        command.friends.push(friendData);
    }

    if (sessionId) {
        await notification.send(user, sessionId, command);
    } else {
        await notification.broadcast(user, command);
    }
}

async function getFriendUsers(user) {
    const friends = await Friend.find({ srcUserId: user.id });
    const activeFriends = friends.filter(friend => friend.get('state') === 'active');
    const friendUsers = [];

    for (const friend of activeFriends) {
        friendUsers.push(await User.fetch(friend.get('dstUserId')));
    }

    return friendUsers;
}
<|endoftext|>


<commit_before>angular.module('zeus.services', [])
.factory('Details', function($http) {
  var getReviews = function (type, id) {
    return $http({
      method: 'GET',
      url: '/review/' + type + '/' + id
    })
    .then(function (res) {
      return res;
    });
  };
  var getDetails = function (type, id, callback) {
    return $http({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/' + type + '/' + id + '?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    }).then(function (res) {
      return res.data;
    });
  };
  var postReview = function (type, id, info) {
    return $http({
      method: 'POST',
      url: '/review/' + type + '/' + id,
      data: info
    })
    .then(function(res) {
      return res;
    });
  };

  return {
    getDetails: getDetails,
    getReviews: getReviews,
    postReview: postReview
  };

})

.factory('Landing', function($http) {
  var getPopularMovies = function() {
    return $http({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/movie/popular?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    })
    .then(function(res) {
      return res.data;
    });
  };

  var getLatestMovies = function() {
    return $http({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/movie/now_playing?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    })
    .then(function(res) {
      return res.data;
    });
  };

  var getUpcomingMovies = function() {
    return $http({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/movie/upcoming?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    })
    .then(function(res) {
      return res.data;
    });
  };

  var getPopularShows = function() {
    return $http ({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/tv/popular?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    })
    .then(function(res) {
      return res.data;
    });
  };
  var getLatestShows = function() {
    return $http ({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/tv/airing_today?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    })
    .then(function(res) {
      return res.data;
    });
  };

  return {
    getPopularMovies: getPopularMovies,
    getLatestMovies: getLatestMovies,
    getUpcomingMovies: getUpcomingMovies,
    getPopularShows: getPopularShows,
    getLatestShows: getLatestShows
  };

})
.factory('Results', function($http) {
  var multiSearch = function(query, page) {
    return $http({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/search/multi?api_key=144a52aa180019a468d95822c036cbce&language=en-US&query=' + query + '&page=' + page
    })
    .then(function(res) {
      return res;
    });
  };

  return {
    multiSearch: multiSearch
  };

});<commit_msg>- Adds empty factory for User<commit_after>angular.module('zeus.services', [])
.factory('Details', function($http) {
  var getReviews = function (type, id) {
    return $http({
      method: 'GET',
      url: '/review/' + type + '/' + id
    })
    .then(function (res) {
      return res;
    });
  };
  var getDetails = function (type, id, callback) {
    return $http({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/' + type + '/' + id + '?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    }).then(function (res) {
      return res.data;
    });
  };
  var postReview = function (type, id, info) {
    return $http({
      method: 'POST',
      url: '/review/' + type + '/' + id,
      data: info
    })
    .then(function(res) {
      return res;
    });
  };

  return {
    getDetails: getDetails,
    getReviews: getReviews,
    postReview: postReview
  };
})

.factory('Landing', function($http) {
  var getPopularMovies = function() {
    return $http({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/movie/popular?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    })
    .then(function(res) {
      return res.data;
    });
  };

  var getLatestMovies = function() {
    return $http({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/movie/now_playing?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    })
    .then(function(res) {
      return res.data;
    });
  };

  var getUpcomingMovies = function() {
    return $http({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/movie/upcoming?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    })
    .then(function(res) {
      return res.data;
    });
  };

  var getPopularShows = function() {
    return $http ({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/tv/popular?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    })
    .then(function(res) {
      return res.data;
    });
  };
  var getLatestShows = function() {
    return $http ({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/tv/airing_today?api_key=144a52aa180019a468d95822c036cbce&language=en-US'
    })
    .then(function(res) {
      return res.data;
    });
  };

  return {
    getPopularMovies: getPopularMovies,
    getLatestMovies: getLatestMovies,
    getUpcomingMovies: getUpcomingMovies,
    getPopularShows: getPopularShows,
    getLatestShows: getLatestShows
  };
})

.factory('Results', function($http) {
  var multiSearch = function(query, page) {
    return $http({
      method: 'GET',
      url: 'https://api.themoviedb.org/3/search/multi?api_key=144a52aa180019a468d95822c036cbce&language=en-US&query=' + query + '&page=' + page
    })
    .then(function(res) {
      return res;
    });
  };

  return {
    multiSearch: multiSearch
  };
});

.factory('User', function() {

  return {

  }
})<|endoftext|>