<commit_before>/**
 * # Channel settings
 * Copyright(c) {YEAR} {AUTHOR} <{AUTHOR_EMAIL}>
 * MIT Licensed
 *
 * The channel is divided into two internal servers: player and admin.
 * Each of those grants different privileges upon connection.
 *
 * Player and admin server share all options specified here. If custom
 * options for each server are needed, they can be specified inside the
 * `playerServer` and `adminServer` properties.
 *
 * Each server must define an 'endpoint' to which nodeGame clients
 * can connect. The default server endpoints are:
 *
 *   - player server: name of the game,
 *   - admin server: name of the game /admin
 *
 * http://www.nodegame.org
 * ---
 */
module.exports = {

    /**
     * ## name (string) Optional
     *
     * The name of the channel
     *
     * Default: the name of the game, as found in the package.json file.
     */
    // name: '{NAME}',

    /**
     * ## alias (string|array) Optional
     *
     * Alternative name/s for the channel
     *
     * By default, if 'gameName' is the name of the channel, files will
     * be served from the address: `http://myserver/gameName/`.
     * Here you can add aliases to enable urls like: `http://myserver/alias/`.
     *
     * Important! `node.connect()` in `public/js/index.js` still needs
     * to use the real channel name, so you will need to pass it explicitly:
     * `node.connect('/gameName').
     */
    // alias: [],

    /**
     * ## playerServer (object|string) Optional
     *
     * Set of custom options applying only to player server
     *
     * If string, it will be interpreted as the name oof the server
     * endpoint for socket.io player connections.
     *
     * If object, the endpoint must be specified in the _endpoint_ property.
     *
     * Default: name-of-the-channel
     */
    playerServer: {

        endpoint: '{NAME}',

        // Anti-spoofing, extra check to see if msg.from matches socket.id
        // on SocketIo socket connections. Spoofed messages are logged
        // normally, and an additional log entry with id and from msg is added.
        // Disable only if you need that extra bit of speed.
        antiSpoofing: true
    },

    /**
     * ## adminServer (object|string) Optional
     *
     * Set of custom options applying only to admin server
     *
     * If string, it will be interpreted as the name oof the server
     * endpoint for socket.io admin connections.
     *
     * If object, the endpoint must be specified in the _endpoint_ property.
     *
     * Default: name-of-the-channel/admin
     */
    // adminServer: '{ADMIN}',

    /**
     * ## getFromAdmins (boolean) Optional
     *
     * If TRUE, players can invoke GET commands on admins
     *
     * Default: FALSE
     */
    getFromAdmins: true,

    /**
     * ## accessDeniedUrl (string) Optional
     *
     * Unauthorized clients will be redirected here.
     *
     * Default: "/pages/accessdenied.htm"
     */
    // accessDeniedUrl: 'unauth.htm',

    /**
     * ## notify (object) Optional
     *
     * Configuration options controlling what events are notified to players
     *
     * Default: player actions are notified to admins only.
     */
    notify: {

        // When a player connects / disconnects.
        onConnect: false,

        // When a player changes a stage / step.
        onStageUpdate: false,

        // When the 'LOADED' stageLevel is fired (useful to sync players)
        onStageLoadedUpdate: false,

        // When any change of stageLevel happens (e.g. INIT, CALLBACK_EXECUTED)
        // Notice: generates a lot of overhead of messages.
        onStageLevelUpdate: false,

    },

    /**
     * ## enableReconnections (boolean) Optional
     *
     * If TRUE, only one TAB per browser will be allowed
     *
     * Default: FALSE
     */
    enableReconnections: true,

    /**
     * ### sameStepReconnectionOnly (boolean) Optional
     *
     * If TRUE, only reconnections in the same game step are allowed.
     *
     * Default: FALSE
     */
    // sameStepReconnectionOnly: false,

    /**
     * ### disposeFailedReconnections (boolean) Optional
     *
     * If TRUE, failed reconnections are disposed
     *
     * If FALSE, failed reconnections are treated as a new connection.
     *
     * A reconnection can fail for the following reasones:
     *
     * - Parameter `enabledReconnections` is FALSE.
     * - Parameter `sameStepReconnectionOnly` is TRUE, and the
     *      client's stage and the logic's stage are different.
     * - The room in which the client was at the moment of
     *      disconnection cannot be located.
     * - Any other error.
     *
     * Default: FALSE
     */
    // disposeFailedReconnections: true,

    /**
     * ### cacheMaxAge (number) Optional
     *
     * The duration in ms of the browser cache for public/ resources
     *
     * Default: 0 (no cache)
     */
    // cacheMaxAge: 360000,

    /**
     * ### sioQuery (boolean) Optional
     *
     * If TRUE, clients connecting via Socket.io can set own parameters
     *
     * Available parameters:
     *
     *  - clientType: sets the client type
     *  - startingRoom: sets the room in which the client will be placed first
     *
     * It is recommended to disable sioQuery in production
     *
     * Default: TRUE
     */
    // sioQuery: false,

    /**
     * ### defaultChannel (boolean) Optional
     *
     * If TRUE, the game is served from / (instead of /gamename/)
     *
     * The route `/gamename` will be disabled, while aliases,
     * if defined, will continue to work if not shadowed by any public path.
     *
     * Important! Socket.io connection must still be established
     * with the right endpoint (e.g. /channelName).
     *
     * Important! Other games might not be reachable any more.
     *
     * Important! Server info query will be disabled.
     *
     * Default: false
     */
    // defaultChannel: false,

    /**
     * ### noAuthCookie (boolean) Optional
     *
     * If TRUE, a cookie is set even with authorization disabled
     *
     * Opening multiple browser tabs will cause a disconnection in other ones.
     *
     * Default: false
     */
    // noAuthCookie: false,

    /**
     * ### roomOwnDataDir (boolean) Optional
     *
     * If TRUE, each new room in the channel has an own data dir named after it
     *
     * Default: true
     */
    // roomOwnDataDir: true,

    /**
     * ### roomCounter (number) Optional
     *
     * If set, room counter is initialized to this value
     *
     * If undefined, room counter self-initialize to the next available id,
     * starting from 1, as found in the data folder of the game.
     *
     * Default: undefined
     */
    // roomCounter: 100,

    /**
     * ### roomCounterPadChars (0 <= number <= 12) Optional
     *
     * If set, leading 0 are added to the room counter to reach desired length
     *
     * For example, if `roomCounterChars` is equal to 6 and
     * the current roomCounter value is 123, then room name is: '000123'.
     *
     * Default: 6
     */
    // roomCounterPadChars: 6

    // Reserved for future versions:

    /**
     * ### roomCounterSeparator (character) Optional
     *
     * If set, this char is inserted between the padded room counter and 'room'
     *
     * Default: ''
     */
    // roomCounterSeparator: '@'

};
<commit_msg>logClients settings<commit_after>/**
 * # Channel settings
 * Copyright(c) {YEAR} {AUTHOR} <{AUTHOR_EMAIL}>
 * MIT Licensed
 *
 * The channel is divided into two internal servers: player and admin.
 * Each of those grants different privileges upon connection.
 *
 * Player and admin server share all options specified here. If custom
 * options for each server are needed, they can be specified inside the
 * `playerServer` and `adminServer` properties.
 *
 * Each server must define an 'endpoint' to which nodeGame clients
 * can connect. The default server endpoints are:
 *
 *   - player server: name of the game,
 *   - admin server: name of the game /admin
 *
 * http://www.nodegame.org
 * ---
 */
module.exports = {

    /**
     * ## name (string) Optional
     *
     * The name of the channel
     *
     * Default: the name of the game, as found in the package.json file.
     */
    // name: '{NAME}',

    /**
     * ## alias (string|array) Optional
     *
     * Alternative name/s for the channel
     *
     * By default, if 'gameName' is the name of the channel, files will
     * be served from the address: `http://myserver/gameName/`.
     * Here you can add aliases to enable urls like: `http://myserver/alias/`.
     *
     * Important! `node.connect()` in `public/js/index.js` still needs
     * to use the real channel name, so you will need to pass it explicitly:
     * `node.connect('/gameName').
     */
    // alias: [],

    /**
     * ## playerServer (object|string) Optional
     *
     * Set of custom options applying only to player server
     *
     * If string, it will be interpreted as the name oof the server
     * endpoint for socket.io player connections.
     *
     * If object, the endpoint must be specified in the _endpoint_ property.
     *
     * Default: name-of-the-channel
     */
    playerServer: {

        endpoint: '{NAME}',

        // Anti-spoofing, extra check to see if msg.from matches socket.id
        // on SocketIo socket connections. Spoofed messages are logged
        // normally, and an additional log entry with id and from msg is added.
        // Disable only if you need that extra bit of speed.
        antiSpoofing: true
    },

    /**
     * ## adminServer (object|string) Optional
     *
     * Set of custom options applying only to admin server
     *
     * If string, it will be interpreted as the name oof the server
     * endpoint for socket.io admin connections.
     *
     * If object, the endpoint must be specified in the _endpoint_ property.
     *
     * Default: name-of-the-channel/admin
     */
    // adminServer: '{ADMIN}',

    /**
     * ## getFromAdmins (boolean) Optional
     *
     * If TRUE, players can invoke GET commands on admins
     *
     * Default: FALSE
     */
    getFromAdmins: true,

    /**
     * ## accessDeniedUrl (string) Optional
     *
     * Unauthorized clients will be redirected here.
     *
     * Default: "/pages/accessdenied.htm"
     */
    // accessDeniedUrl: 'unauth.htm',

    /**
     * ## notify (object) Optional
     *
     * Configuration options controlling what events are notified to players
     *
     * Default: player actions are notified to admins only.
     */
    notify: {

        // When a player connects / disconnects.
        onConnect: false,

        // When a player changes a stage / step.
        onStageUpdate: false,

        // When the 'LOADED' stageLevel is fired (useful to sync players)
        onStageLoadedUpdate: false,

        // When any change of stageLevel happens (e.g. INIT, CALLBACK_EXECUTED)
        // Notice: generates a lot of overhead of messages.
        onStageLevelUpdate: false,

    },

    /**
     * ## enableReconnections (boolean) Optional
     *
     * If TRUE, only one TAB per browser will be allowed
     *
     * Default: FALSE
     */
    enableReconnections: true,

    /**
     * ### sameStepReconnectionOnly (boolean) Optional
     *
     * If TRUE, only reconnections in the same game step are allowed.
     *
     * Default: FALSE
     */
    // sameStepReconnectionOnly: false,

    /**
     * ### disposeFailedReconnections (boolean) Optional
     *
     * If TRUE, failed reconnections are disposed
     *
     * If FALSE, failed reconnections are treated as a new connection.
     *
     * A reconnection can fail for the following reasones:
     *
     * - Parameter `enabledReconnections` is FALSE.
     * - Parameter `sameStepReconnectionOnly` is TRUE, and the
     *      client's stage and the logic's stage are different.
     * - The room in which the client was at the moment of
     *      disconnection cannot be located.
     * - Any other error.
     *
     * Default: FALSE
     */
    // disposeFailedReconnections: true,

    /**
     * ### cacheMaxAge (number) Optional
     *
     * The duration in ms of the browser cache for public/ resources
     *
     * Default: 0 (no cache)
     */
    // cacheMaxAge: 360000,

    /**
     * ### sioQuery (boolean) Optional
     *
     * If TRUE, clients connecting via Socket.io can set own parameters
     *
     * Available parameters:
     *
     *  - clientType: sets the client type
     *  - startingRoom: sets the room in which the client will be placed first
     *
     * It is recommended to disable sioQuery in production
     *
     * Default: TRUE
     */
    // sioQuery: false,

    /**
     * ### defaultChannel (boolean) Optional
     *
     * If TRUE, the game is served from / (instead of /gamename/)
     *
     * The route `/gamename` will be disabled, while aliases,
     * if defined, will continue to work if not shadowed by any public path.
     *
     * Important! Socket.io connection must still be established
     * with the right endpoint (e.g. /channelName).
     *
     * Important! Other games might not be reachable any more.
     *
     * Important! Server info query will be disabled.
     *
     * Default: false
     */
    // defaultChannel: false,

    /**
     * ### noAuthCookie (boolean) Optional
     *
     * If TRUE, a cookie is set even with authorization disabled
     *
     * Opening multiple browser tabs will cause a disconnection in other ones.
     *
     * Default: false
     */
    // noAuthCookie: false,

    /**
     * ### roomOwnDataDir (boolean) Optional
     *
     * If TRUE, each new room in the channel has an own data dir named after it
     *
     * Default: true
     */
    // roomOwnDataDir: true,

    /**
     * ### roomCounter (number) Optional
     *
     * If set, room counter is initialized to this value
     *
     * If undefined, room counter self-initialize to the next available id,
     * starting from 1, as found in the data folder of the game.
     *
     * Default: undefined
     */
    // roomCounter: 100,

    /**
     * ### roomCounterPadChars (0 <= number <= 12) Optional
     *
     * If set, leading 0 are added to the room counter to reach desired length
     *
     * For example, if `roomCounterChars` is equal to 6 and
     * the current roomCounter value is 123, then room name is: '000123'.
     *
     * Default: 6
     */
    // roomCounterPadChars: 6

    // Reserved for future versions:

    /**
     * ### roomCounterSeparator (character) Optional
     *
     * If set, this char is inserted between the padded room counter and 'room'
     *
     * Default: ''
     */
    // roomCounterSeparator: '@'

    /**
     * ## logClients
     *
     * If TRUE, all connected/disconnected clients are logged to a csv file
     *
     * Default: FALSE
     */
    // logClients: true,

    /**
     * ## logClientsExtra
     *
     * Adds additional fields to the file of logged clients
     *
     * Default: undefined
     */
    // logClientsExtra: function(p) {
    //     return [ p.WorkerId || 'NA', p.HITId || 'NA',
    //              p.AssignmentId || 'NA', p.ExitCode || 'NA' ];
    // },

    /**
    * ## logClientsInterval
    *
    * How often (in milliseconds) data about clients is written to file system
    *
    * Default: 10000
    */
    // logClientsInterval: 10000,
};
<|endoftext|>


<commit_before>// StatGraph

// A graph will have to know about the challenge rating on the page.

// The maximum / minimum should change every time the CR gets changed,
// but the stat curve (ratio) should remain the same.

// Graph object
function Graph()
{
	// Attributes of the graph
	this.NUM_COLUMNS = 10,
	this.maximum = 1, // arbitrary min & max for now
	this.minimum = 0,

	// The graph, when initialized, should take a div and transform it into an
	// editable bar graph.
	this.init = function()
	{
		console.log("Graph initialized");
	},

	this.onclick = function()
	{

	},

};<commit_msg> Added mouse function stubs to statgraph.js<commit_after>// StatGraph

// A graph will have to know about the challenge rating on the page.

// The maximum / minimum should change every time the CR gets changed,
// but the stat curve (ratio) should remain the same.

// Graph object
function Graph()
{
	// Attributes of the graph
	this.NUM_COLUMNS = 10;

	this.isMouseDown = false;

	this.init = function()
	{
		console.log("Graph created.");
	};

	// --------------------
	// Mouse Functions
	// --------------------
	$(this).mousedown(function()
		{
			// On mousedown, the graph checks for which bar the mouse is over and
			// sets the y position of that bar to the next approximate height.
			isMouseDown = true;
		});
	$(this).mousemove(function()
		{
			// On mousemove, if the mouse is down, do mousedown events for each bar, changing
			// height as the mouse is dragged.
		});
	$(this).mouseup(function()
		{
			// Reset the graph variable that says the mouse is down.
		});

};<|endoftext|>


<commit_before>
// vim: set ts=4 sw=4 et:
"use strict";

function Zone( p_width, p_height ) {
    var self = this;

    this.width = p_width;
    this.height = p_height;

    var matrix = [];

    console.log("zone/initialize width = %s", this.width );
    console.log("zone/initialize height = %s", this.height );

    /*
     * Set matrix position to given color
     */
    this.pixel_set = function( pos, color ) {
        var idx = Math.floor( pos.y ) * self.width + Math.floor(pos.x);
        console.log("zone/pixel_set idx = %s", idx );
        matrix[idx] = color;
    };

    /*
     * Get color at given matrix position
     */
    this.pixel_get = function( pos ) {
        var idx = Math.floor( pos.y ) * self.width + Math.floor( pos.x );
        console.log("zone/pixel_get idx = %s", idx );
        return matrix[idx];
    };

    /*
     * Return whethe given position is inside or outside zone
     */
    this.is_bound = function( pos ) {
        if ( pos.x < 0 ) return false;
        if ( pos.x >= self.width ) return false;
        if ( pos.y < 0 ) return false;
        if ( pos.y >= self.height ) return false;
        return true;
    }

    /*
     * Utility function for debugging
     */
    this.to_s = function() { JSON.stringify(this); };

}

<commit_msg>js zone: * Initialize default matrix to ZONE_BACKGROUND_COLOR ; * Maintain matrix as pixels a set (closes issue #163) ; * Maintain local, remote & current patches ; * Added patch_record & patch_enqueue methods.<commit_after>
// vim: set ts=4 sw=4 et:
"use strict";

const ZONE_BACKGROUND_COLOR = '#000';

function Zone( p_width, p_height ) {
    var self = this;

    // zone dimensions
    this.width = p_width;
    this.height = p_height;

    // color matrix (for maintaining state localy)
    var _matrix = [];

    // patches to send
    var _local_patches = [];

    // the patch we are working on
    var _current_patch = null;

    // patches to apply localy
    var _remote_patches = [];

    // console.log("zone/initialize width = %s", this.width );
    // console.log("zone/initialize height = %s", this.height );

    /**
     * Set matrix default color
     */
    this.matrix_initialize = function() {
        var imax = self.width * self.height;
        for (var i=0; i<imax; i++){
            _matrix[i] = ZONE_BACKGROUND_COLOR;
        }
    }

    /*
     * Set matrix position to given color
     */
    this.pixel_set = function( pos, color ) {
        var idx = Math.floor( pos.y ) * self.width + Math.floor(pos.x);
        // console.log("zone/pixel_set idx = %s", idx );
        _matrix[idx] = color;
    };

    /*
     * Get color at given matrix position
     */
    this.pixel_get = function( pos ) {
        var idx = Math.floor( pos.y ) * self.width + Math.floor( pos.x );
        // console.log("zone/pixel_get idx = %s", idx );
        return _matrix[idx];
    };

    /*
     * Return whethe given position is inside or outside zone
     */
    this.is_bound = function( pos ) {
        if ( pos.x < 0 ) return false;
        if ( pos.x >= self.width ) return false;
        if ( pos.y < 0 ) return false;
        if ( pos.y >= self.height ) return false;
        return true;
    }

    /**
     * Create patch out of latest changes
     */
    this.patch_record = function( pos, color ) {
        var patch_update;
        var patch_enqueue;

        if ( _current_patch == null ) {
            // console.log("zone/patch_record: patch creation!");
            _current_patch = {
                stamp: new Date(),
                color: color,
                changes: [ { x: pos.x, y: pos.y, stamp: 0 }  ]
            }
        } else {
            if ( _current_patch.color == color) {
                self.patch_enqueue();
                _current_patch = {
                    stamp: new Date(),
                    color: color,
                    changes: [ { x: pos.x, y: pos.y, stamp: 0 }  ]
                }
            } else {
                // console.log("zone/patch_record: patch update!");

                _current_patch.changes.push( { x: pos.x, y: pos.y, stamp: 0 });
            }
        }
    };

    /**
     * Push current patch to local patch queue
     */
    this.patch_enqueue = function() {
        _local_patches.push(_current_patch);
        _current_patch = null;
    };

    /*
     * Utility function for debugging
     */
    this.to_s = function() { JSON.stringify(this); };

    // constructor
    self.matrix_initialize();

}

<|endoftext|>


<commit_before>import Header from '../src/components/Header'
import '../src/components/Header/Header.css'
// import '../src/components/Select/Select.css'
// import '../src/components/UserList/UserList.css'
// import '../src/components/SearchBox/SearchBox.css'
import 'antd/lib/menu/style/css'
import 'antd/lib/dropdown/style/css'
import 'antd/lib/icon/style/css'
import 'antd/lib/input/style/css'
import 'antd/lib/select/style/css'

export default Header
<commit_msg>remove comments<commit_after>import Header from '../src/components/Header'
import '../src/components/Header/Header.css'
import 'antd/lib/menu/style/css'
import 'antd/lib/dropdown/style/css'
import 'antd/lib/icon/style/css'
import 'antd/lib/input/style/css'
import 'antd/lib/select/style/css'

export default Header
<|endoftext|>


<commit_before>

// ----------------------------------------------------------------
// class RttEstimator
var RttEstimator = function RttEstimator() {
  this.n_samples = 0;
  this.rtt = 1000.0;
  this.variance = 0;
  this.multiplier = 1;
  this.gain = 0.1;

  this.min_rto = 10.0;
  this.max_multiplier = 16;
};

// public method measurement
RttEstimator.prototype.measurement = function RttEstimator_measurement(m) {
  if (this.n_samples) {
    var err = m - this.rtt;
    this.rtt += err * this.gain;
    this.variance += (Math.abs(err) - this.variance) * this.gain;
  } else {
    this.rtt = m;
    this.variance = m/2;
  }
  ++this.n_samples;
};

// public method increase_multiplier
RttEstimator.prototype.increase_multiplier = function RttEstimator_increase_multiplier() {
  this.multiplier = Math.min(this.max_multiplier, this.multiplier*2);
};

// public method reset_multiplier
RttEstimator.prototype.reset_multiplier = function RttEstimator_reset_multiplier() {
  this.multiplier = 1;
}

// public method rto
RttEstimator.prototype.rto = function RttEstimator_rto() {
  return Math.max(this.min_rto, (this.rtt + 4*this.variance) * this.multiplier);
};

// ----------------------------------------------------------------
// class NamePrefixTable
// NamePrefixTable is solely used for strategy decision support.
// It does not act as FIB or PIT.
var NamePrefixTable = function NamePrefixTable() {
  this.T = {};
};

// private method entry_seek
NamePrefixTable.prototype.entry_seek = function NamePrefixTable_entry_seek(name, expires) {
  var name_key = name.to_uri();
  var entry = this.T[name_key];
  if (expires === false) return entry;
  if (!entry) this.T[name_key] = entry = { _expiry:0, _timer:0 };

  var now = Date.now(), expiry = now + expires;
  entry._expiry = Math.max(entry._expiry, expiry);
  clearTimeout(entry._timer);
  entry._timer = setTimeout(this.entry_expiry.bind(this, name_key), entry._expiry - now);

  return entry;
};

// private method entry_expiry
NamePrefixTable.prototype.entry_expiry = function NamePrefixTable_entry_expiry(name_key) {
  var now = Date.now;
  var entry = this.T[name_key];
  if (now >= entry._expiry) {
    delete this.T[name_key];
  } else {
    setTimeout(this.entry_expiry.bind(this, name_key), entry._expiry - now);
  }
};

// public method set
NamePrefixTable.prototype.set = function NamePrefixTable_add(name, key, value, expires) {
  if (!expires) expires = 2000;
  var entry = this.entry_seek(name, expires);
  entry[key] = value;
};

// public method renew_or_set
// If key exists, renew; otherwise, set to new_value or create_new_value().
NamePrefixTable.prototype.renew_or_set = function namePrefixTable_get(name, key, new_value, expires, create_new_value) {
  var entry = this.entry_seek(name, expires);
  if (entry[key] === undefined) {
    if (typeof new_value == 'undefined') {
      entry[key] = create_new_value();
    } else {
      entry[key] = new_value;
    }
  }
  return entry[key];
};

// public method get
NamePrefixTable.prototype.get = function namePrefixTable_get(name, key) {
  var entry = this.entry_seek(name, false);
  if (!entry) return undefined;
  return entry[key];
};

// ----------------------------------------------------------------
exports.RttEstimator = RttEstimator;
exports.NamePrefixTable = NamePrefixTable;

<commit_msg>fix NamePrefixTable memory leak<commit_after>

// ----------------------------------------------------------------
// class RttEstimator
var RttEstimator = function RttEstimator() {
  this.n_samples = 0;
  this.rtt = 1000.0;
  this.variance = 0;
  this.multiplier = 1;
  this.gain = 0.1;

  this.min_rto = 10.0;
  this.max_multiplier = 16;
};

// public method measurement
RttEstimator.prototype.measurement = function RttEstimator_measurement(m) {
  if (this.n_samples) {
    var err = m - this.rtt;
    this.rtt += err * this.gain;
    this.variance += (Math.abs(err) - this.variance) * this.gain;
  } else {
    this.rtt = m;
    this.variance = m/2;
  }
  ++this.n_samples;
};

// public method increase_multiplier
RttEstimator.prototype.increase_multiplier = function RttEstimator_increase_multiplier() {
  this.multiplier = Math.min(this.max_multiplier, this.multiplier*2);
};

// public method reset_multiplier
RttEstimator.prototype.reset_multiplier = function RttEstimator_reset_multiplier() {
  this.multiplier = 1;
}

// public method rto
RttEstimator.prototype.rto = function RttEstimator_rto() {
  return Math.max(this.min_rto, (this.rtt + 4*this.variance) * this.multiplier);
};

// ----------------------------------------------------------------
// class NamePrefixTable
// NamePrefixTable is solely used for strategy decision support.
// It does not act as FIB or PIT.
var NamePrefixTable = function NamePrefixTable() {
  this.T = {};
};

// private method entry_seek
NamePrefixTable.prototype.entry_seek = function NamePrefixTable_entry_seek(name, expires) {
  var name_key = name.to_uri();
  var entry = this.T[name_key];
  if (expires === false) return entry;
  if (!entry) this.T[name_key] = entry = { _expiry:0, _timer:0 };

  var now = Date.now(), expiry = now + expires;
  entry._expiry = Math.max(entry._expiry, expiry);
  clearTimeout(entry._timer);
  entry._timer = setTimeout(this.entry_expiry.bind(this, name_key), entry._expiry - now);

  return entry;
};

// private method entry_expiry
NamePrefixTable.prototype.entry_expiry = function NamePrefixTable_entry_expiry(name_key) {
  var now = Date.now();
  var entry = this.T[name_key];
  if (now >= entry._expiry) {
    delete this.T[name_key];
  } else {
    setTimeout(this.entry_expiry.bind(this, name_key), entry._expiry - now);
  }
};

// public method set
NamePrefixTable.prototype.set = function NamePrefixTable_add(name, key, value, expires) {
  if (!expires) expires = 2000;
  var entry = this.entry_seek(name, expires);
  entry[key] = value;
};

// public method renew_or_set
// If key exists, renew; otherwise, set to new_value or create_new_value().
NamePrefixTable.prototype.renew_or_set = function namePrefixTable_get(name, key, new_value, expires, create_new_value) {
  var entry = this.entry_seek(name, expires);
  if (entry[key] === undefined) {
    if (typeof new_value == 'undefined') {
      entry[key] = create_new_value();
    } else {
      entry[key] = new_value;
    }
  }
  return entry[key];
};

// public method get
NamePrefixTable.prototype.get = function namePrefixTable_get(name, key) {
  var entry = this.entry_seek(name, false);
  if (!entry) return undefined;
  return entry[key];
};

// ----------------------------------------------------------------
exports.RttEstimator = RttEstimator;
exports.NamePrefixTable = NamePrefixTable;

<|endoftext|>


<commit_before>/*
  Copyright 2010, François de Metz <francois@2metz.fr>
*/
/**
 * Roster Plugin
 * Allow easily roster management
 *
 *  Features
 *  * Get roster from server
 *  * handle presence
 *  * handle roster iq
 *  * subscribe/unsubscribe
 *  * authorize/unauthorize
 *  * roster versioning (xep 237)
 */
Strophe.addConnectionPlugin('roster',
{
    /** Function: init
     * Plugin init
     *
     * Parameters:
     *   (Strophe.Connection) conn - Strophe connection
     */
    init: function(conn)
    {
        this._connection = conn;
        this._callbacks = [];
        this._callbacks_request = [];
        /** Property: items
         * Roster items
         * [
         *    {
         *        name         : "",
         *        jid          : "",
         *        subscription : "",
         *        ask          : "",
         *        groups       : ["", ""],
         *        resources    : {
         *            myresource : {
         *                show   : "",
         *                status : "",
         *                priority : ""
         *            }
         *        }
         *    }
         * ]
         */
        items = [];
              /** Property: ver
               * current roster revision
               * always null if server doesn't support xep 237
               */
        ver = null;
        // Override the connect and attach methods to always add presence and roster handlers.
        // They are removed when the connection disconnects, so must be added on connection.
        var oldCallback, roster = this, _connect = conn.connect, _attach = conn.attach;
        var newCallback = function(status)
        {
            if (status == Strophe.Status.ATTACHED || status == Strophe.Status.CONNECTED)
            {
                try
                {
                    // Presence subscription
                    conn.addHandler(roster._onReceivePresence.bind(roster), null, 'presence', null, null, null);
                    conn.addHandler(roster._onReceiveIQ.bind(roster), Strophe.NS.ROSTER, 'iq', "set", null, null);
                }
                catch (e)
                {
                    Strophe.error(e);
                }
            }
            if (oldCallback !== null)
                oldCallback.apply(this, arguments);
        };
        conn.connect = function(jid, pass, callback, wait, hold, route)
        {
            oldCallback = callback;
            if (typeof jid  == "undefined")
                jid  = null;
            if (typeof pass == "undefined")
                pass = null;
            callback = newCallback;
            _connect.apply(conn, [jid, pass, callback, wait, hold, route]);
        };
        conn.attach = function(jid, sid, rid, callback, wait, hold, wind)
        {
            oldCallback = callback;
            if (typeof jid == "undefined")
                jid = null;
            if (typeof sid == "undefined")
                sid = null;
            if (typeof rid == "undefined")
                rid = null;
            callback = newCallback;
            _attach.apply(conn, [jid, sid, rid, callback, wait, hold, wind]);
        };

        Strophe.addNamespace('ROSTER_VER', 'urn:xmpp:features:rosterver');
        Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
    },
    /** Function: supportVersioning
     * return true if roster versioning is enabled on server
     */
    supportVersioning: function()
    {
        return (this._connection.features && this._connection.features.getElementsByTagName('ver').length > 0);
    },
    /** Function: get
     * Get Roster on server
     *
     * Parameters:
     *   (Function) userCallback - callback on roster result
     *   (String) ver - current rev of roster
     *      (only used if roster versioning is enabled)
     *   (Array) items - initial items of ver
     *      (only used if roster versioning is enabled)
     *     In browser context you can use sessionStorage
     *     to store your roster in json (JSON.stringify())
     */
    get: function(userCallback, ver, items)
    {
        var attrs = {xmlns: Strophe.NS.ROSTER};
        this.items = [];
        if (this.supportVersioning())
        {
            // empty rev because i want an rev attribute in the result
            attrs.ver = ver || '';
            this.items = items || [];
        }
        var iq = $iq({type: 'get',  'id' : this._connection.getUniqueId('roster')}).c('query', attrs);
        return this._connection.sendIQ(iq,
                                this._onReceiveRosterSuccess.bind(this, userCallback),
                                this._onReceiveRosterError.bind(this, userCallback));
    },
    /** Function: registerCallback
     * register callback on roster (presence and iq)
     *
     * Parameters:
     *   (Function) call_back
     */
    registerCallback: function(call_back)
    {
        this._callbacks.push(call_back);
    },

    registerRequestCallback: function (call_back)
    {
        this._callbacks_request.push(call_back);
    },

    /** Function: findItem
     * Find item by JID
     *
     * Parameters:
     *     (String) jid
     */
    findItem : function(jid)
    {
        for (var i = 0; i < this.items.length; i++)
        {
            if (this.items[i] && this.items[i].jid == jid)
            {
                return this.items[i];
            }
        }
        return false;
    },
    /** Function: removeItem
     * Remove item by JID
     *
     * Parameters:
     *     (String) jid
     */
    removeItem : function(jid)
    {
        for (var i = 0; i < this.items.length; i++)
        {
            if (this.items[i] && this.items[i].jid == jid)
            {
                this.items.splice(i, 1);
                return true;
            }
        }
        return false;
    },
    /** Function: subscribe
     * Subscribe presence
     *
     * Parameters:
     *     (String) jid
     *     (String) message (optional)
     *     (String) nick  (optional)
     */
    subscribe: function(jid, message, nick) {
        var pres = $pres({to: jid, type: "subscribe"});
        if (message && message !== "") {
            pres.c("status").t(message).up();
        }
        if (nick && nick !== "") {
            pres.c('nick', {'xmlns': Strophe.NS.NICK}).t(nick).up();
        }
        this._connection.send(pres);
    },
    /** Function: unsubscribe
     * Unsubscribe presence
     *
     * Parameters:
     *     (String) jid
     *     (String) message
     */
    unsubscribe: function(jid, message)
    {
        var pres = $pres({to: jid, type: "unsubscribe"});
        if (message && message !== "")
            pres.c("status").t(message);
        this._connection.send(pres);
    },
    /** Function: authorize
     * Authorize presence subscription
     *
     * Parameters:
     *     (String) jid
     *     (String) message
     */
    authorize: function(jid, message)
    {
        var pres = $pres({to: jid, type: "subscribed"});
        if (message && message !== "")
            pres.c("status").t(message);
        this._connection.send(pres);
    },
    /** Function: unauthorize
     * Unauthorize presence subscription
     *
     * Parameters:
     *     (String) jid
     *     (String) message
     */
    unauthorize: function(jid, message)
    {
        var pres = $pres({to: jid, type: "unsubscribed"});
        if (message && message !== "")
            pres.c("status").t(message);
        this._connection.send(pres);
    },
    /** Function: add
     * Add roster item
     *
     * Parameters:
     *   (String) jid - item jid
     *   (String) name - name
     *   (Array) groups
     *   (Function) call_back
     */
    add: function(jid, name, groups, call_back)
    {
        var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: jid,
                                                                                name: name});
        for (var i = 0; i < groups.length; i++)
        {
            iq.c('group').t(groups[i]).up();
        }
        this._connection.sendIQ(iq, call_back, call_back);
    },
    /** Function: update
     * Update roster item
     *
     * Parameters:
     *   (String) jid - item jid
     *   (String) name - name
     *   (Array) groups
     *   (Function) call_back
     */
    update: function(jid, name, groups, call_back)
    {
        var item = this.findItem(jid);
        if (!item)
        {
            throw "item not found";
        }
        var newName = name || item.name;
        var newGroups = groups || item.groups;
        var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: item.jid,
                                                                                name: newName});
        for (var i = 0; i < newGroups.length; i++)
        {
            iq.c('group').t(newGroups[i]).up();
        }
        return this._connection.sendIQ(iq, call_back, call_back);
    },
    /** Function: remove
     * Remove roster item
     *
     * Parameters:
     *   (String) jid - item jid
     *   (Function) call_back
     */
    remove: function(jid, call_back)
    {
        var item = this.findItem(jid);
        if (!item)
        {
            throw "item not found";
        }
        var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: item.jid,
                                                                                subscription: "remove"});
        this._connection.sendIQ(iq, call_back, call_back);
    },
    /** PrivateFunction: _onReceiveRosterSuccess
     *
     */
    _onReceiveRosterSuccess: function(userCallback, stanza)
    {
        this._updateItems(stanza);
        userCallback(this.items);
    },
    /** PrivateFunction: _onReceiveRosterError
     *
     */
    _onReceiveRosterError: function(userCallback, stanza)
    {
        userCallback(this.items);
    },
    /** PrivateFunction: _onReceivePresence
     * Handle presence
     */
    _onReceivePresence : function(presence)
    {
        // TODO: from is optional
        var jid = presence.getAttribute('from');
        var from = Strophe.getBareJidFromJid(jid);
        var item = this.findItem(from);
        var type = presence.getAttribute('type');
        // not in roster
        if (!item)
        {
            // if 'friend request' presence
            if (type === 'subscribe') {
                this._call_backs_request(from);
            }
            return true;
        }
        if (type == 'unavailable')
        {
            delete item.resources[Strophe.getResourceFromJid(jid)];
        }
        else if (!type)
        {
            // TODO: add timestamp
            item.resources[Strophe.getResourceFromJid(jid)] = {
                show     : (presence.getElementsByTagName('show').length !== 0) ? Strophe.getText(presence.getElementsByTagName('show')[0]) : "",
                status   : (presence.getElementsByTagName('status').length !== 0) ? Strophe.getText(presence.getElementsByTagName('status')[0]) : "",
                priority : (presence.getElementsByTagName('priority').length !== 0) ? Strophe.getText(presence.getElementsByTagName('priority')[0]) : ""
            };
        }
        else
        {
            // Stanza is not a presence notification. (It's probably a subscription type stanza.)
            return true;
        }
        this._call_backs(this.items, item);
        return true;
    },
    /** PrivateFunction: _call_backs_request
     * call all the callbacks waiting for 'friend request' presences
     */
    _call_backs_request : function(from)
    {
        for (var i = 0; i < this._callbacks_request.length; i++) // [].forEach my love ...
        {
            this._callbacks_request[i](from);
        }
    },

    /** PrivateFunction: _call_backs
     *
     */
    _call_backs : function(items, item)
    {
        for (var i = 0; i < this._callbacks.length; i++) // [].forEach my love ...
        {
            this._callbacks[i](items, item);
        }
    },
    /** PrivateFunction: _onReceiveIQ
     * Handle roster push.
     */
    _onReceiveIQ : function(iq)
    {
        var id = iq.getAttribute('id');
        var from = iq.getAttribute('from');
        // Receiving client MUST ignore stanza unless it has no from or from = user's JID.
        if (from && from !== "" && from != this._connection.jid && from != Strophe.getBareJidFromJid(this._connection.jid))
            return true;
        var iqresult = $iq({type: 'result', id: id, from: this._connection.jid});
        this._connection.send(iqresult);
        this._updateItems(iq);
        return true;
    },
    /** PrivateFunction: _updateItems
     * Update items from iq
     */
    _updateItems : function(iq)
    {
        var query = iq.getElementsByTagName('query');
        if (query.length !== 0)
        {
            this.ver = query.item(0).getAttribute('ver');
            var self = this;
            Strophe.forEachChild(query.item(0), 'item',
                function (item)
                {
                    self._updateItem(item);
                }
           );
        }
        this._call_backs(this.items);
    },
    /** PrivateFunction: _updateItem
     * Update internal representation of roster item
     */
    _updateItem : function(item)
    {
        var jid           = item.getAttribute("jid");
        var name          = item.getAttribute("name");
        var subscription  = item.getAttribute("subscription");
        var ask           = item.getAttribute("ask");
        var groups        = [];
        Strophe.forEachChild(item, 'group',
            function(group)
            {
                groups.push(Strophe.getText(group));
            }
        );

        if (subscription == "remove")
        {
            this.removeItem(jid);
            return;
        }

        item = this.findItem(jid);
        if (!item)
        {
            this.items.push({
                name         : name,
                jid          : jid,
                subscription : subscription,
                ask          : ask,
                groups       : groups,
                resources    : {}
            });
        }
        else
        {
            item.name = name;
            item.subscription = subscription;
            item.ask = ask;
            item.groups = groups;
        }
    }
});
<commit_msg>When updating a roster entry, include it in the callback invocation<commit_after>/*
  Copyright 2010, François de Metz <francois@2metz.fr>
*/
/**
 * Roster Plugin
 * Allow easily roster management
 *
 *  Features
 *  * Get roster from server
 *  * handle presence
 *  * handle roster iq
 *  * subscribe/unsubscribe
 *  * authorize/unauthorize
 *  * roster versioning (xep 237)
 */
Strophe.addConnectionPlugin('roster',
{
    /** Function: init
     * Plugin init
     *
     * Parameters:
     *   (Strophe.Connection) conn - Strophe connection
     */
    init: function(conn)
    {
        this._connection = conn;
        this._callbacks = [];
        this._callbacks_request = [];
        /** Property: items
         * Roster items
         * [
         *    {
         *        name         : "",
         *        jid          : "",
         *        subscription : "",
         *        ask          : "",
         *        groups       : ["", ""],
         *        resources    : {
         *            myresource : {
         *                show   : "",
         *                status : "",
         *                priority : ""
         *            }
         *        }
         *    }
         * ]
         */
        items = [];
              /** Property: ver
               * current roster revision
               * always null if server doesn't support xep 237
               */
        ver = null;
        // Override the connect and attach methods to always add presence and roster handlers.
        // They are removed when the connection disconnects, so must be added on connection.
        var oldCallback, roster = this, _connect = conn.connect, _attach = conn.attach;
        var newCallback = function(status)
        {
            if (status == Strophe.Status.ATTACHED || status == Strophe.Status.CONNECTED)
            {
                try
                {
                    // Presence subscription
                    conn.addHandler(roster._onReceivePresence.bind(roster), null, 'presence', null, null, null);
                    conn.addHandler(roster._onReceiveIQ.bind(roster), Strophe.NS.ROSTER, 'iq', "set", null, null);
                }
                catch (e)
                {
                    Strophe.error(e);
                }
            }
            if (oldCallback !== null)
                oldCallback.apply(this, arguments);
        };
        conn.connect = function(jid, pass, callback, wait, hold, route)
        {
            oldCallback = callback;
            if (typeof jid  == "undefined")
                jid  = null;
            if (typeof pass == "undefined")
                pass = null;
            callback = newCallback;
            _connect.apply(conn, [jid, pass, callback, wait, hold, route]);
        };
        conn.attach = function(jid, sid, rid, callback, wait, hold, wind)
        {
            oldCallback = callback;
            if (typeof jid == "undefined")
                jid = null;
            if (typeof sid == "undefined")
                sid = null;
            if (typeof rid == "undefined")
                rid = null;
            callback = newCallback;
            _attach.apply(conn, [jid, sid, rid, callback, wait, hold, wind]);
        };

        Strophe.addNamespace('ROSTER_VER', 'urn:xmpp:features:rosterver');
        Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
    },
    /** Function: supportVersioning
     * return true if roster versioning is enabled on server
     */
    supportVersioning: function()
    {
        return (this._connection.features && this._connection.features.getElementsByTagName('ver').length > 0);
    },
    /** Function: get
     * Get Roster on server
     *
     * Parameters:
     *   (Function) userCallback - callback on roster result
     *   (String) ver - current rev of roster
     *      (only used if roster versioning is enabled)
     *   (Array) items - initial items of ver
     *      (only used if roster versioning is enabled)
     *     In browser context you can use sessionStorage
     *     to store your roster in json (JSON.stringify())
     */
    get: function(userCallback, ver, items)
    {
        var attrs = {xmlns: Strophe.NS.ROSTER};
        this.items = [];
        if (this.supportVersioning())
        {
            // empty rev because i want an rev attribute in the result
            attrs.ver = ver || '';
            this.items = items || [];
        }
        var iq = $iq({type: 'get',  'id' : this._connection.getUniqueId('roster')}).c('query', attrs);
        return this._connection.sendIQ(iq,
                                this._onReceiveRosterSuccess.bind(this, userCallback),
                                this._onReceiveRosterError.bind(this, userCallback));
    },
    /** Function: registerCallback
     * register callback on roster (presence and iq)
     *
     * Parameters:
     *   (Function) call_back
     */
    registerCallback: function(call_back)
    {
        this._callbacks.push(call_back);
    },

    registerRequestCallback: function (call_back)
    {
        this._callbacks_request.push(call_back);
    },

    /** Function: findItem
     * Find item by JID
     *
     * Parameters:
     *     (String) jid
     */
    findItem : function(jid)
    {
        for (var i = 0; i < this.items.length; i++)
        {
            if (this.items[i] && this.items[i].jid == jid)
            {
                return this.items[i];
            }
        }
        return false;
    },
    /** Function: removeItem
     * Remove item by JID
     *
     * Parameters:
     *     (String) jid
     */
    removeItem : function(jid)
    {
        for (var i = 0; i < this.items.length; i++)
        {
            if (this.items[i] && this.items[i].jid == jid)
            {
                this.items.splice(i, 1);
                return true;
            }
        }
        return false;
    },
    /** Function: subscribe
     * Subscribe presence
     *
     * Parameters:
     *     (String) jid
     *     (String) message (optional)
     *     (String) nick  (optional)
     */
    subscribe: function(jid, message, nick) {
        var pres = $pres({to: jid, type: "subscribe"});
        if (message && message !== "") {
            pres.c("status").t(message).up();
        }
        if (nick && nick !== "") {
            pres.c('nick', {'xmlns': Strophe.NS.NICK}).t(nick).up();
        }
        this._connection.send(pres);
    },
    /** Function: unsubscribe
     * Unsubscribe presence
     *
     * Parameters:
     *     (String) jid
     *     (String) message
     */
    unsubscribe: function(jid, message)
    {
        var pres = $pres({to: jid, type: "unsubscribe"});
        if (message && message !== "")
            pres.c("status").t(message);
        this._connection.send(pres);
    },
    /** Function: authorize
     * Authorize presence subscription
     *
     * Parameters:
     *     (String) jid
     *     (String) message
     */
    authorize: function(jid, message)
    {
        var pres = $pres({to: jid, type: "subscribed"});
        if (message && message !== "")
            pres.c("status").t(message);
        this._connection.send(pres);
    },
    /** Function: unauthorize
     * Unauthorize presence subscription
     *
     * Parameters:
     *     (String) jid
     *     (String) message
     */
    unauthorize: function(jid, message)
    {
        var pres = $pres({to: jid, type: "unsubscribed"});
        if (message && message !== "")
            pres.c("status").t(message);
        this._connection.send(pres);
    },
    /** Function: add
     * Add roster item
     *
     * Parameters:
     *   (String) jid - item jid
     *   (String) name - name
     *   (Array) groups
     *   (Function) call_back
     */
    add: function(jid, name, groups, call_back)
    {
        var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: jid,
                                                                                name: name});
        for (var i = 0; i < groups.length; i++)
        {
            iq.c('group').t(groups[i]).up();
        }
        this._connection.sendIQ(iq, call_back, call_back);
    },
    /** Function: update
     * Update roster item
     *
     * Parameters:
     *   (String) jid - item jid
     *   (String) name - name
     *   (Array) groups
     *   (Function) call_back
     */
    update: function(jid, name, groups, call_back)
    {
        var item = this.findItem(jid);
        if (!item)
        {
            throw "item not found";
        }
        var newName = name || item.name;
        var newGroups = groups || item.groups;
        var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: item.jid,
                                                                                name: newName});
        for (var i = 0; i < newGroups.length; i++)
        {
            iq.c('group').t(newGroups[i]).up();
        }
        return this._connection.sendIQ(iq, call_back, call_back);
    },
    /** Function: remove
     * Remove roster item
     *
     * Parameters:
     *   (String) jid - item jid
     *   (Function) call_back
     */
    remove: function(jid, call_back)
    {
        var item = this.findItem(jid);
        if (!item)
        {
            throw "item not found";
        }
        var iq = $iq({type: 'set'}).c('query', {xmlns: Strophe.NS.ROSTER}).c('item', {jid: item.jid,
                                                                                subscription: "remove"});
        this._connection.sendIQ(iq, call_back, call_back);
    },
    /** PrivateFunction: _onReceiveRosterSuccess
     *
     */
    _onReceiveRosterSuccess: function(userCallback, stanza)
    {
        this._updateItems(stanza);
        this._call_backs(this.items);
        userCallback(this.items);
    },
    /** PrivateFunction: _onReceiveRosterError
     *
     */
    _onReceiveRosterError: function(userCallback, stanza)
    {
        userCallback(this.items);
    },
    /** PrivateFunction: _onReceivePresence
     * Handle presence
     */
    _onReceivePresence : function(presence)
    {
        // TODO: from is optional
        var jid = presence.getAttribute('from');
        var from = Strophe.getBareJidFromJid(jid);
        var item = this.findItem(from);
        var type = presence.getAttribute('type');
        // not in roster
        if (!item)
        {
            // if 'friend request' presence
            if (type === 'subscribe') {
                this._call_backs_request(from);
            }
            return true;
        }
        if (type == 'unavailable')
        {
            delete item.resources[Strophe.getResourceFromJid(jid)];
        }
        else if (!type)
        {
            // TODO: add timestamp
            item.resources[Strophe.getResourceFromJid(jid)] = {
                show     : (presence.getElementsByTagName('show').length !== 0) ? Strophe.getText(presence.getElementsByTagName('show')[0]) : "",
                status   : (presence.getElementsByTagName('status').length !== 0) ? Strophe.getText(presence.getElementsByTagName('status')[0]) : "",
                priority : (presence.getElementsByTagName('priority').length !== 0) ? Strophe.getText(presence.getElementsByTagName('priority')[0]) : ""
            };
        }
        else
        {
            // Stanza is not a presence notification. (It's probably a subscription type stanza.)
            return true;
        }
        this._call_backs(this.items, item);
        return true;
    },
    /** PrivateFunction: _call_backs_request
     * call all the callbacks waiting for 'friend request' presences
     */
    _call_backs_request : function(from)
    {
        for (var i = 0; i < this._callbacks_request.length; i++) // [].forEach my love ...
        {
            this._callbacks_request[i](from);
        }
    },

    /** PrivateFunction: _call_backs
     *
     */
    _call_backs : function(items, item)
    {
        for (var i = 0; i < this._callbacks.length; i++) // [].forEach my love ...
        {
            this._callbacks[i](items, item);
        }
    },
    /** PrivateFunction: _onReceiveIQ
     * Handle roster push.
     */
    _onReceiveIQ : function(iq)
    {
        var id = iq.getAttribute('id');
        var from = iq.getAttribute('from');
        // Receiving client MUST ignore stanza unless it has no from or from = user's JID.
        if (from && from !== "" && from != this._connection.jid && from != Strophe.getBareJidFromJid(this._connection.jid))
            return true;
        var iqresult = $iq({type: 'result', id: id, from: this._connection.jid});
        this._connection.send(iqresult);
        this._updateItems(iq);
        return true;
    },
    /** PrivateFunction: _updateItems
     * Update items from iq
     */
    _updateItems : function(iq)
    {
        var query = iq.getElementsByTagName('query');
        if (query.length !== 0)
        {
            this.ver = query.item(0).getAttribute('ver');
            var self = this;
            Strophe.forEachChild(query.item(0), 'item',
                function (item)
                {
                    self._updateItem(item);
                }
           );
        }
    },
    /** PrivateFunction: _updateItem
     * Update internal representation of roster item
     */
    _updateItem : function(item)
    {
        var jid           = item.getAttribute("jid");
        var name          = item.getAttribute("name");
        var subscription  = item.getAttribute("subscription");
        var ask           = item.getAttribute("ask");
        var groups        = [];
        Strophe.forEachChild(item, 'group',
            function(group)
            {
                groups.push(Strophe.getText(group));
            }
        );

        if (subscription == "remove")
        {
            this.removeItem(jid);
            this._call_backs(this.items, {jid: jid, subscription: 'remove'});
            return;
        }

        item = this.findItem(jid);
        if (!item)
        {
            item = {
                name         : name,
                jid          : jid,
                subscription : subscription,
                ask          : ask,
                groups       : groups,
                resources    : {}
            };
            this.items.push(item);
        }
        else
        {
            item.name = name;
            item.subscription = subscription;
            item.ask = ask;
            item.groups = groups;
        }
        this._call_backs(this.items, item);
    }
});
<|endoftext|>


<commit_before>import { StyleSheet } from 'react-native';

const globals = StyleSheet.create({
  green: {
    backgroundColor: '#7DC242',
  },
  orange: {
    backgroundColor: '#F69731',
  },
  red: {
    backgroundColor: '#ED1651',
  },
  yellow: {
    backgroundColor: '#E6D228',
  },
  base: {
    flex: 1
  },
  whiteText: {
    color: '#fff'
  },
  textStyle: {
    fontSize: 18,
  },
  icon: {
    opacity: 0.75,
  },
  pkFont: {
    // fontFamily: 'NotoNastaliqUrdu-Regular',
    alignSelf: 'flex-end',
    fontFamily: 'NotoSans-Regular',
  },
  enFont: {
    fontFamily: 'NotoSans-Regular',
  },
  bold: {
    fontFamily: 'NotoSans-Bold',
  },
  menuButtons: {
    alignItems: 'center'
  },
  tabStyles: {
    backgroundColor: '#7DC242'
  },
  twoButtons: {
    flex: 2
  },
  screenBg: {
    width:'100%',
    flexBasis: 500
  },
  para: {
    paddingHorizontal: 20,
    paddingVertical: 10
  },
  notPara: {
    paddingHorizontal: 5,
    paddingVertical: 2
  },
  linkIcon: {
    paddingBottom: 12,
    color: '#fff',
    alignSelf: 'flex-end',
  },
  linkButton: {
    fontFamily: 'NotoSans-Bold',
    textAlign: 'center',
    paddingVertical: 5,
    paddingHorizontal: 5,
    fontSize:12,
    width: 220,
    textAlignVertical: 'center',
    alignSelf: 'center'
  },
  linkView: {
    marginHorizontal: 20,
    marginVertical:10,
    borderRadius: 40,
    flexDirection: 'row',
    minHeight: 40,
    justifyContent:  'center',
    alignContent: 'center',
    elevation: 3
  },
  inlandLink: {
    color: '#fff',
    backgroundColor: 'rgb(41,149,220)',
    fontFamily: 'NotoSans-Bold',
  },
  listItemContainer: {
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'flex-start',
    alignContent: 'stretch',
  },
  listIcon: {
    alignSelf: 'flex-start',
    color: '#F69731',
    borderTopColor: '#F69731',
    borderTopWidth: 2,
    backgroundColor: '#fff',
    width: '100%',
    flexShrink: 1,
    paddingVertical: 10,
    paddingHorizontal: 20
  },
  listItem: {
    paddingVertical: 10,
    flexGrow: 5,
  },
  lineBreak: {
    paddingBottom: 20,
    borderBottomColor: '#F69731',
    borderBottomWidth: 1,
  },
  shortBreak: {
    width: '100%',
    backgroundColor: '#F69731',
  }
})

export { globals };
<commit_msg>increased Urdu fontsize<commit_after>import { StyleSheet } from 'react-native';

const globals = StyleSheet.create({
  green: {
    backgroundColor: '#7DC242',
  },
  orange: {
    backgroundColor: '#F69731',
  },
  red: {
    backgroundColor: '#ED1651',
  },
  yellow: {
    backgroundColor: '#E6D228',
  },
  base: {
    flex: 1
  },
  whiteText: {
    color: '#fff'
  },
  textStyle: {
    fontSize: 18,
  },
  icon: {
    opacity: 0.75,
  },
  pkFont: {
    // fontFamily: 'NotoNastaliqUrdu-Regular',
    alignSelf: 'flex-end',
    fontFamily: 'NotoSans-Regular',
    fontSize: 14,
  },
  enFont: {
    fontFamily: 'NotoSans-Regular',
  },
  bold: {
    fontFamily: 'NotoSans-Bold',
  },
  menuButtons: {
    alignItems: 'center'
  },
  tabStyles: {
    backgroundColor: '#7DC242'
  },
  twoButtons: {
    flex: 2
  },
  screenBg: {
    width:'100%',
    flexBasis: 500
  },
  para: {
    paddingHorizontal: 20,
    paddingVertical: 10
  },
  notPara: {
    paddingHorizontal: 5,
    paddingVertical: 2
  },
  linkIcon: {
    paddingBottom: 12,
    color: '#fff',
    alignSelf: 'flex-end',
  },
  linkButton: {
    fontFamily: 'NotoSans-Bold',
    textAlign: 'center',
    paddingVertical: 5,
    paddingHorizontal: 5,
    fontSize:12,
    width: 220,
    textAlignVertical: 'center',
    alignSelf: 'center'
  },
  linkView: {
    marginHorizontal: 20,
    marginVertical:10,
    borderRadius: 40,
    flexDirection: 'row',
    minHeight: 40,
    justifyContent:  'center',
    alignContent: 'center',
    elevation: 3
  },
  inlandLink: {
    color: '#fff',
    backgroundColor: 'rgb(41,149,220)',
    fontFamily: 'NotoSans-Bold',
  },
  listItemContainer: {
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'flex-start',
    alignContent: 'stretch',
  },
  listIcon: {
    alignSelf: 'flex-start',
    color: '#F69731',
    borderTopColor: '#F69731',
    borderTopWidth: 2,
    backgroundColor: '#fff',
    width: '100%',
    flexShrink: 1,
    paddingVertical: 10,
    paddingHorizontal: 20
  },
  listItem: {
    paddingVertical: 10,
    flexGrow: 5,
  },
  lineBreak: {
    paddingBottom: 20,
    borderBottomColor: '#F69731',
    borderBottomWidth: 1,
  },
  shortBreak: {
    width: '100%',
    backgroundColor: '#F69731',
  }
})

export { globals };
<|endoftext|>


<commit_before>openerp.suvit_web_diagram = function(instance, local) {
  var QWeb = instance.web.qweb;
  instance.web.DiagramView.include({
    init: function(parent, dataset, view_id, options) {
        var self = this;
        window.CuteGraphOrig = window.CuteGraph;
        window.CuteGraph = function (r,style,parentNode) {
            self.graph = new CuteGraphOrig(r,style,parentNode);
            return self.graph;
        }
        window.CuteGraph.wordwrap = window.CuteGraphOrig.wordwrap;
        this._super(parent, dataset, view_id, options);
    },

    save_coords: function(node_obj){
        var NodeModel = new instance.web.Model(this.node);
        var coords = node_obj.get_pos();
        coords['x'] = coords['x'] - 50;
        coords['y'] = coords['y'] - 50;
        NodeModel.call('write', [node_obj.id, coords]);
        // console.log(coords);
    },

//какой-то костыль, но я не нашел способа сделать лучше.
    find_label: function(r, el) {
        self = this;
        if (r == el) {
            if (el.next && el.next.type == 'text') {
                return el.next;
            }
            return false;
        } else if (r.next) {
            return self.find_label(r.next, el);
        }
        return false;
    },

    draw_diagram: function(result) {
        var self = this;
        this._super(result);
        if (self.graph) {
            _.each(self.graph.get_node_list(), function(n) {
                var fig = n.get_fig();
                fig.drag(null, null, function(){
                    self.save_coords(n);
                });
                node_label = self.find_label(self.graph.r.bottom, fig);
                node_label.drag(null, null, function(){
                  self.save_coords(n);
                });
            });
        }
    }
  });
};
<commit_msg>diagram fix<commit_after>openerp.suvit_web_diagram = function(instance, local) {
  var QWeb = instance.web.qweb;
  instance.web.DiagramView.include({
    init: function(parent, dataset, view_id, options) {
        var self = this;
        window.CuteGraphOrig = window.CuteGraph;
        window.CuteGraph = function (r,style,parentNode) {
            self.graph = new CuteGraphOrig(r,style,parentNode);
            return self.graph;
        }
        window.CuteGraph.wordwrap = window.CuteGraphOrig.wordwrap;
        this._super(parent, dataset, view_id, options);
    },

    save_coords: function(node_obj){
        var NodeModel = new instance.web.Model(this.node);
        var coords = node_obj.get_pos();
        coords['x'] = coords['x'] - 50;
        coords['y'] = coords['y'] - 50;
        // NodeModel.call('write', [node_obj.id, coords]);
        console.log(coords);
    },

    draw_diagram: function(result) {
        var self = this;
        this._super(result);
        _.each(self.graph.get_node_list(), function(n) {
            var fig = n.get_fig();
            fig.drag(null, null, function(){
                self.save_coords(n);
            });
            node_label = self.graph.r.getById(fig.id).next;
            node_label.drag(null, null, function(){
              self.save_coords(n);
            });
        });
    }
  });
};
<|endoftext|>


<commit_before>/**
 * Copyright 2015-present Tuan Le.
 *
 * Licensed under the MIT License.
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/mit-license.html
 *
 * 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.
 *
 *------------------------------------------------------------------------
 *
 * @module ReactAppRendererComposite
 * @description - A React app renderer composite for server environment.
 *
 * @author Tuan Le (tuan.t.lei@gmail.com)
 */
/* @flow */
'use strict'; // eslint-disable-line

/* load CompositeElement */
import CompositeElement from '../../../core/elements/composite-element';

/* load CommonElement */
import CommonElement from '../../../core/elements/common-element';

/* create CommonElement as Hf object */
const Hf = CommonElement();

/**
 * @description - A React server app renderer composite module.
 *
 * @module ReactAppRendererComposite
 * @return {object}
 */
export default CompositeElement({
    template: {
        /**
         * @description - Initialized and check that factory is valid for this composite.
         *
         * @method $initReactAppRendererComposite
         * @return void
         */
        $initReactAppRendererComposite: function $initReactAppRendererComposite () {
            const app = this;
            if (Hf.DEVELOPMENT) {
                if (!Hf.isSchema({
                    getTopDomain: `function`,
                    getRenderer: `function`,
                    getTopComponent: `function`
                }).of(app)) {
                    Hf.log(`error`, `ReactAppRendererComposite.$init - App is invalid. Cannot apply composite.`);
                }
            }
        },
        /**
         * @description - Render app top level component to the target environment.
         *
         * @method renderToTarget
         * @return void
         */
        renderToTarget: function renderToTarget () {
            const app = this;
            const appComponent = app.getTopComponent();
            if (!(Hf.isObject(appComponent) || Hf.isFunction(appComponent))) {
                Hf.log(`error`, `ReactAppRendererComposite.renderToTarget - React component is invalid.`);
            } else {
                const ReactDOMRenderer = app.getRenderer();
                if (!Hf.isSchema({
                    renderToString: `function`
                }).of(ReactDOMRenderer)) {
                    Hf.log(`error`, `ReactAppRendererComposite.renderToTarget - React renderer is invalid.`);
                } else {
                    const domain = app.getTopDomain();
                    if (!Hf.isSchema({
                        outgoing: `function`
                    }).of(domain)) {
                        Hf.log(`error`, `ReactAppRendererComposite.renderToTarget - App:${app.name} domain is invalid.`);
                    } else {
                        const renderedMarkup = ReactDOMRenderer.renderToString(appComponent);
                        if (Hf.isString(renderedMarkup) && !Hf.isEmpty(renderedMarkup)) {
                            domain.outgoing(`on-render-markup-to-string`).emit(() => renderedMarkup);
                        } else {
                            Hf.log(`warn1`, `ReactAppRendererComposite.renderToTarget - React rendered markup is invalid.`);
                        }
                    }
                }
            }
        }
    }
});
<commit_msg>Minor code cleanups.<commit_after>/**
 * Copyright 2015-present Tuan Le.
 *
 * Licensed under the MIT License.
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/mit-license.html
 *
 * 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.
 *
 *------------------------------------------------------------------------
 *
 * @module ReactAppRendererComposite
 * @description - A React app renderer composite for server environment.
 *
 * @author Tuan Le (tuan.t.lei@gmail.com)
 */
/* @flow */
'use strict'; // eslint-disable-line

/* load CompositeElement */
import CompositeElement from '../../../core/elements/composite-element';

/* load CommonElement */
import CommonElement from '../../../core/elements/common-element';

/* create CommonElement as Hf object */
const Hf = CommonElement();

/**
 * @description - A React server app renderer composite module.
 *
 * @module ReactAppRendererComposite
 * @return {object}
 */
export default CompositeElement({
    template: {
        /**
         * @description - Initialized and check that factory is valid for this composite.
         *
         * @method $initReactAppRendererComposite
         * @return void
         */
        $initReactAppRendererComposite: function $initReactAppRendererComposite () {
            const app = this;
            if (Hf.DEVELOPMENT) {
                if (!Hf.isSchema({
                    getTopDomain: `function`,
                    getRenderer: `function`,
                    getTopComponent: `function`
                }).of(app)) {
                    Hf.log(`error`, `ReactAppRendererComposite.$init - App is invalid. Cannot apply composite.`);
                }
            }
        },
        /**
         * @description - Render app top level component to the target environment.
         *
         * @method renderToTarget
         * @return void
         */
        renderToTarget: function renderToTarget () {
            const app = this;
            const appComponent = app.getTopComponent();
            if (!(Hf.isObject(appComponent) || Hf.isFunction(appComponent))) {
                Hf.log(`error`, `ReactAppRendererComposite.renderToTarget - React component is invalid.`);
            } else {
                const ReactDOMRenderer = app.getRenderer();
                if (!Hf.isSchema({
                    renderToString: `function`
                }).of(ReactDOMRenderer)) {
                    Hf.log(`error`, `ReactAppRendererComposite.renderToTarget - React renderer is invalid.`);
                } else {
                    const domain = app.getTopDomain();
                    if (!Hf.isSchema({
                        outgoing: `function`
                    }).of(domain)) {
                        Hf.log(`error`, `ReactAppRendererComposite.renderToTarget - App:${app.name} domain is invalid.`);
                    } else {
                        const renderedMarkup = ReactDOMRenderer.renderToString(appComponent);
                        if (Hf.isNonEmptyString(renderedMarkup)) {
                            domain.outgoing(`on-render-markup-to-string`).emit(() => renderedMarkup);
                        } else {
                            Hf.log(`warn1`, `ReactAppRendererComposite.renderToTarget - React rendered markup is invalid.`);
                        }
                    }
                }
            }
        }
    }
});
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import { Button, Modal } from 'react-bootstrap';
import styled from 'styled-components';
import FontAwesome from 'react-fontawesome';
import classnames from 'classnames';

import {
  EntityDataModelApi
} from 'loom-data';

import * as actionFactories from './EntitySetDetailActionFactories';
import * as edmActionFactories from '../edm/EdmActionFactories';
import * as PermissionsActionFactory from '../permissions/PermissionsActionFactory';
import EntitySetPermissionsRequestList from '../permissions/components/EntitySetPermissionsRequestList';
import { PermissionsPropType, getPermissions, DEFAULT_PERMISSIONS } from '../permissions/PermissionsStorage';
import { getEdmObject } from '../edm/EdmStorage';
import PropertyTypeList from '../edm/components/PropertyTypeList';
import { PermissionsPanel } from '../../views/Main/Schemas/Components/PermissionsPanel';
import AddDataForm from '../entitysetforms/AddDataForm';
import ActionDropdown from '../edm/components/ActionDropdown';
import AsyncContent, { AsyncStatePropType } from '../../components/asynccontent/AsyncContent';
import { EntitySetPropType } from '../edm/EdmModel';
import Page from '../../components/page/Page';
import PageConsts from '../../utils/Consts/PageConsts';
import styles from './entitysetdetail.module.css';

import StyledFlexContainer from '../../components/flex/StyledFlexContainer';
import StyledFlexContainerStacked from '../../components/flex/StyledFlexContainerStacked';

const TitleControlsContainer = styled(StyledFlexContainer)`
  justify-content: space-between;
`;

const ControlsContainer = styled(StyledFlexContainerStacked)`
  align-items: flex-end;
`;

class EntitySetDetailComponent extends React.Component {
  static propTypes = {
    asyncState: AsyncStatePropType.isRequired,
    router: PropTypes.object.isRequired,

    // Async content
    entitySet: EntitySetPropType,
    entitySetPermissions: PermissionsPropType.isRequired,

    // Loading
    loadEntitySet: PropTypes.func.isRequired
  };

  constructor(props) {
    super(props);
    this.state = {
      editingPermissions: false,
      confirmingDelete: false,
      addingData: false,
      deleteError: false
    };
  }

  setEditingPermissions = () => {
    this.setState({ editingPermissions: true });
  };

  renderHeaderContent = () => {
    const { entitySet, entitySetPermissions } = this.props;

    const contactValue = (entitySet.contacts.length > 0) ? entitySet.contacts.join(', ') : 'none';

    return (

    );
  };

  renderPermissionsPanel = () => {
    if (!this.props.entitySet) return null;
    return (
      <Modal
          show={this.state.editingPermissions}
          onHide={this.closePermissionsPanel}>
        <Modal.Header closeButton>
          <Modal.Title>Manage permissions for entity set: {this.props.entitySet.title}</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <PermissionsPanel entitySetId={this.props.entitySet.id} />
        </Modal.Body>
      </Modal>
    );
  };

  renderSearchEntitySet = () => {
    if (!this.props.entitySet) return null;
    return (

        <Button bsStyle="primary" className={styles.center}>
          <Link className={styles.buttonLink} to={`/search/${this.props.entitySet.id}`}>
            <FontAwesome name="search" />
            Search this entity set
          </Link>
        </Button>

    );
  }

  renderAddDataForm = () => {
    if (!this.props.entitySet || !this.props.entitySetPermissions.WRITE) return null;
    return (
      <Modal
          show={this.state.addingData}
          onHide={this.closeAddDataModal}>
        <Modal.Header closeButton>
          <Modal.Title>Add a new row of data to this entity set</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <AddDataForm
              entitySetId={this.props.entitySet.id}
              primaryKey={this.props.entitySet.entityType.key}
              propertyTypes={this.props.entitySet.entityType.properties} />
        </Modal.Body>
      </Modal>
    );
  }

  deleteEntitySet = () => {
    if (!this.props.entitySet) return;
    EntityDataModelApi.deleteEntitySet(this.props.entitySet.id)
    .then(() => {
      this.props.router.push(`/${PageConsts.HOME}`);
    }).catch(() => {
      this.setState({
        confirmingDelete: false,
        deleteError: true
      });
    });
  }

  setConfirmingDelete = () => {
    this.setState({ confirmingDelete: true });
  }

  cancelDelete = () => {
    this.setState({ confirmingDelete: false });
  }

  openAddDataModal = () => {
    this.setState({ addingData: true });
  }

  closeAddDataModal = () => {
    this.setState({ addingData: false });
  }

  renderAddDataButton = () => {
    if (!this.props.entitySet || !this.props.entitySetPermissions.WRITE) return null;
    return (

        <Button
            bsStyle="success"
            className={styles.center}
            onClick={this.openAddDataModal}>
          <FontAwesome name="plus" size="lg" />
          Add Data
        </Button>

    );
  }

  renderDeleteEntitySet = () => {
    if (!this.props.entitySet || !this.props.entitySetPermissions.OWNER) return null;
    const error = (this.state.deleteError) ?
Unable to delete entity set
 : null;
    return (

        <Button
            bsStyle="danger"
            className={styles.center}
            onClick={this.setConfirmingDelete}>
          <FontAwesome name="times" size="lg" />
          Delete this entity set
        </Button>
        {error}

    );
  }

  renderConfirmDeleteModal = () => {
    if (!this.props.entitySet) return null;
    return (
      <Modal
          show={this.state.confirmingDelete}
          onHide={this.cancelDelete}>
        <Modal.Header closeButton>
          <Modal.Title>Are you sure you want to delete {this.props.entitySet.title}?</Modal.Title>
        </Modal.Header>
        <Modal.Body>

            <Button onClick={this.deleteEntitySet} bsStyle="danger">Confirm</Button>

        </Modal.Body>
      </Modal>
    );
  };

  closePermissionsPanel = () => {
    this.setState({ editingPermissions: false });
  };

  render() {
    return (
      <Page>
        <Page.Header>
          <AsyncContent {...this.props.asyncState} content={this.renderHeaderContent} />
        </Page.Header>
        <Page.Body>
          {this.renderAddDataButton()}

Data in Entity Set

          <AsyncContent
              {...this.props.asyncState}
              content={() => {
                // TODO: Remove when removing denormalization
                const propertyTypeIds = this.props.entitySet.entityType.properties.map((property) => {
                  return property.id;
                });
                return (
                  <PropertyTypeList
                      entitySetId={this.props.entitySet.id}
                      propertyTypeIds={propertyTypeIds}
                      className="propertyTypeStyleDefault" />
                );
              }} />
          {this.renderAddDataForm()}
          {this.renderPermissionsPanel()}
          {this.renderSearchEntitySet()}
          {this.renderDeleteEntitySet()}
          {this.renderConfirmDeleteModal()}

        </Page.Body>
      </Page>
    );
  }

  componentDidMount() {
    this.props.loadEntitySet();
  }
}

function mapStateToProps(state) {
  const entitySetDetail = state.get('entitySetDetail');
  const normalizedData = state.get('normalizedData');
  const permissions = state.get('permissions');

  let entitySet;
  let entitySetPermissions;
  const reference = entitySetDetail.get('entitySetReference');
  if (reference) {
    entitySet = getEdmObject(normalizedData.toJS(), reference.toJS());
    entitySetPermissions = getPermissions(permissions, [entitySet.id]);
  }
  else {
    entitySetPermissions = DEFAULT_PERMISSIONS;
  }

  return {
    asyncState: entitySetDetail.get('asyncState').toJS(),
    entitySet,
    entitySetPermissions
  };
}

function mapDispatchToProps(dispatch, ownProps) {
  const id = ownProps.params.id;
  return {
    loadEntitySet: () => {
      dispatch(actionFactories.entitySetDetailRequest(id));
      dispatch(PermissionsActionFactory.getEntitySetsAuthorizations([id]));
      // TODO: Move filter creation in helper function in EdmApi
      dispatch(edmActionFactories.filteredEdmRequest(
        [{
          type: 'EntitySet',
          id,
          include: ['EntitySet', 'EntityType', 'PropertyTypeInEntitySet']
        }]
      ));
    }
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(EntitySetDetailComponent);
<commit_msg>move button inside link<commit_after>import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import { Button, Modal } from 'react-bootstrap';
import styled from 'styled-components';
import FontAwesome from 'react-fontawesome';
import classnames from 'classnames';

import {
  EntityDataModelApi
} from 'loom-data';

import * as actionFactories from './EntitySetDetailActionFactories';
import * as edmActionFactories from '../edm/EdmActionFactories';
import * as PermissionsActionFactory from '../permissions/PermissionsActionFactory';
import EntitySetPermissionsRequestList from '../permissions/components/EntitySetPermissionsRequestList';
import { PermissionsPropType, getPermissions, DEFAULT_PERMISSIONS } from '../permissions/PermissionsStorage';
import { getEdmObject } from '../edm/EdmStorage';
import PropertyTypeList from '../edm/components/PropertyTypeList';
import { PermissionsPanel } from '../../views/Main/Schemas/Components/PermissionsPanel';
import AddDataForm from '../entitysetforms/AddDataForm';
import ActionDropdown from '../edm/components/ActionDropdown';
import AsyncContent, { AsyncStatePropType } from '../../components/asynccontent/AsyncContent';
import { EntitySetPropType } from '../edm/EdmModel';
import Page from '../../components/page/Page';
import PageConsts from '../../utils/Consts/PageConsts';
import styles from './entitysetdetail.module.css';

import StyledFlexContainer from '../../components/flex/StyledFlexContainer';
import StyledFlexContainerStacked from '../../components/flex/StyledFlexContainerStacked';

const TitleControlsContainer = styled(StyledFlexContainer)`
  justify-content: space-between;
`;

const ControlsContainer = styled(StyledFlexContainerStacked)`
  align-items: flex-end;
`;

class EntitySetDetailComponent extends React.Component {
  static propTypes = {
    asyncState: AsyncStatePropType.isRequired,
    router: PropTypes.object.isRequired,

    // Async content
    entitySet: EntitySetPropType,
    entitySetPermissions: PermissionsPropType.isRequired,

    // Loading
    loadEntitySet: PropTypes.func.isRequired
  };

  constructor(props) {
    super(props);
    this.state = {
      editingPermissions: false,
      confirmingDelete: false,
      addingData: false,
      deleteError: false
    };
  }

  setEditingPermissions = () => {
    this.setState({ editingPermissions: true });
  };

  renderHeaderContent = () => {
    const { entitySet, entitySetPermissions } = this.props;

    const contactValue = (entitySet.contacts.length > 0) ? entitySet.contacts.join(', ') : 'none';

    return (

    );
  };

  renderPermissionsPanel = () => {
    if (!this.props.entitySet) return null;
    return (
      <Modal
          show={this.state.editingPermissions}
          onHide={this.closePermissionsPanel}>
        <Modal.Header closeButton>
          <Modal.Title>Manage permissions for entity set: {this.props.entitySet.title}</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <PermissionsPanel entitySetId={this.props.entitySet.id} />
        </Modal.Body>
      </Modal>
    );
  };

  renderSearchEntitySet = () => {
    if (!this.props.entitySet) return null;
    return (

        <Link className={styles.buttonLink} to={`/search/${this.props.entitySet.id}`}>
          <Button bsStyle="primary" className={styles.center}>
            <FontAwesome name="search" />
            Search this entity set
          </Button>
        </Link>

    );
  }

  renderAddDataForm = () => {
    if (!this.props.entitySet || !this.props.entitySetPermissions.WRITE) return null;
    return (
      <Modal
          show={this.state.addingData}
          onHide={this.closeAddDataModal}>
        <Modal.Header closeButton>
          <Modal.Title>Add a new row of data to this entity set</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <AddDataForm
              entitySetId={this.props.entitySet.id}
              primaryKey={this.props.entitySet.entityType.key}
              propertyTypes={this.props.entitySet.entityType.properties} />
        </Modal.Body>
      </Modal>
    );
  }

  deleteEntitySet = () => {
    if (!this.props.entitySet) return;
    EntityDataModelApi.deleteEntitySet(this.props.entitySet.id)
    .then(() => {
      this.props.router.push(`/${PageConsts.HOME}`);
    }).catch(() => {
      this.setState({
        confirmingDelete: false,
        deleteError: true
      });
    });
  }

  setConfirmingDelete = () => {
    this.setState({ confirmingDelete: true });
  }

  cancelDelete = () => {
    this.setState({ confirmingDelete: false });
  }

  openAddDataModal = () => {
    this.setState({ addingData: true });
  }

  closeAddDataModal = () => {
    this.setState({ addingData: false });
  }

  renderAddDataButton = () => {
    if (!this.props.entitySet || !this.props.entitySetPermissions.WRITE) return null;
    return (

        <Button
            bsStyle="success"
            className={styles.center}
            onClick={this.openAddDataModal}>
          <FontAwesome name="plus" size="lg" />
          Add Data
        </Button>

    );
  }

  renderDeleteEntitySet = () => {
    if (!this.props.entitySet || !this.props.entitySetPermissions.OWNER) return null;
    const error = (this.state.deleteError) ?
Unable to delete entity set
 : null;
    return (

        <Button
            bsStyle="danger"
            className={styles.center}
            onClick={this.setConfirmingDelete}>
          <FontAwesome name="times" size="lg" />
          Delete this entity set
        </Button>
        {error}

    );
  }

  renderConfirmDeleteModal = () => {
    if (!this.props.entitySet) return null;
    return (
      <Modal
          show={this.state.confirmingDelete}
          onHide={this.cancelDelete}>
        <Modal.Header closeButton>
          <Modal.Title>Are you sure you want to delete {this.props.entitySet.title}?</Modal.Title>
        </Modal.Header>
        <Modal.Body>

            <Button onClick={this.deleteEntitySet} bsStyle="danger">Confirm</Button>

        </Modal.Body>
      </Modal>
    );
  };

  closePermissionsPanel = () => {
    this.setState({ editingPermissions: false });
  };

  render() {
    return (
      <Page>
        <Page.Header>
          <AsyncContent {...this.props.asyncState} content={this.renderHeaderContent} />
        </Page.Header>
        <Page.Body>
          {this.renderAddDataButton()}

Data in Entity Set

          <AsyncContent
              {...this.props.asyncState}
              content={() => {
                // TODO: Remove when removing denormalization
                const propertyTypeIds = this.props.entitySet.entityType.properties.map((property) => {
                  return property.id;
                });
                return (
                  <PropertyTypeList
                      entitySetId={this.props.entitySet.id}
                      propertyTypeIds={propertyTypeIds}
                      className="propertyTypeStyleDefault" />
                );
              }} />
          {this.renderAddDataForm()}
          {this.renderPermissionsPanel()}
          {this.renderSearchEntitySet()}
          {this.renderDeleteEntitySet()}
          {this.renderConfirmDeleteModal()}

        </Page.Body>
      </Page>
    );
  }

  componentDidMount() {
    this.props.loadEntitySet();
  }
}

function mapStateToProps(state) {
  const entitySetDetail = state.get('entitySetDetail');
  const normalizedData = state.get('normalizedData');
  const permissions = state.get('permissions');

  let entitySet;
  let entitySetPermissions;
  const reference = entitySetDetail.get('entitySetReference');
  if (reference) {
    entitySet = getEdmObject(normalizedData.toJS(), reference.toJS());
    entitySetPermissions = getPermissions(permissions, [entitySet.id]);
  }
  else {
    entitySetPermissions = DEFAULT_PERMISSIONS;
  }

  return {
    asyncState: entitySetDetail.get('asyncState').toJS(),
    entitySet,
    entitySetPermissions
  };
}

function mapDispatchToProps(dispatch, ownProps) {
  const id = ownProps.params.id;
  return {
    loadEntitySet: () => {
      dispatch(actionFactories.entitySetDetailRequest(id));
      dispatch(PermissionsActionFactory.getEntitySetsAuthorizations([id]));
      // TODO: Move filter creation in helper function in EdmApi
      dispatch(edmActionFactories.filteredEdmRequest(
        [{
          type: 'EntitySet',
          id,
          include: ['EntitySet', 'EntityType', 'PropertyTypeInEntitySet']
        }]
      ));
    }
  };
}

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