<commit_before>/*!
 * ${copyright}
 */
sap.ui.define([
	"sap/base/Log"
], function(Log) {
	"use strict";

	function getQueryString(sURL) {
		var iHash = sURL.indexOf('#');
		if (iHash >= 0) {
			sURL = sURL.slice(0, iHash);
		}
		var iSearch = sURL.indexOf('?');
		if (iSearch >= 0 ) {
			return sURL.slice(iSearch + 1);
		}
		return "";
	}

	function decode(str) {
		return decodeURIComponent(str.replace(/\+/g,' '));
	}

	function parse(mParams, sQueryString) {
		function append(sName, sValue) {
			if ( sName in mParams ) {
				mParams[sName].push(sValue);
			} else {
				mParams[sName] = [sValue];
			}
		}

		sQueryString.split("&").forEach(function(sName) {
			var iPos = sName.indexOf("=");
			if ( iPos >= 0 ) {
				append(decode(sName.slice(0, iPos)), decode(sName.slice(iPos + 1)));
			} else if ( sName.length ) {
				append(decode(sName), "");
			}
		});
	}

	/**
	 * @class Provides access to the individual parameters of a URL query string.
	 *
	 * This class parses the query string from a URL and provides access to the values of individual parameters.
	 * There are methods to check whether the query string {@link #has contains a parameter (has())},
	 * to {@link #get get a single value (get())} for a parameter and to {@link #getAll get a list
	 * of all values (getAll())} for a parameter. Another method allows to {@link #keys iterate over
	 * all parameter names (keys())}.
	 *
	 * The signature and behavior of those methods is aligned with the corresponding methods of the upcoming Web API
	 * URLSearchParams.
	 *
	 *
Decoding

	 *
	 * The constructor and the factory methods expect percentage encoded input whereas all other APIs expect and
	 * return decoded strings. After parsing the query string, any plus sign (0x2b) in names or values is replaced
	 * by a blank (0x20) and the resulting strings are percentage decoded (decodeURIComponent).
	 *
	 *
	 *
Future Migration

	 *
	 * Note: To simplify a future migration from this class to the standard URLSearchParams
	 * API, consuming code should follow some recommendations:
	 *

	 *
do not use the constructor, either use {@link #.fromURL UriParameters.fromURL} when the input is a full URL,
	 *     or use {@link #.fromQuery UriParameters.fromQuery} when the input only contains the query part of an URL
	 *     (e.g. location.search).

	 *
do not use the get method with the second parameter bAll; use the getAll
	 *     method instead

	 *
do not access the internal property mParams (you never should access internal properties of
	 *     UI5 classes or objects). With the predecessor of this API, access to mParams was often used
	 *     to check whether a parameter is defined at all. Using the new has method or checking the
	 *     result of get against null serves the same purpose.

	 *

	 * Callers using UriParameters.fromQuery(input) can be migrated to new URLSearchParams(input)
	 * once the new API is available in all supported browsers. Callers using UriParameters.fromURL(input)
	 * can be migrated to new URL(input).searchParams then.
	 *
	 * @since 1.68
	 * @alias module:sap/base/util/UriParameters
	 * @param {string} [sURL] URL with parameters
	 * @public
	 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
	 * @constructor-deprecated As of 1.68, using the constructor has been deprecated. Either use {@link #.fromURL} or {@link #.fromQuery}.
	 */
	var UriParameters = function(sURL) {

		var mParams = Object.create(null);

		if ( sURL != null ) {
			// validate input
			if ( typeof sURL !== "string" ) {
				throw new TypeError("query parameter must be a string");
			}
			// For compatibility reasons, we accept a URL and extract the query string from it
			// Note that is different from the behavior of the standard URLSearchParams API
			// which interprets any input as query string
			parse(mParams, getQueryString(sURL));
		}

		/**
		 * Checks whether a parameter occurs at least once in the query string.
		 *
		 * @example
		 *
		 * UriParameters.fromQuery("?foo=bar").has("foo") === true
		 * UriParameters.fromQuery("?foo").get("foo") === true
		 * UriParameters.fromQuery("?foo").get("bar") === false
		 * UriParameters.fromQuery("?foo+bar=ba%7a").get("foo bar") === true
		 *
		 * @param {string} sName Name of the query parameter to check
		 * @returns {boolean} Whether the parameter has been defined
		 * @public
		 */
		this.has = function(sName) {
			return sName in mParams;
		};

		/**
		 * Internal implementation of get
		 * @private
		 */
		this._get = function(sName, bAll) {
			return sName in mParams ? mParams[sName][0] : null;
		};

		/**
		 * Returns all values of the query parameter with the given name.
		 *
		 * An array of string values of all occurrences of the parameter with the given name is returned.
		 * This array is empty if (and only if) the parameter does not occur in the query string.
		 *
		 * @example
		 * // pseudo code, '===' here means 'deep equals'
		 * UriParameters.fromQuery("?foo=bar").getAll("foo") === ["bar"]
		 * UriParameters.fromQuery("?foo").getAll("foo") === [""]
		 * UriParameters.fromQuery("?foo").getAll("bar") === []
		 * UriParameters.fromQuery("?foo=bar&foo=baz").getAll("foo") === ["bar","baz"]
		 *
		 * @param {string} sName Name of the query parameter
		 * @returns {string[]} Array with all values of the query parameter with the given name
		 * @SecSource {return|XSS} Return values might contain external URL parameters
		 * @public
		 */
		this.getAll = function(sName) {
			return sName in mParams ? mParams[sName].slice() : [];
		};

		/**
		 * Returns an iterator for all contained parameter names.
		 *
		 * @example
Using keys() iterator without ES6 syntax

		 *
		 * var params = UriParameters.fromQuery("?a=1&b=2&c=3");
		 * var keys = Array.from(params.keys()); // ["a", "b", "c"]
		 *
		 * @returns {Iterator<string>} Iterator for all parameter names.
		 * @public
		 */
		this.keys = function() {
			return Object.keys(mParams).values(); // requires ES6 API, but not ES6 syntax
		};

		// marker to log only once per instance
		var bParamWarning = false;

		/*
		 * As many consumers of the old API used this internal property, we make it available but log a warning.
		 * Using this property prevents a later migration to the URLSearchParams API.
		 */
		Object.defineProperty(this, "mParams", {
			get: function() {
				if ( !bParamWarning ) {
					Log.warning("[Deprecated] UriParameters.mParams must not be accessed.");
					bParamWarning = true;
				}
				return Object.assign({}, mParams);
			},
			configurable: false
		});

	};

	/**
	 * Returns the first value of the named query parameter.
	 *
	 * The value of the first occurrence of the parameter with name sName in the query
	 * string is returned. If that first occurrence does not contain a value (it does not contain an equal
	 * sign), then an empty string is returned.
	 *
	 * If (and only if) the parameter does not occur in the query string, null is returned.
	 *
	 * @example
	 *
	 * UriParameters.fromQuery("?foo=bar").get("foo") === "bar"
	 * UriParameters.fromQuery("?foo").get("foo") === ""
	 * UriParameters.fromQuery("?foo").get("bar") === null
	 * UriParameters.fromQuery("?foo+bar=ba%7a").get("foo bar") === "baz"
	 *
	 * @param {string} sName Name of the query parameter to get the value for
	 * @param {boolean} [bAll=false] Whether all values for the parameter should be returned; the use of
	 *   this parameter is deprecated and highly discouraged; use the {@link #getAll} method instead
	 * @return {string|null} First value of the query parameter with the given name or null
	 * @SecSource {return|XSS} Return value contains URL parameters
	 * @public
	 */
	UriParameters.prototype.get = function(sName, bAll) {
		// Note: this method is still implemented on the prototype to keep existing mocks working
		if ( bAll ) {
			// LOG
			Log.warning("[Deprecated] UriParameters.get(..., true) must not be used, use getAll() instead.");
			return this.getAll(sName);
		}
		return this._get(sName);
	};

	/**
	 * Parses the query portion of the given URL and returns an object to access the individual
	 * parameters.
	 *
	 * Callers using UriParameters.fromURL(input) can be migrated to
	 * new URL(input).searchParams once that API is available (or polyfilled) in all supported browsers.
	 *
	 * @param {string} sURL to parse the query portion of.
	 * @returns {module:sap/base/util/UriParameters} Object providing read access to the query parameters
	 * @public
	 */
	UriParameters.fromURL = function(sUrl) {
		return new UriParameters(sUrl);
	};

	/**
	 * Parses the given query string and returns an interface to access the individual parameters.
	 *
	 * Callers using UriParameters.fromQuery(input) can be migrated to
	 * new URLSearchParams(input) once that API is available (or polyfilled) in all supported browsers.
	 *
	 * @param {string} [sQuery=""] Query string to parse, a leading question mark (?) will be ignored
	 * @returns {module:sap/base/util/UriParameters} Object providing read access to the query parameters
	 * @public
	 */
	UriParameters.fromQuery = function(sQuery) {
		if ( typeof sQuery === "string" ) {
			if ( sQuery[0] !== "?" ) {
				sQuery = "?" + sQuery;
			}
			// encode hash signs to prevent them from being cut-off
			sQuery = sQuery.replace(/#/g, "%23");
		}
		return new UriParameters(sQuery);
	};

	return UriParameters;
});
<commit_msg>[INTERNAL] base.util.UrlParameters: fix JSDoc samples for method 'has'<commit_after>/*!
 * ${copyright}
 */
sap.ui.define([
	"sap/base/Log"
], function(Log) {
	"use strict";

	function getQueryString(sURL) {
		var iHash = sURL.indexOf('#');
		if (iHash >= 0) {
			sURL = sURL.slice(0, iHash);
		}
		var iSearch = sURL.indexOf('?');
		if (iSearch >= 0 ) {
			return sURL.slice(iSearch + 1);
		}
		return "";
	}

	function decode(str) {
		return decodeURIComponent(str.replace(/\+/g,' '));
	}

	function parse(mParams, sQueryString) {
		function append(sName, sValue) {
			if ( sName in mParams ) {
				mParams[sName].push(sValue);
			} else {
				mParams[sName] = [sValue];
			}
		}

		sQueryString.split("&").forEach(function(sName) {
			var iPos = sName.indexOf("=");
			if ( iPos >= 0 ) {
				append(decode(sName.slice(0, iPos)), decode(sName.slice(iPos + 1)));
			} else if ( sName.length ) {
				append(decode(sName), "");
			}
		});
	}

	/**
	 * @class Provides access to the individual parameters of a URL query string.
	 *
	 * This class parses the query string from a URL and provides access to the values of individual parameters.
	 * There are methods to check whether the query string {@link #has contains a parameter (has())},
	 * to {@link #get get a single value (get())} for a parameter and to {@link #getAll get a list
	 * of all values (getAll())} for a parameter. Another method allows to {@link #keys iterate over
	 * all parameter names (keys())}.
	 *
	 * The signature and behavior of those methods is aligned with the corresponding methods of the upcoming Web API
	 * URLSearchParams.
	 *
	 *
Decoding

	 *
	 * The constructor and the factory methods expect percentage encoded input whereas all other APIs expect and
	 * return decoded strings. After parsing the query string, any plus sign (0x2b) in names or values is replaced
	 * by a blank (0x20) and the resulting strings are percentage decoded (decodeURIComponent).
	 *
	 *
	 *
Future Migration

	 *
	 * Note: To simplify a future migration from this class to the standard URLSearchParams
	 * API, consuming code should follow some recommendations:
	 *

	 *
do not use the constructor, either use {@link #.fromURL UriParameters.fromURL} when the input is a full URL,
	 *     or use {@link #.fromQuery UriParameters.fromQuery} when the input only contains the query part of an URL
	 *     (e.g. location.search).

	 *
do not use the get method with the second parameter bAll; use the getAll
	 *     method instead

	 *
do not access the internal property mParams (you never should access internal properties of
	 *     UI5 classes or objects). With the predecessor of this API, access to mParams was often used
	 *     to check whether a parameter is defined at all. Using the new has method or checking the
	 *     result of get against null serves the same purpose.

	 *

	 * Callers using UriParameters.fromQuery(input) can be migrated to new URLSearchParams(input)
	 * once the new API is available in all supported browsers. Callers using UriParameters.fromURL(input)
	 * can be migrated to new URL(input).searchParams then.
	 *
	 * @since 1.68
	 * @alias module:sap/base/util/UriParameters
	 * @param {string} [sURL] URL with parameters
	 * @public
	 * @see https://url.spec.whatwg.org/#interface-urlsearchparams
	 * @constructor-deprecated As of 1.68, using the constructor has been deprecated. Either use {@link #.fromURL} or {@link #.fromQuery}.
	 */
	var UriParameters = function(sURL) {

		var mParams = Object.create(null);

		if ( sURL != null ) {
			// validate input
			if ( typeof sURL !== "string" ) {
				throw new TypeError("query parameter must be a string");
			}
			// For compatibility reasons, we accept a URL and extract the query string from it
			// Note that is different from the behavior of the standard URLSearchParams API
			// which interprets any input as query string
			parse(mParams, getQueryString(sURL));
		}

		/**
		 * Checks whether a parameter occurs at least once in the query string.
		 *
		 * @example
		 *
		 * UriParameters.fromQuery("?foo=bar").has("foo") === true
		 * UriParameters.fromQuery("?foo").has("foo") === true
		 * UriParameters.fromQuery("?foo").has("bar") === false
		 * UriParameters.fromQuery("?foo+bar=ba%7a").has("foo bar") === true
		 *
		 * @param {string} sName Name of the query parameter to check
		 * @returns {boolean} Whether the parameter has been defined
		 * @public
		 */
		this.has = function(sName) {
			return sName in mParams;
		};

		/**
		 * Internal implementation of get
		 * @private
		 */
		this._get = function(sName, bAll) {
			return sName in mParams ? mParams[sName][0] : null;
		};

		/**
		 * Returns all values of the query parameter with the given name.
		 *
		 * An array of string values of all occurrences of the parameter with the given name is returned.
		 * This array is empty if (and only if) the parameter does not occur in the query string.
		 *
		 * @example
		 * // pseudo code, '===' here means 'deep equals'
		 * UriParameters.fromQuery("?foo=bar").getAll("foo") === ["bar"]
		 * UriParameters.fromQuery("?foo").getAll("foo") === [""]
		 * UriParameters.fromQuery("?foo").getAll("bar") === []
		 * UriParameters.fromQuery("?foo=bar&foo=baz").getAll("foo") === ["bar","baz"]
		 *
		 * @param {string} sName Name of the query parameter
		 * @returns {string[]} Array with all values of the query parameter with the given name
		 * @SecSource {return|XSS} Return values might contain external URL parameters
		 * @public
		 */
		this.getAll = function(sName) {
			return sName in mParams ? mParams[sName].slice() : [];
		};

		/**
		 * Returns an iterator for all contained parameter names.
		 *
		 * @example
Using keys() iterator without ES6 syntax

		 *
		 * var params = UriParameters.fromQuery("?a=1&b=2&c=3");
		 * var keys = Array.from(params.keys()); // ["a", "b", "c"]
		 *
		 * @returns {Iterator<string>} Iterator for all parameter names.
		 * @public
		 */
		this.keys = function() {
			return Object.keys(mParams).values(); // requires ES6 API, but not ES6 syntax
		};

		// marker to log only once per instance
		var bParamWarning = false;

		/*
		 * As many consumers of the old API used this internal property, we make it available but log a warning.
		 * Using this property prevents a later migration to the URLSearchParams API.
		 */
		Object.defineProperty(this, "mParams", {
			get: function() {
				if ( !bParamWarning ) {
					Log.warning("[Deprecated] UriParameters.mParams must not be accessed.");
					bParamWarning = true;
				}
				return Object.assign({}, mParams);
			},
			configurable: false
		});

	};

	/**
	 * Returns the first value of the named query parameter.
	 *
	 * The value of the first occurrence of the parameter with name sName in the query
	 * string is returned. If that first occurrence does not contain a value (it does not contain an equal
	 * sign), then an empty string is returned.
	 *
	 * If (and only if) the parameter does not occur in the query string, null is returned.
	 *
	 * @example
	 *
	 * UriParameters.fromQuery("?foo=bar").get("foo") === "bar"
	 * UriParameters.fromQuery("?foo").get("foo") === ""
	 * UriParameters.fromQuery("?foo").get("bar") === null
	 * UriParameters.fromQuery("?foo+bar=ba%7a").get("foo bar") === "baz"
	 *
	 * @param {string} sName Name of the query parameter to get the value for
	 * @param {boolean} [bAll=false] Whether all values for the parameter should be returned; the use of
	 *   this parameter is deprecated and highly discouraged; use the {@link #getAll} method instead
	 * @return {string|null} First value of the query parameter with the given name or null
	 * @SecSource {return|XSS} Return value contains URL parameters
	 * @public
	 */
	UriParameters.prototype.get = function(sName, bAll) {
		// Note: this method is still implemented on the prototype to keep existing mocks working
		if ( bAll ) {
			// LOG
			Log.warning("[Deprecated] UriParameters.get(..., true) must not be used, use getAll() instead.");
			return this.getAll(sName);
		}
		return this._get(sName);
	};

	/**
	 * Parses the query portion of the given URL and returns an object to access the individual
	 * parameters.
	 *
	 * Callers using UriParameters.fromURL(input) can be migrated to
	 * new URL(input).searchParams once that API is available (or polyfilled) in all supported browsers.
	 *
	 * @param {string} sURL to parse the query portion of.
	 * @returns {module:sap/base/util/UriParameters} Object providing read access to the query parameters
	 * @public
	 */
	UriParameters.fromURL = function(sUrl) {
		return new UriParameters(sUrl);
	};

	/**
	 * Parses the given query string and returns an interface to access the individual parameters.
	 *
	 * Callers using UriParameters.fromQuery(input) can be migrated to
	 * new URLSearchParams(input) once that API is available (or polyfilled) in all supported browsers.
	 *
	 * @param {string} [sQuery=""] Query string to parse, a leading question mark (?) will be ignored
	 * @returns {module:sap/base/util/UriParameters} Object providing read access to the query parameters
	 * @public
	 */
	UriParameters.fromQuery = function(sQuery) {
		if ( typeof sQuery === "string" ) {
			if ( sQuery[0] !== "?" ) {
				sQuery = "?" + sQuery;
			}
			// encode hash signs to prevent them from being cut-off
			sQuery = sQuery.replace(/#/g, "%23");
		}
		return new UriParameters(sQuery);
	};

	return UriParameters;
});
<|endoftext|>


<commit_before>module.exports = WebSocketTracker

var debug = require('debug')('bittorrent-tracker:websocket-tracker')
var extend = require('xtend')
var hat = require('hat')
var inherits = require('inherits')
var Peer = require('simple-peer')
var Socket = require('simple-websocket')

var common = require('../common')
var Tracker = require('./tracker')

// Use a socket pool, so tracker clients share WebSocket objects for the same server.
// In practice, WebSockets are pretty slow to establish, so this gives a nice performance
// boost, and saves browser resources.
var socketPool = {}

var RECONNECT_MINIMUM = 15 * 1000
var RECONNECT_MAXIMUM = 30 * 60 * 1000
var RECONNECT_VARIANCE = 30 * 1000
var OFFER_TIMEOUT = 50 * 1000

inherits(WebSocketTracker, Tracker)

function WebSocketTracker (client, announceUrl, opts) {
  var self = this
  Tracker.call(self, client, announceUrl)
  debug('new websocket tracker %s', announceUrl)

  self.peers = {} // peers (offer id -> peer)
  self.socket = null
  self.reconnecting = false
  self.retries = 0

  self._openSocket()
}

WebSocketTracker.prototype.DEFAULT_ANNOUNCE_INTERVAL = 30 * 1000 // 30 seconds

WebSocketTracker.prototype.announce = function (opts) {
  var self = this
  if (self.destroyed || self.reconnecting) return
  if (!self.socket.connected) {
    return self.socket.once('connect', self.announce.bind(self, opts))
  }

  // Limit the number of offers that are generated, since it can be slow
  var numwant = Math.min(opts.numwant, 10)

  self._generateOffers(numwant, function (offers) {
    var params = extend(opts, {
      action: 'announce',
      numwant: numwant,
      info_hash: self.client._infoHashBinary,
      peer_id: self.client._peerIdBinary,
      offers: offers
    })
    if (self._trackerId) params.trackerid = self._trackerId

    self._send(params)
  })
}

WebSocketTracker.prototype.scrape = function (opts) {
  var self = this
  if (self.destroyed || self.reconnecting) return
  if (!self.socket.connected) {
    return self.socket.once('connect', self.scrape.bind(self, opts))
  }

  var infoHashes = (Array.isArray(opts.infoHash) && opts.infoHash.length > 0)
    ? opts.infoHash.map(function (infoHash) {
      return infoHash.toString('binary')
    })
    : (opts.infoHash && opts.infoHash.toString('binary')) || self.client._infoHashBinary
  var params = {
    action: 'scrape',
    info_hash: infoHashes
  }

  self._send(params)
}

WebSocketTracker.prototype.destroy = function (cb) {
  var self = this
  if (!cb) cb = noop
  if (self.destroyed) return cb(null)
  self.destroyed = true
  clearInterval(self.interval)
  clearTimeout(self.reconnectTimer)

  if (socketPool[self.announceUrl]) socketPool[self.announceUrl].consumers--

  self.socket.removeListener('connect', self._onSocketConnectBound)
  self.socket.removeListener('data', self._onSocketDataBound)
  self.socket.removeListener('close', self._onSocketCloseBound)
  self.socket.removeListener('error', self._onSocketErrorBound)

  self._onSocketConnectBound = null
  self._onSocketErrorBound = null
  self._onSocketDataBound = null
  self._onSocketCloseBound = null

  if (socketPool[self.announceUrl].consumers === 0) {
    delete socketPool[self.announceUrl]

    self.socket.on('error', noop) // ignore all future errors

    try {
      self.socket.destroy(cb)
    } catch (err) {
      if (cb) cb()
    }
  }

  self.socket = null
}

WebSocketTracker.prototype._openSocket = function () {
  var self = this
  self.destroyed = false

  self._onSocketConnectBound = self._onSocketConnect.bind(self)
  self._onSocketErrorBound = self._onSocketError.bind(self)
  self._onSocketDataBound = self._onSocketData.bind(self)
  self._onSocketCloseBound = self._onSocketClose.bind(self)

  self.socket = socketPool[self.announceUrl]
  if (!self.socket) {
    self.socket = socketPool[self.announceUrl] = new Socket(self.announceUrl)
    self.socket.consumers = 1
    self.socket.on('connect', self._onSocketConnectBound)
  } else {
    socketPool[self.announceUrl].consumers++
  }

  self.socket.on('data', self._onSocketDataBound)
  self.socket.on('close', self._onSocketCloseBound)
  self.socket.on('error', self._onSocketErrorBound)
}

WebSocketTracker.prototype._onSocketConnect = function () {
  var self = this
  if (self.destroyed) return

  if (self.reconnecting) {
    self.reconnecting = false
    self.retries = 0
    self.announce(self.client._defaultAnnounceOpts())
  }
}

WebSocketTracker.prototype._onSocketData = function (data) {
  var self = this
  if (self.destroyed) return

  try {
    data = JSON.parse(data)
  } catch (err) {
    self.client.emit('warning', new Error('Invalid tracker response'))
    return
  }

  if (data.action === 'announce' || data.offer || data.answer) {
    self._onAnnounceResponse(data)
  } else if (data.action === 'scrape') {
    self._onScrapeResponse(data)
  } else {
    self._onSocketError(new Error('invalid action in WS response: ' + data.action))
  }
}

WebSocketTracker.prototype._onAnnounceResponse = function (data) {
  var self = this

  if (data.info_hash !== self.client._infoHashBinary) {
    debug(
      'ignoring websocket data from %s for %s (looking for %s: reused socket)',
      self.announceUrl, common.binaryToHex(data.info_hash), self.client.infoHash
    )
    return
  }

  if (data.peer_id && data.peer_id === self.client._peerIdBinary) {
    // ignore offers/answers from this client
    return
  }

  debug(
    'received %s from %s for %s',
    JSON.stringify(data), self.announceUrl, self.client.infoHash
  )

  var failure = data['failure reason']
  if (failure) return self.client.emit('warning', new Error(failure))

  var warning = data['warning message']
  if (warning) self.client.emit('warning', new Error(warning))

  var interval = data.interval || data['min interval']
  if (interval) self.setInterval(interval * 1000)

  var trackerId = data['tracker id']
  if (trackerId) {
    // If absent, do not discard previous trackerId value
    self._trackerId = trackerId
  }

  if (data.complete != null) {
    self.client.emit('update', {
      announce: self.announceUrl,
      complete: data.complete,
      incomplete: data.incomplete
    })
  }

  var peer
  if (data.offer && data.peer_id) {
    debug('creating peer (from remote offer)')
    peer = new Peer({
      trickle: false,
      config: self.client._rtcConfig,
      wrtc: self.client._wrtc
    })
    peer.id = common.binaryToHex(data.peer_id)
    peer.once('signal', function (answer) {
      var params = {
        info_hash: self.client._infoHashBinary,
        peer_id: self.client._peerIdBinary,
        to_peer_id: data.peer_id,
        answer: answer,
        offer_id: data.offer_id
      }
      if (self._trackerId) params.trackerid = self._trackerId
      self._send(params)
    })
    peer.signal(data.offer)
    self.client.emit('peer', peer)
  }

  if (data.answer && data.peer_id) {
    var offerId = common.binaryToHex(data.offer_id)
    peer = self.peers[offerId]
    if (peer) {
      peer.id = common.binaryToHex(data.peer_id)
      peer.signal(data.answer)
      self.client.emit('peer', peer)

      clearTimeout(peer.trackerTimeout)
      peer.trackerTimeout = null
      delete self.peers[offerId]
    } else {
      debug('got unexpected answer: ' + JSON.stringify(data.answer))
    }
  }
}

WebSocketTracker.prototype._onScrapeResponse = function (data) {
  var self = this
  data = data.files || {}

  var keys = Object.keys(data)
  if (keys.length === 0) {
    self.client.emit('warning', new Error('invalid scrape response'))
    return
  }

  keys.forEach(function (infoHash) {
    var response = data[infoHash]
    // TODO: optionally handle data.flags.min_request_interval
    // (separate from announce interval)
    self.client.emit('scrape', {
      announce: self.announceUrl,
      infoHash: common.binaryToHex(infoHash),
      complete: response.complete,
      incomplete: response.incomplete,
      downloaded: response.downloaded
    })
  })
}

WebSocketTracker.prototype._onSocketClose = function () {
  var self = this
  if (self.destroyed) return
  self.destroy()
  self._startReconnectTimer()
}

WebSocketTracker.prototype._onSocketError = function (err) {
  var self = this
  if (self.destroyed) return
  self.destroy()
  // errors will often happen if a tracker is offline, so don't treat it as fatal
  self.client.emit('warning', err)
  self._startReconnectTimer()
}

WebSocketTracker.prototype._startReconnectTimer = function () {
  var self = this
  var ms = Math.floor(Math.random() * RECONNECT_VARIANCE) + Math.min(Math.pow(2, self.retries) * RECONNECT_MINIMUM, RECONNECT_MAXIMUM)

  self.reconnecting = true
  self.reconnectTimer = setTimeout(function () {
    self.retries++
    self._openSocket()
  }, ms)
  if (self.reconnectTimer.unref) self.reconnectTimer.unref()

  debug('reconnecting socket in %s ms', ms)
}

WebSocketTracker.prototype._send = function (params) {
  var self = this
  if (self.destroyed) return

  var message = JSON.stringify(params)
  debug('send %s', message)
  self.socket.send(message)
}

WebSocketTracker.prototype._generateOffers = function (numwant, cb) {
  var self = this
  var offers = []
  debug('generating %s offers', numwant)

  for (var i = 0; i < numwant; ++i) {
    generateOffer()
  }

  function generateOffer () {
    var offerId = hat(160)
    debug('creating peer (from _generateOffers)')
    var peer = self.peers[offerId] = new Peer({
      initiator: true,
      trickle: false,
      config: self.client._rtcConfig,
      wrtc: self.client._wrtc
    })
    peer.once('signal', function (offer) {
      offers.push({
        offer: offer,
        offer_id: common.hexToBinary(offerId)
      })
      checkDone()
    })
    peer.trackerTimeout = setTimeout(function () {
      debug('tracker timeout: destroying peer')
      peer.trackerTimeout = null
      delete self.peers[offerId]
      peer.destroy()
    }, OFFER_TIMEOUT)
  }

  function checkDone () {
    if (offers.length === numwant) {
      debug('generated %s offers', numwant)
      cb(offers)
    }
  }
}

function noop () {}
<commit_msg>Destroy peers and tracker timeouts on destroy<commit_after>module.exports = WebSocketTracker

var debug = require('debug')('bittorrent-tracker:websocket-tracker')
var extend = require('xtend')
var hat = require('hat')
var inherits = require('inherits')
var Peer = require('simple-peer')
var Socket = require('simple-websocket')

var common = require('../common')
var Tracker = require('./tracker')

// Use a socket pool, so tracker clients share WebSocket objects for the same server.
// In practice, WebSockets are pretty slow to establish, so this gives a nice performance
// boost, and saves browser resources.
var socketPool = {}

var RECONNECT_MINIMUM = 15 * 1000
var RECONNECT_MAXIMUM = 30 * 60 * 1000
var RECONNECT_VARIANCE = 30 * 1000
var OFFER_TIMEOUT = 50 * 1000

inherits(WebSocketTracker, Tracker)

function WebSocketTracker (client, announceUrl, opts) {
  var self = this
  Tracker.call(self, client, announceUrl)
  debug('new websocket tracker %s', announceUrl)

  self.peers = {} // peers (offer id -> peer)
  self.socket = null
  self.reconnecting = false
  self.retries = 0

  self._openSocket()
}

WebSocketTracker.prototype.DEFAULT_ANNOUNCE_INTERVAL = 30 * 1000 // 30 seconds

WebSocketTracker.prototype.announce = function (opts) {
  var self = this
  if (self.destroyed || self.reconnecting) return
  if (!self.socket.connected) {
    return self.socket.once('connect', self.announce.bind(self, opts))
  }

  // Limit the number of offers that are generated, since it can be slow
  var numwant = Math.min(opts.numwant, 10)

  self._generateOffers(numwant, function (offers) {
    var params = extend(opts, {
      action: 'announce',
      numwant: numwant,
      info_hash: self.client._infoHashBinary,
      peer_id: self.client._peerIdBinary,
      offers: offers
    })
    if (self._trackerId) params.trackerid = self._trackerId

    self._send(params)
  })
}

WebSocketTracker.prototype.scrape = function (opts) {
  var self = this
  if (self.destroyed || self.reconnecting) return
  if (!self.socket.connected) {
    return self.socket.once('connect', self.scrape.bind(self, opts))
  }

  var infoHashes = (Array.isArray(opts.infoHash) && opts.infoHash.length > 0)
    ? opts.infoHash.map(function (infoHash) {
      return infoHash.toString('binary')
    })
    : (opts.infoHash && opts.infoHash.toString('binary')) || self.client._infoHashBinary
  var params = {
    action: 'scrape',
    info_hash: infoHashes
  }

  self._send(params)
}

WebSocketTracker.prototype.destroy = function (cb) {
  var self = this
  if (!cb) cb = noop
  if (self.destroyed) return cb(null)
  self.destroyed = true
  clearInterval(self.interval)
  clearTimeout(self.reconnectTimer)

  // Destroy peers
  for(var peerId in self.peers) {
    var peer = self.peers[peerId]
    clearTimeout(peer.trackerTimeout)
    peer.destroy()
  }
  delete self.peers

  // Close socked
  if (socketPool[self.announceUrl]) socketPool[self.announceUrl].consumers--

  self.socket.removeListener('connect', self._onSocketConnectBound)
  self.socket.removeListener('data', self._onSocketDataBound)
  self.socket.removeListener('close', self._onSocketCloseBound)
  self.socket.removeListener('error', self._onSocketErrorBound)

  self._onSocketConnectBound = null
  self._onSocketErrorBound = null
  self._onSocketDataBound = null
  self._onSocketCloseBound = null

  if (socketPool[self.announceUrl].consumers === 0) {
    delete socketPool[self.announceUrl]

    self.socket.on('error', noop) // ignore all future errors

    try {
      self.socket.destroy(cb)
    } catch (err) {
      if (cb) cb()
    }
  }

  self.socket = null
}

WebSocketTracker.prototype._openSocket = function () {
  var self = this
  self.destroyed = false

  self._onSocketConnectBound = self._onSocketConnect.bind(self)
  self._onSocketErrorBound = self._onSocketError.bind(self)
  self._onSocketDataBound = self._onSocketData.bind(self)
  self._onSocketCloseBound = self._onSocketClose.bind(self)

  self.socket = socketPool[self.announceUrl]
  if (!self.socket) {
    self.socket = socketPool[self.announceUrl] = new Socket(self.announceUrl)
    self.socket.consumers = 1
    self.socket.on('connect', self._onSocketConnectBound)
  } else {
    socketPool[self.announceUrl].consumers++
  }

  self.socket.on('data', self._onSocketDataBound)
  self.socket.on('close', self._onSocketCloseBound)
  self.socket.on('error', self._onSocketErrorBound)
}

WebSocketTracker.prototype._onSocketConnect = function () {
  var self = this
  if (self.destroyed) return

  if (self.reconnecting) {
    self.reconnecting = false
    self.retries = 0
    self.announce(self.client._defaultAnnounceOpts())
  }
}

WebSocketTracker.prototype._onSocketData = function (data) {
  var self = this
  if (self.destroyed) return

  try {
    data = JSON.parse(data)
  } catch (err) {
    self.client.emit('warning', new Error('Invalid tracker response'))
    return
  }

  if (data.action === 'announce' || data.offer || data.answer) {
    self._onAnnounceResponse(data)
  } else if (data.action === 'scrape') {
    self._onScrapeResponse(data)
  } else {
    self._onSocketError(new Error('invalid action in WS response: ' + data.action))
  }
}

WebSocketTracker.prototype._onAnnounceResponse = function (data) {
  var self = this

  if (data.info_hash !== self.client._infoHashBinary) {
    debug(
      'ignoring websocket data from %s for %s (looking for %s: reused socket)',
      self.announceUrl, common.binaryToHex(data.info_hash), self.client.infoHash
    )
    return
  }

  if (data.peer_id && data.peer_id === self.client._peerIdBinary) {
    // ignore offers/answers from this client
    return
  }

  debug(
    'received %s from %s for %s',
    JSON.stringify(data), self.announceUrl, self.client.infoHash
  )

  var failure = data['failure reason']
  if (failure) return self.client.emit('warning', new Error(failure))

  var warning = data['warning message']
  if (warning) self.client.emit('warning', new Error(warning))

  var interval = data.interval || data['min interval']
  if (interval) self.setInterval(interval * 1000)

  var trackerId = data['tracker id']
  if (trackerId) {
    // If absent, do not discard previous trackerId value
    self._trackerId = trackerId
  }

  if (data.complete != null) {
    self.client.emit('update', {
      announce: self.announceUrl,
      complete: data.complete,
      incomplete: data.incomplete
    })
  }

  var peer
  if (data.offer && data.peer_id) {
    debug('creating peer (from remote offer)')
    peer = new Peer({
      trickle: false,
      config: self.client._rtcConfig,
      wrtc: self.client._wrtc
    })
    peer.id = common.binaryToHex(data.peer_id)
    peer.once('signal', function (answer) {
      var params = {
        info_hash: self.client._infoHashBinary,
        peer_id: self.client._peerIdBinary,
        to_peer_id: data.peer_id,
        answer: answer,
        offer_id: data.offer_id
      }
      if (self._trackerId) params.trackerid = self._trackerId
      self._send(params)
    })
    peer.signal(data.offer)
    self.client.emit('peer', peer)
  }

  if (data.answer && data.peer_id) {
    var offerId = common.binaryToHex(data.offer_id)
    peer = self.peers[offerId]
    if (peer) {
      peer.id = common.binaryToHex(data.peer_id)
      peer.signal(data.answer)
      self.client.emit('peer', peer)

      clearTimeout(peer.trackerTimeout)
      peer.trackerTimeout = null
      delete self.peers[offerId]
    } else {
      debug('got unexpected answer: ' + JSON.stringify(data.answer))
    }
  }
}

WebSocketTracker.prototype._onScrapeResponse = function (data) {
  var self = this
  data = data.files || {}

  var keys = Object.keys(data)
  if (keys.length === 0) {
    self.client.emit('warning', new Error('invalid scrape response'))
    return
  }

  keys.forEach(function (infoHash) {
    var response = data[infoHash]
    // TODO: optionally handle data.flags.min_request_interval
    // (separate from announce interval)
    self.client.emit('scrape', {
      announce: self.announceUrl,
      infoHash: common.binaryToHex(infoHash),
      complete: response.complete,
      incomplete: response.incomplete,
      downloaded: response.downloaded
    })
  })
}

WebSocketTracker.prototype._onSocketClose = function () {
  var self = this
  if (self.destroyed) return
  self.destroy()
  self._startReconnectTimer()
}

WebSocketTracker.prototype._onSocketError = function (err) {
  var self = this
  if (self.destroyed) return
  self.destroy()
  // errors will often happen if a tracker is offline, so don't treat it as fatal
  self.client.emit('warning', err)
  self._startReconnectTimer()
}

WebSocketTracker.prototype._startReconnectTimer = function () {
  var self = this
  var ms = Math.floor(Math.random() * RECONNECT_VARIANCE) + Math.min(Math.pow(2, self.retries) * RECONNECT_MINIMUM, RECONNECT_MAXIMUM)

  self.reconnecting = true
  self.reconnectTimer = setTimeout(function () {
    self.retries++
    self._openSocket()
  }, ms)
  if (self.reconnectTimer.unref) self.reconnectTimer.unref()

  debug('reconnecting socket in %s ms', ms)
}

WebSocketTracker.prototype._send = function (params) {
  var self = this
  if (self.destroyed) return

  var message = JSON.stringify(params)
  debug('send %s', message)
  self.socket.send(message)
}

WebSocketTracker.prototype._generateOffers = function (numwant, cb) {
  var self = this
  var offers = []
  debug('generating %s offers', numwant)

  for (var i = 0; i < numwant; ++i) {
    generateOffer()
  }

  function generateOffer () {
    var offerId = hat(160)
    debug('creating peer (from _generateOffers)')
    var peer = self.peers[offerId] = new Peer({
      initiator: true,
      trickle: false,
      config: self.client._rtcConfig,
      wrtc: self.client._wrtc
    })
    peer.once('signal', function (offer) {
      offers.push({
        offer: offer,
        offer_id: common.hexToBinary(offerId)
      })
      checkDone()
    })
    peer.trackerTimeout = setTimeout(function () {
      debug('tracker timeout: destroying peer')
      peer.trackerTimeout = null
      delete self.peers[offerId]
      peer.destroy()
    }, OFFER_TIMEOUT)
  }

  function checkDone () {
    if (offers.length === numwant) {
      debug('generated %s offers', numwant)
      cb(offers)
    }
  }
}

function noop () {}
<|endoftext|>


<commit_before>/**
* K-means cluster analysis and vizualization
* using d3js
* Isaak Y Tecle <iyt2@cornell.edu>
*
*/

JSAN.use("CXGN.List");
JSAN.use("jquery.blockUI");
JSAN.use('solGS.solGS')

jQuery(document).ready( function() {

    var url = window.location.pathname;

    if (url.match(/cluster\/analysis/) != null) {

        var list = new CXGN.List();

        var listMenu = list.listSelect("kcluster_genotypes", ['accessions', 'trials']);

	if (listMenu.match(/option/) != null) {

            jQuery("#kcluster_genotypes_list").append(listMenu);

        } else {
            jQuery("#kcluster_genotypes_list").append("<select><option>no lists found - Log in</option></select>");
        }
    }

});

jQuery(document).ready( function() {

    var url = window.location.pathname;

    if (url.match(/solgs\/trait|breeders_toolbox\/trial|breeders\/trial\/|solgs\/selection\//)) {
       checkKClusterResult();
    }

});

function checkKClusterResult () {

    var listId = jQuery('#list_id').val();

    var popId = solGS.getPopulationDetails();

    var comboPopsId = jQuery('#combo_pops_id').val();
    //console.log('combo ' + comboPopsId)
    jQuery.ajax({
        type: 'POST',
        dataType: 'json',
	data: {'list_id': listId,
	       'combo_pops_id' : comboPopsId,
	       'training_pop_id' : popId.training_pop_id,
	       'selection_pop_id': popId.selection_pop_id},
        url: '/cluster/check/result/',
        success: function(response) {
            if (response.result) {

		if (response.list_id) {
		    setListId(response.list_id);
		}
		console.log('calling kcluster combo id ' + response.combo_pops_id)
		console.log('calling kcluster result ' + response.result)
		kClusterResult();
	    } else {
		jQuery("#run_kcluster").show();
            }
	},

	});

}

jQuery(document).ready( function() {

    jQuery("#run_kcluster").click(function() {
        kClusterResult();
    });

});

jQuery(document).ready( function() {

    var url = window.location.pathname;

    if (url.match(/cluster\/analysis/) != null) {
        var listId;

        jQuery("<option>", {value: '', selected: true}).prependTo("#kcluster_genotypes_list_select");

        jQuery("#kcluster_genotypes_list_select").change(function() {
            listId = jQuery(this).find("option:selected").val();

            if (listId) {
                jQuery("#kcluster_genotypes_list_upload").click(function() {
                    loadKClusterGenotypesList(listId);
                });
            }
        });

	checkKClusterResult();
    }
});

function loadKClusterGenotypesList(listId) {

    var genoList = getKClusterGenotypesListData(listId);
    var listName = genoList.name;
    var listType = genoList.listType;

    if ( listId.length === 0) {
        alert('The list is empty. Please select a list with content.' );
    } else {
	jQuery.blockUI.defaults.applyPlatformOpacityRules = false;
        jQuery.blockUI({message: 'Please wait..'});

        var kClusterGenotypes = jQuery("#list_kcluster_populations_table").doesExist();

        if (kClusterGenotypes == false) {
            kClusterGenotypes = getKClusterGenotypesListData(listId);
            jQuery("#list_pca_populations").append(kClusterGenotypes).show();
        }
        else {
            var addRow = '
'
                + ''
                + listName + ''
                + '	'
		+ '' + listType + '	'
                + ''
                + ''
                + '[ Run K-means Cluster ]' + ''
                + '
';

            var tdId = '#list_kcluster_page_' + listId;
            var addedRow = jQuery(tdId).doesExist();

            if (addedRow == false) {
                jQuery("#list_kcluster_populations_table tr:last").after(addRow);
            }
        }
	jQuery.unblockUI();
    }

}

function kClusterResult () {

    var popId  = solGS.getPopulationDetails();
    var listId = getListId();

    if (listId) {
	popId['training_pop_id'] = 'list_' + listId;
    }

    var listName;
    var listType;

    if (listId) {
	var genoList = getKClusterGenotypesListData(listId);
	listName = genoList.name;
	listType = genoList.listType;
    }

    if (listId || popId.training_pop_id || popId.selection_pop_id) {
	jQuery("#kcluster_message").html("Running K-means clustering... please wait...");
	jQuery("#run_kcluster").hide();
    }

    jQuery.ajax({
        type: 'POST',
        dataType: 'json',
        data: {'training_pop_id': popId.training_pop_id,
	       'selection_pop_id': popId.selection_pop_id,
	       'combo_pops_id': popId.combo_pops_id,
	       'list_id': listId,
	       'list_name': listName,
	       'list_type': listType,
	      },
        url: '/kcluster/result',
        success: function(response) {
            if (response.status === 'success') {

		if (response.pop_id) {
		    var popId = response.pop_id;
		}

		var plotData = { 'scores': response.pca_scores,
				 'variances': response.pca_variances,
				 'pop_id': popId,
				 'list_id': listId,
				 'list_name': listName,
				 'trials_names': response.trials_names,
				 'output_link' : response.output_link
			       };

                plotKCluster(plotData);
		jQuery("#kcluster_message").empty();
		jQuery("#run_kcluster").hide();

            } else {
		jQuery("#pca_message").html(response.status);
		jQuery("#run_kcluster").show();
            }
	},
        error: function(response) {
            jQuery("#kcluster_message").html('Error occured running K-means clustering.');
	    jQuery("#run_kcluser").show();
        }
    });

}

function getKClusterPopsList (listId) {

    var genoList       = getKClusterGenotypesListData(listId);
    var listName       = genoList.name;
    var listType       = genoList.listType;

    var pcaPopsList ='

'
                                + 'Population	'
                                + 'List type	'
                                + 'Run K-means	'
                                +'
'
                                + '
'
                                + ''
                                + ''
                                + listName + ''
                                + '	'
    	                        + '' + listType + '	'
                                + ''
                                + ''
                                + '[ Run K-means]'+ ''
                                + '

';

    return kClusterPopsList;
}

jQuery.fn.doesExist = function(){

        return jQuery(this).length > 0;

 };

function getKClusterGenotypesListData(listId) {

    var list = new CXGN.List();

    if (! listId == "") {
	var listName = list.listNameById(listId);
        var listType = list.getListType(listId);

	return {'name'     : listName,
		'listType' : listType,
               };
    } else {
	return;
    }

}

function setListId (listId) {

    var existingListId = jQuery("#list_id").doesExist();
    console.log(listId)
    //listId = listId.replace('list_', '');
     console.log(listId)
    if (existingListId) {
	jQuery("#list_id").remove();
    }

    jQuery("#kcluster_canvas").append('<input type="hidden" id="list_id" value=' + listId + '></input>');

}

function getListId () {

    var listId = jQuery("#list_id").val();
    return listId;

}

function plotKCluster(plotData){

    var scores      = plotData.scores;
    var variances   = plotData.variances;
    var trialsNames = plotData.trials_names;

    var pc12 = [];
    var pc1  = [];
    var pc2  = [];
    var trials = [];

    jQuery.each(scores, function(i, pc) {
        pc12.push( [{'name' : pc[0], 'pc1' : parseFloat(pc[2]), 'pc2': parseFloat(pc[3]), 'trial':pc[1] }]);
	pc1.push(parseFloat(pc[2]));
	pc2.push(parseFloat(pc[3]));
	trials.push(pc[1]);
    });

    var height = 300;
    var width  = 500;
    var pad    = {left:40, top:20, right:40, bottom:20};
    var totalH = height + pad.top + pad.bottom + 200;
    var totalW = width + pad.left + pad.right + 400;

    var svg = d3.select("#pca_canvas")
        .append("svg")
        .attr("width", totalW)
        .attr("height", totalH);

    var pcaPlot = svg.append("g")
        .attr("id", "#pca_plot")
        .attr("transform", "translate(" + (pad.left) + "," + (pad.top) + ")");

    var pc1Min = d3.min(pc1);
    var pc1Max = d3.max(pc1);

    var pc1Limits = d3.max([Math.abs(d3.min(pc1)), d3.max(pc1)]);
    var pc2Limits = d3.max([Math.abs(d3.min(pc2)), d3.max(pc2)]);

    var pc1AxisScale = d3.scale.linear()
        .domain([0, pc1Limits])
        .range([0, width/2]);

    var pc1AxisLabel = d3.scale.linear()
        .domain([(-1 * pc1Limits), pc1Limits])
        .range([0, width]);

    var pc2AxisScale = d3.scale.linear()
        .domain([0, pc2Limits])
        .range([0, (height/2)]);

    var pc1Axis = d3.svg.axis()
        .scale(pc1AxisLabel)
        .tickSize(3)
        .orient("bottom");

    var pc2AxisLabel = d3.scale.linear()
        .domain([(-1 * pc2Limits), pc2Limits])
        .range([height, 0]);

   var pc2Axis = d3.svg.axis()
        .scale(pc2AxisLabel)
        .tickSize(3)
        .orient("left");

    var pc1AxisMid = (0.5 * height) + pad.top;
    var pc2AxisMid = (0.5 * width) + pad.left;

    var yMidLineData = [
	{"x": pc2AxisMid, "y": pad.top},
	{"x": pc2AxisMid, "y": pad.top + height}
    ];

    var xMidLineData = [
	{"x": pad.left, "y": pad.top + height/2},
	{"x": pad.left + width, "y": pad.top + height/2}
    ];

    var lineFunction = d3.svg.line()
        .x(function(d) { return d.x; })
        .y(function(d) { return d.y; })
        .interpolate("linear");

    pcaPlot.append("path")
        .attr("d", lineFunction(yMidLineData))
        .attr("stroke", "red")
        .attr("stroke-width", 1)
        .attr("fill", "none");

    pcaPlot.append("path")
        .attr("d", lineFunction(xMidLineData))
        .attr("stroke", "green")
        .attr("stroke-width", 1)
        .attr("fill", "none");

    pcaPlot.append("g")
        .attr("class", "PC1 axis")
        .attr("transform", "translate(" + pad.left + "," + (pad.top + height) +")")
        .call(pc1Axis)
        .selectAll("text")
        .attr("y", 0)
        .attr("x", 10)
        .attr("dy", ".1em")
        .attr("transform", "rotate(90)")
        .attr("fill", "green")
        .style({"text-anchor":"start", "fill": "#86B404"});

    pcaPlot.append("g")
        .attr("class", "PC2 axis")
        .attr("transform", "translate(" + pad.left +  "," + pad.top  + ")")
        .call(pc2Axis)
        .selectAll("text")
        .attr("y", 0)
        .attr("x", -10)
        .attr("fill", "green")
        .style("fill", "#86B404");

    pcaPlot.append("g")
        .attr("id", "pc1_axis_label")
        .append("text")
        .text("PC1: " + variances[0][1] + "%" )
        .attr("y", pad.top + height + 55)
        .attr("x", width/2)
        .attr("font-size", 12)
        .style("fill", "green")

    pcaPlot.append("g")
        .attr("id", "pc2_axis_label")
        .append("text")
        .text("PC2: " + variances[1][1] + "%" )
	.attr("transform", "rotate(-90)")
	.attr("y", -5)
        .attr("x", -((pad.left + height/2) + 10))
        .attr("font-size", 12)
        .style("fill", "red")

    var grpColor = d3.scale.category10();

    pcaPlot.append("g")
        .selectAll("circle")
        .data(pc12)
        .enter()
        .append("circle")
        .style("fill", function(d) {return grpColor(d[0].trial); })
        .attr("r", 3)
        .attr("cx", function(d) {
            var xVal = d[0].pc1;
	    if (xVal >= 0) {
                return  (pad.left + (width/2)) + pc1AxisScale(xVal);
            } else {
                return (pad.left + (width/2)) - (-1 * pc1AxisScale(xVal));
           }
        })
        .attr("cy", function(d) {
            var yVal = d[0].pc2;

            if (yVal >= 0) {
                return ( pad.top + (height/2)) - pc2AxisScale(yVal);
            } else {
                return (pad.top + (height/2)) +  (-1 * pc2AxisScale(yVal));
            }
        })
        .on("mouseover", function(d) {
            d3.select(this)
                .attr("r", 5)
                .style("fill", "#86B404")
            pcaPlot.append("text")
                .attr("id", "dLabel")
                .style("fill", "#86B404")
                .text( d[0].name + "(" + d[0].pc1 + "," + d[0].pc2 + ")")
                .attr("x", pad.left + 1)
                .attr("y", pad.top + 80);
        })
        .on("mouseout", function(d) {
            d3.select(this)
                .attr("r", 3)
                .style("fill", function(d) {return grpColor(d[0].trial); })
            d3.selectAll("text#dLabel").remove();
        });

    pcaPlot.append("rect")
	.attr("transform", "translate(" + pad.left + "," + pad.top + ")")
        .attr("height", height)
        .attr("width", width)
        .attr("fill", "none")
        .attr("stroke", "#523CB5")
        .attr("stroke-width", 1)
        .attr("pointer-events", "none");

    var id;
    if ( plotData.pop_id) {
    	id = plotData.pop_id;
    } else {
	id = plotData.list_id;
    }

    var popName = "";
    if (plotData.list_name) {
	popName = ' -- ' + plotData.list_name;
    }

    var pcaDownload;
    if (plotData.pop_id)  {
	pcaDownload = "/download/pca/scores/population/" + id;
    }

     pcaPlot.append("a")
	.attr("xlink:href", pcaDownload)
	.append("text")
	.text("[ Download PCA scores ]" + popName)
	.attr("y", pad.top + height + 75)
        .attr("x", pad.left)
        .attr("font-size", 14)
        .style("fill", "#954A09")

    var shareLink;
    if (plotData.output_link)  {
	    shareLink = plotData.output_link;
    }

    pcaPlot.append("a")
	.attr("xlink:href", shareLink)
	.append("text")
	.text("[Share plot ]")
	.attr("y", pad.top + height + 100)
        .attr("x", pad.left)
        .attr("font-size", 14)
        .style("fill", "#954A09")

    if (trialsNames && Object.keys(trialsNames).length > 1) {
	var trialsIds = jQuery.unique(trials);
	trialsIds = jQuery.unique(trialsIds);

	var legendValues = [];
	var cnt = 0;

	var allTrialsNames = [];

	for (var tr in trialsNames) {
	    allTrialsNames.push(trialsNames[tr]);
	};

	trialsIds.forEach( function (id) {
	    var trialName = trialsNames[id];
	    if (isNaN(id)) {trialName = allTrialsNames.join(' & ');}
	    legendValues.push([cnt, id, trialName]);
	    cnt++;
	});

	var recLH = 20;
	var recLW = 20;

	var legend = pcaPlot.append("g")
            .attr("class", "cell")
            .attr("transform", "translate(" + (width + 60) + "," + (height * 0.25) + ")")
            .attr("height", 100)
            .attr("width", 100);

	legend = legend.selectAll("rect")
            .data(legendValues)
            .enter()
            .append("rect")
            .attr("x", function (d) { return 1;})
            .attr("y", function (d) {return 1 + (d[0] * recLH) + (d[0] * 5); })
            .attr("width", recLH)
            .attr("height", recLW)
            .style("stroke", "black")
            .attr("fill", function (d) {
		return  grpColor(d[1]);
            });

	var legendTxt = pcaPlot.append("g")
            .attr("transform", "translate(" + (width + 90) + "," + ((height * 0.25) + (0.5 * recLW)) + ")")
            .attr("id", "legendtext");

	legendTxt.selectAll("text")
            .data(legendValues)
            .enter()
            .append("text")
            .attr("fill", "#523CB5")
            .style("fill", "#523CB5")
            .attr("x", 1)
            .attr("y", function (d) { return 1 + (d[0] * recLH) + (d[0] * 5); })
            .text(function (d) {
		return d[2];
            })
            .attr("dominant-baseline", "middle")
	    .attr("text-anchor", "start");
    }

}

<commit_msg>replace file..<commit_after><|endoftext|>


<commit_before>/*!
 * ${copyright}
 */

sap.ui.define(['sap/ui/mdc/odata/TypeUtil', 'sap/ui/mdc/enum/BaseType'], function(ODataTypeUtil, BaseType) {
	"use strict";

	/**
	 * Provides mapping functionality for odata v4 data types to base types. Extend this object in your project to customize behaviour depending on model usage.
	 * Note:
	 * This utility is experimental and the API/behavior is not finalized and hence this should not be used for productive usage.
	 * @namespace
	 * @author SAP SE
	 * @private
	 * @experimental As of version 1.79
	 * @since 1.79.0
	 * @alias sap.ui.mdc.odata.v4.TypeUtil
	 */
	var ODataV4TypeUtil = Object.assign({}, ODataTypeUtil);

	ODataV4TypeUtil.getBaseType = function(sType, oFormatOptions, oConstraints) {

		switch (sType) {
			case "sap.ui.model.odata.type.Date":
				return BaseType.Date;

			case "sap.ui.model.odata.type.TimeOfDay":
				return BaseType.Time;

			case "sap.ui.model.odata.type.Unit":
			case "sap.ui.model.odata.type.Currency":
				if (!oFormatOptions || ((!oFormatOptions.hasOwnProperty("showMeasure") || oFormatOptions.showMeasure) && (!oFormatOptions.hasOwnProperty("showNumber") || oFormatOptions.showNumber))) {
					return BaseType.Unit;
				} else if (!oFormatOptions.hasOwnProperty("showNumber") || oFormatOptions.showNumber) {
					return BaseType.Numeric; // only number to show
				} else {
					return BaseType.String; // only unit to show
				}
			default:
				return ODataTypeUtil.getBaseType(sType, oFormatOptions, oConstraints);
		}
	};

	ODataV4TypeUtil.getDataTypeClassName = function(sType) {

		// V4 specific types
		var mEdmTypes = {
			"Edm.Date": "sap.ui.model.odata.type.Date", // V4 Date
			"Edm.TimeOfDay": "sap.ui.model.odata.type.TimeOfDay" // V4 constraints: {precision}
		};
		if (mEdmTypes[sType]) {
			sType = mEdmTypes[sType];
		} else {
			sType = ODataTypeUtil.getDataTypeClassName(sType);
		}
		return sType;
	};

	return ODataV4TypeUtil;

}, /* bExport= */ true);
<commit_msg>[INTERNAL] TypeUtil.js: setting the V4 property for DateTimeOffset<commit_after>/*!
 * ${copyright}
 */

sap.ui.define(['sap/ui/mdc/odata/TypeUtil', 'sap/ui/mdc/enum/BaseType'], function(ODataTypeUtil, BaseType) {
	"use strict";

	/**
	 * Provides mapping functionality for odata v4 data types to base types. Extend this object in your project to customize behaviour depending on model usage.
	 * Note:
	 * This utility is experimental and the API/behavior is not finalized and hence this should not be used for productive usage.
	 * @namespace
	 * @author SAP SE
	 * @private
	 * @experimental As of version 1.79
	 * @since 1.79.0
	 * @alias sap.ui.mdc.odata.v4.TypeUtil
	 */
	var ODataV4TypeUtil = Object.assign({}, ODataTypeUtil);

	ODataV4TypeUtil.getBaseType = function(sType, oFormatOptions, oConstraints) {

		switch (sType) {
			case "sap.ui.model.odata.type.Date":
				return BaseType.Date;

			case "sap.ui.model.odata.type.TimeOfDay":
				return BaseType.Time;

			case "sap.ui.model.odata.type.Unit":
			case "sap.ui.model.odata.type.Currency":
				if (!oFormatOptions || ((!oFormatOptions.hasOwnProperty("showMeasure") || oFormatOptions.showMeasure) && (!oFormatOptions.hasOwnProperty("showNumber") || oFormatOptions.showNumber))) {
					return BaseType.Unit;
				} else if (!oFormatOptions.hasOwnProperty("showNumber") || oFormatOptions.showNumber) {
					return BaseType.Numeric; // only number to show
				} else {
					return BaseType.String; // only unit to show
				}
			default:
				return ODataTypeUtil.getBaseType(sType, oFormatOptions, oConstraints);
		}
	};

	ODataV4TypeUtil.getDataTypeClassName = function(sType) {

		// V4 specific types
		var mEdmTypes = {
			"Edm.Date": "sap.ui.model.odata.type.Date", // V4 Date
			"Edm.TimeOfDay": "sap.ui.model.odata.type.TimeOfDay" // V4 constraints: {precision}
		};
		if (mEdmTypes[sType]) {
			sType = mEdmTypes[sType];
		} else {
			sType = ODataTypeUtil.getDataTypeClassName(sType);
		}
		return sType;
	};

	ODataV4TypeUtil.getDataTypeInstance = function(sDataType, formatOptions, constraints) {
		switch (sDataType) {
			case "sap.ui.model.odata.type.DateTimeOffset":
			case "Edm.DateTimeOffset":
				if (!constraints) {
					constraints = {};
				}
				constraints.V4 = true;
				break;

			default:
		}

		var TypeClass = this.getDataTypeClass(sDataType);
		return new TypeClass(formatOptions, constraints);
	};

	return ODataV4TypeUtil;

}, /* bExport= */ true);
<|endoftext|>


<commit_before>/**
 * @ngdoc directive
 * @name faHeaderFooterLayout
 * @module famous.angular
 * @restrict EA
 * @description
 * This directive will create a Famo.us HeaderFooterLayout containing
 * a Header, Content, and Footer based on the order of its child elements.
 *  See [https://famo.us/docs/views/HeaderFooterLayout]
 *
 * @usage
 * ```html
 * <fa-header-footer-layout>
 *   <!-- header rendernode -->
 *   <!-- content rendernode -->
 *   <!-- footer rendernode -->
 * </fa-header-footer-layout>
 * ```
 * @example
 * `Fa-header-footer` is a View that arranges three renderables into a header and footer area with defined sizes, and a content area that fills up the remaining space.
 *
 * To use it, declare it in the html and nest 3 renderables inside.  In the example below, there are three direct children elements: a Modifier (with an `fa-surface` nested inside), a Surface, and another Modifier (with an `fa-surface` nested inside).  The order that they are declared in the html determines whether each corresponds to a header, content, and footer.
 *
 * Since the header and footer Modifiers have fixed heights of `[undefined, 75]` (fill the parent container horizontally, 75 pixels vertically), the content will fill the remaining height of the parent modifier or context.
 *
 *```html
 * <fa-header-footer-layout>
 *   <!-- header -->
 *   <fa-modifier fa-size="[undefined, 75]">
 *     <fa-surface fa-background-color="'red'">Header</fa-surface>
 *   </fa-modifier>
 *
 *   <!-- content -->
 *   <fa-surface fa-background-color="'blue'">Content</fa-surface>
 *
 *   <!-- footer -->
 *   <fa-modifier fa-size="[undefined, 75]">
 *     <fa-surface fa-background-color="'green'">Footer</fa-surface>
 *   </fa-modifier>
 * </fa-header-footer-layout>
 *```
 *
 * ## ng-repeat inside a fa-header-footer
 *
 * `Fa-header-footer` works with ng-repeat'ed renderables:
 *
 * ```html
 * <fa-header-footer-layout>
 *   <fa-modifier ng-repeat="view in views" fa-size="view.size" >
 *     <fa-surface fa-background-color="view.bgColor">
 *       {{view.text}}
 *     </fa-surface>
 *   </fa-modifier>
 * </fa-header-footer-layout>
 * ```
 * ```javascript
 * $scope.views = [
 * {bgColor: "red", text: "header", size: [undefined, 100]},
 * {bgColor: "green", text: "content", size: [undefined, undefined]},
 * {bgColor: "blue", text: "footer", size: [undefined, 100]}
 * ];
 * ```
 * In the example above, 3 renderables are generated through an ng-repeat.  The header and footer `Modifier`s generated by the ng-repeat have defined sizes of `[undefined, 100]` (they will fill their parent container horizontally, and be 100 pixels vertically).  The content has a size of `[undefined, undefined]`, and it will fill the remaining heght and width of its container.
 *
 * Note: If more than 3 renderables are nested inside an `fa-header-footer-layout`, it will throw an error: `fa-header-footer-layout can accept no more than 3 children.`
 *
 */

angular.module('famous.angular')
  .directive('faHeaderFooterLayout', ["$famous", "$famousDecorator", function ($famous, $famousDecorator) {
    return {
      template: '

',
      restrict: 'E',
      transclude: true,
      scope: true,
      compile: function (tElem, tAttrs, transclude) {
        var HeaderFooterLayout = $famous["famous/views/HeaderFooterLayout"];
        var RenderNode = $famous["famous/core/RenderNode"];
        return {
          pre: function (scope, element, attrs) {
            var isolate = $famousDecorator.ensureIsolate(scope);

            var _header = new RenderNode();
            var _content = new RenderNode();
            var _footer = new RenderNode();

            var options = scope.$eval(attrs.faOptions) || {};
            isolate.renderNode = new HeaderFooterLayout(options);
            $famousDecorator.addRole('renderable',isolate);
            isolate.show();

            var _numberOfChildren = 0;

            $famousDecorator.sequenceWith(
              scope,
              function(data) {
                _numberOfChildren++;
                if (_numberOfChildren === 1) {
                  isolate.renderNode.header.add(data.renderGate);
                } else if (_numberOfChildren === 2){
                  isolate.renderNode.content.add(data.renderGate);
                } else if (_numberOfChildren === 3){
                  isolate.renderNode.footer.add(data.renderGate);
                } else {
                  throw new Error('fa-header-footer-layout can accept no more than 3 children');
                }
              },
              // TODO: support removing children
              function(childScopeId) {
                throw new Error ('unimplemented: fa-header-footer-layout does not support removing children');
              }
            );

          },
          post: function (scope, element, attrs) {
            var isolate = $famousDecorator.ensureIsolate(scope);

            transclude(scope, function (clone) {
              element.find('div').append(clone);
            });

            $famousDecorator.registerChild(scope, element, isolate);
          }
        };
      }
    };
  }]);
<commit_msg>docs: correct the documentation for sizing elements in a fa-header-footer-layout<commit_after>/**
 * @ngdoc directive
 * @name faHeaderFooterLayout
 * @module famous.angular
 * @restrict EA
 * @description
 * This directive will create a Famo.us HeaderFooterLayout containing
 * a Header, Content, and Footer based on the order of its child elements.
 *  See [https://famo.us/docs/views/HeaderFooterLayout]
 *
 * @usage
 * ```html
 * <fa-header-footer-layout>
 *   <!-- header rendernode -->
 *   <!-- content rendernode -->
 *   <!-- footer rendernode -->
 * </fa-header-footer-layout>
 * ```
 * @example
 * `fa-header-footer` is a View that arranges three renderables into a header and footer area with defined sizes, and a content area that fills up the remaining space.
 *
 * To use it, declare it in the html and nest 3 renderables inside.  In the example below, there are three direct children elements: a Modifier (with an `fa-surface` nested inside), a Surface, and another Modifier (with an `fa-surface` nested inside).  The order that they are declared in the html determines whether each corresponds to a header, content, and footer.
 *
 * Since the header and footer Modifiers have fixed heights of `[undefined, 75]` (fill the parent container horizontally, 75 pixels vertically), the content will fill the remaining height of the parent modifier or context.
 *
 *```html
 * <fa-header-footer-layout fa-options="{headerSize: 75, footerSize: 75}">
 *   <!-- header -->
 *     <fa-surface fa-background-color="'red'">Header</fa-surface>
 *
 *   <!-- content -->
 *   <fa-surface fa-background-color="'blue'">Content</fa-surface>
 *
 *   <!-- footer -->
 *     <fa-surface fa-background-color="'green'">Footer</fa-surface>
 * </fa-header-footer-layout>
 *```
 *
 * Famo.us' `HeaderFooterLayout` defaults to a vertical orientation.
 * Specify a direction in the options to obtains a horizontal orientation.
 *
 * ```html
 * <fa-header-footer-layout fa-options="{direction: 0, headerSize: 75, footerSize: 75}">
 *   <!-- header -->
 *     <fa-surface fa-background-color="'red'">Header</fa-surface>
 *
 *   <!-- content -->
 *   <fa-surface fa-background-color="'blue'">Content</fa-surface>
 *
 *   <!-- footer -->
 *     <fa-surface fa-background-color="'green'">Footer</fa-surface>
 * </fa-header-footer-layout>
 * ```
 *
 * ## ng-repeat inside a fa-header-footer
 *
 * `Fa-header-footer` works with ng-repeat'ed renderables:
 *
 * ```html
 * <fa-header-footer-layout>
 *   <fa-modifier ng-repeat="view in views" fa-size="view.size" >
 *     <fa-surface fa-background-color="view.bgColor">
 *       {{view.text}}
 *     </fa-surface>
 *   </fa-modifier>
 * </fa-header-footer-layout>
 * ```
 * ```javascript
 * $scope.views = [
 * {bgColor: "red", text: "header", size: [undefined, 100]},
 * {bgColor: "green", text: "content", size: [undefined, undefined]},
 * {bgColor: "blue", text: "footer", size: [undefined, 100]}
 * ];
 * ```
 * In the example above, 3 renderables are generated through an ng-repeat.  The header and footer `Modifier`s generated by the ng-repeat have defined sizes of `[undefined, 100]` (they will fill their parent container horizontally, and be 100 pixels vertically).  The content has a size of `[undefined, undefined]`, and it will fill the remaining heght and width of its container.
 *
 * Note:
 *
 * - If more than 3 renderables are nested inside an `fa-header-footer-layout`, it will throw an error: `fa-header-footer-layout can accept no more than 3 children.`
 * - Furthermore, in the basic example we used the `fa-options` attribute to specify the size of the header and footer. Here we do not use modifiers on the surfaces within the header fotter layout to achieve a similar effect. Note that this approach does not work as well with vertical orientations.
 *
 *
 */

angular.module('famous.angular')
  .directive('faHeaderFooterLayout', ["$famous", "$famousDecorator", function ($famous, $famousDecorator) {
    return {
      template: '

',
      restrict: 'E',
      transclude: true,
      scope: true,
      compile: function (tElem, tAttrs, transclude) {
        var HeaderFooterLayout = $famous["famous/views/HeaderFooterLayout"];
        var RenderNode = $famous["famous/core/RenderNode"];
        return {
          pre: function (scope, element, attrs) {
            var isolate = $famousDecorator.ensureIsolate(scope);

            var _header = new RenderNode();
            var _content = new RenderNode();
            var _footer = new RenderNode();

            var options = scope.$eval(attrs.faOptions) || {};
            isolate.renderNode = new HeaderFooterLayout(options);
            $famousDecorator.addRole('renderable',isolate);
            isolate.show();

            var _numberOfChildren = 0;

            $famousDecorator.sequenceWith(
              scope,
              function(data) {
                _numberOfChildren++;
                if (_numberOfChildren === 1) {
                  isolate.renderNode.header.add(data.renderGate);
                } else if (_numberOfChildren === 2){
                  isolate.renderNode.content.add(data.renderGate);
                } else if (_numberOfChildren === 3){
                  isolate.renderNode.footer.add(data.renderGate);
                } else {
                  throw new Error('fa-header-footer-layout can accept no more than 3 children');
                }
              },
              // TODO: support removing children
              function(childScopeId) {
                throw new Error ('unimplemented: fa-header-footer-layout does not support removing children');
              }
            );

          },
          post: function (scope, element, attrs) {
            var isolate = $famousDecorator.ensureIsolate(scope);

            transclude(scope, function (clone) {
              element.find('div').append(clone);
            });

            $famousDecorator.registerChild(scope, element, isolate);
          }
        };
      }
    };
  }]);
<|endoftext|>


<commit_before>var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:8080/my_database');

var Schema = mongoose.Schema;

// create a schema
var gameInfoSchema = new Schema({
  title: { type: String, required: true },
  descrption: { type: String },
  pictures: [ { type: String } ],
  videos: [ { type: String } ],
  link_facebook: { type: String },
  link_twitter: { type: String },
  link_google_plus: { type: String },
  download: { type: String },
  version: { type: String },
  username: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  admin: { type: Boolean },
  location: { type: String },
  created_at: { type: Date },
  updated_at: { type: Date }
});

// the schema is useless so far
// we need to create a model using it
var gameInfo = mongoose.model('gameInfo', gameInfoSchema);

// make this available to our users in our Node applications
module.exports = gameInfo;<commit_msg>Correct capitalization of GameInfo in models/game-info.js.<commit_after>var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:8080/my_database');

var Schema = mongoose.Schema;

// create a schema
var gameInfoSchema = new Schema({
  title: { type: String, required: true },
  descrption: { type: String },
  pictures: [ { type: String } ],
  videos: [ { type: String } ],
  link_facebook: { type: String },
  link_twitter: { type: String },
  link_google_plus: { type: String },
  download: { type: String },
  version: { type: String },
  username: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  admin: { type: Boolean },
  location: { type: String },
  created_at: { type: Date },
  updated_at: { type: Date }
});

// the schema is useless so far
// we need to create a model using it
var GameInfo = mongoose.model('GameInfo', gameInfoSchema);

// make this available to our users in our Node applications
module.exports = GameInfo;<|endoftext|>


<commit_before>var Player = (function () {

	var Player = function(game, x, y, asset, frame) {
		x = x || game.world.centerX;
		y = y || game.world.centerY;
	  Phaser.Sprite.call(this, game, x, y, asset, frame);
	}

	Player.prototype = Object.create(Phaser.Sprite.prototype);
	Player.prototype.constructor = Player;

	return Player;

}).call(this);
<commit_msg>setupConnection method on palyer<commit_after>var Player = (function () {

	var Player = function(game, x, y, asset, frame) {
		x = x || game.world.centerX;
		y = y || game.world.centerY;
	  Phaser.Sprite.call(this, game, x, y, asset, frame);

	  this.playerReady = false;
	}

	Player.prototype = Object.create(Phaser.Sprite.prototype);
	Player.prototype.constructor = Player;

	Player.prototype.setupConnection = function (connection) {
		var _this = this;
		this.peerConn = connection;
		this.peerConn.on('data', function (data) {
			if (data.type === 'click') {
				if (_this.playerReady) {
					// trigger player power
				} else {
					_this.playerReady = true;
				}
			} else if (data.type === 'devicemotion') {
				// device motion stuff here
			}
		});
	});

	return Player;

}).call(this);
<|endoftext|>


<commit_before>export default ($preview, block) => {

	const preview = $preview[0]
	const target = preview.querySelector('.js-inner-blocks')

	if( target ) {

		if( block.innerBlocks ) {

			target.appendChild(block.innerBlocks)

		} else {

			const innerBlocks = preview.closest('.wp-block').querySelector('.editor-inner-blocks')

			block.innerBlocks = innerBlocks

			target.appendChild(innerBlocks)

		}

	}

}<commit_msg>inner block fix<commit_after>export default ($preview, block) => {

	const preview = $preview[0]
	const target = preview.querySelector('.js-inner-blocks')

	if( target ) {

		if( $preview.innerBlocks ) {

			target.appendChild($preview.innerBlocks)

		} else {

			const innerBlocks = preview.closest('.wp-block').querySelector('.editor-inner-blocks')

			$preview.innerBlocks = innerBlocks

			target.appendChild(innerBlocks)

		}

	}

}<|endoftext|>


<commit_before>const SIZE = 800
let canvas
let wheel

$(function() {
    canvas = $('#wheel')
    canvas[0].width = SIZE
    canvas[0].height = SIZE
    wheel = canvas[0].getContext('2d')

    wheel.beginPath()
    wheel.arc(SIZE / 2, SIZE / 2, SIZE / 2 - 50, 0, 2 * Math.PI, false)
    wheel.fill()

    wheel.beginPath()
    wheel.moveTo(SIZE - 100, SIZE / 2)
    wheel.lineTo(SIZE - 10, SIZE / 2 - 10)
    wheel.lineTo(SIZE, SIZE / 2)
    wheel.lineTo(SIZE - 10, SIZE / 2 + 10)
    wheel.fillStyle = '#f00'
    wheel.fill()

    $.post('php/save.php', {
        file: {
            group: 'test-1',
            people: [{
                name: 'user 1',
                points: 3
            }, {
                name: 'user 2',
                points: 1
            }]
        }
    }, function(data) {
        alert(data)
    })
})
<commit_msg>Separate dummy save from main<commit_after>const SIZE = 800
let canvas
let wheel

$(function() {
    canvas = $('#wheel')
    canvas[0].width = SIZE
    canvas[0].height = SIZE
    wheel = canvas[0].getContext('2d')

    wheel.beginPath()
    wheel.arc(SIZE / 2, SIZE / 2, SIZE / 2 - 50, 0, 2 * Math.PI, false)
    wheel.fill()

    wheel.beginPath()
    wheel.moveTo(SIZE - 100, SIZE / 2)
    wheel.lineTo(SIZE - 10, SIZE / 2 - 10)
    wheel.lineTo(SIZE, SIZE / 2)
    wheel.lineTo(SIZE - 10, SIZE / 2 + 10)
    wheel.fillStyle = '#f00'
    wheel.fill()
})

const save = function() {
    $.post('php/save.php', {
        file: {
            group: 'test-1',
            people: [{
                name: 'user 1',
                points: 3
            }, {
                name: 'user 2',
                points: 1
            }]
        }
    }, function(data) {
        alert(data)
    })
}
<|endoftext|>


<commit_before>(function () {
    'use strict';

    var crypto = require('crypto');
    var bodyParser = require('body-parser');
    var r = require('rethinkdb');
    var config = require('../config');
    var router = require('express').Router();

    var lastUpdate = 0;

    router.get('/photo', function (req, res) {
        if (req.query['hub.verify_token'] == config.instagram.verify) {
            res.send(req.query['hub.challenge']);
        } else {
            config.logger.error('token verification incorrect');
            res.status(500).json({err: 'Verify token incorrect'});
        }
    });

    router.use(bodyParser.json({
        verify: function (req, res, buf) {
            var hmac = crypto.createHmac('sha1', config.instagram.secret);
            var hash = hmac.update(buf).digest('hex');

            if (req.header('X-Hub-Signature') == hash) {
                req.validOrigin = true;
            }
        }
    }));

    router.post('/photo', function (req, res) {
        if (!req.validOrigin) {
            config.logger.error('Invalid signature in POST /api/instagram/photo');
            return res.status(500).json({err: 'Invalid signature'});
        }

        var update = req.body[0];
        res.json({success: true, kind: update.object});

        if (update.time - lastUpdate < 1) return;
        lastUpdate = update.time;

        var path = 'https://api.instagram.com/v1/tags/' + update.object_id + '/media/recent?client_id=' + config.instagram.client;

        var conn;
        r.connect(config.database)
            .then(function (c) {
                conn = c;
                return r.table('pictures')
                    .insert(r.http(path)('data').merge(function (item) {
                        return {
                            created_time: r.epochTime(r.row('created_time').coerceTo('number')),
                            time: r.now(),
                            place: r.point(
                                item('location')('longitude'),
                                item('location')('latitude')).default(null)
                        }
                    })).run(conn)
            })
            .error(function (err) {
                config.logger.error(err);
            })
            .finally(function () {
                if (conn)
                    conn.close();
            });
    });

    module.exports = router;
})();
<commit_msg>minor bug fix<commit_after>(function () {
    'use strict';

    var crypto = require('crypto');
    var bodyParser = require('body-parser');
    var r = require('rethinkdb');
    var config = require('../config');
    var router = require('express').Router();

    var lastUpdate = 0;

    router.get('/photo', function (req, res) {
        if (req.query['hub.verify_token'] == config.instagram.verify) {
            res.send(req.query['hub.challenge']);
        } else {
            config.logger.error('token verification incorrect');
            res.status(500).json({err: 'Verify token incorrect'});
        }
    });

    router.use(bodyParser.json({
        verify: function (req, res, buf) {
            var hmac = crypto.createHmac('sha1', config.instagram.secret);
            var hash = hmac.update(buf).digest('hex');

            if (req.header('X-Hub-Signature') == hash) {
                req.validOrigin = true;
            }
        }
    }));

    router.post('/photo', function (req, res) {
        if (!req.validOrigin) {
            config.logger.error('Invalid signature in POST /api/instagram/photo');
            return res.status(500).json({err: 'Invalid signature'});
        }

        var update = req.body[0];
        res.json({success: true, kind: update.object});

        if (update.time - lastUpdate < 1) return;
        lastUpdate = update.time;

        var path = 'https://api.instagram.com/v1/tags/' + update.object_id + '/media/recent?client_id=' + config.instagram.client;

        var conn;
        r.connect(config.database)
            .then(function (c) {
                conn = c;
                return r.table('pictures')
                    .insert(r.http(path)('data').merge(function (item) {
                        return {
                            created_time: r.epochTime(item('created_time').coerceTo('number')),
                            time: r.now(),
                            place: r.point(
                                item('location')('longitude'),
                                item('location')('latitude')).default(null)
                        }
                    })).run(conn)
            })
            .error(function (err) {
                config.logger.error(err);
            })
            .finally(function () {
                if (conn)
                    conn.close();
            });
    });

    module.exports = router;
})();
<|endoftext|>


<commit_before>// Copyright (c) 2017, Rubén Rincón
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
'use strict';
var $ = require('jquery');
var monaco = require('monaco-editor');
var cpp = require('monaco-editor/esm/vs/basic-languages/cpp/cpp');

// We need to create a new definition for cpp so we can remove invalid keywords

function definition() {
    var cppp = $.extend(true, {}, cpp.language); // deep copy

    function removeKeyword(keyword) {
        var index = cppp.keywords.indexOf(keyword);
        if (index > -1) {
            cppp.keywords.splice(index, 1);
        }
    }

    function removeKeywords(keywords) {
        for (var i = 0; i < keywords.length; ++i) {
            removeKeyword(keywords[i]);
        }
    }

    function addKeywords(keywords) {
        // (Ruben) Done one by one as if you just push them all, Monaco complains that they're not strings, but as
        // far as I can tell, they indeed are all strings. This somehow fixes it. If you know how to fix it, plz go
        for (var i = 0; i < keywords.length; ++i) {
            cppp.keywords.push(keywords[i]);
        }
    }

    // We remove everything that's not an identifier, underscore reserved name and not an official C++ keyword...
    // Regarding #617, final is a identifier with special meaning, not a fully qualified keyword
    removeKeywords(["abstract", "amp", "array", "cpu", "delegate", "each", "event", "finally", "gcnew",
        "generic", "in", "initonly", "interface", "interior_ptr", "internal", "literal", "partial", "pascal",
        "pin_ptr", "property", "ref", "restrict", "safe_cast", "sealed", "title_static", "where"]);

    addKeywords(["alignas", "alignof", "and", "and_eq", "asm", "bitand", "bitor", "char8_t", "char16_t",
        "char32_t", "compl", "concept", "consteval", "co_await", "co_return", "co_yield", "not", "not_eq", "or",
        "or_eq", "requires", "xor", "xor_eq"]);

    // #880, patch annotations to handle [ [ something ] ]
    function patchAnnotation(root) {
        for (var i = 0; i < root.length; ++i) {
            if (root[i][1] === 'annotation') {
                root[i][0] = /\[\s*\[[^\]]*]\s*]/;
            }
        }
    }

    patchAnnotation(cppp.tokenizer.root);

    return cppp;
}

var def = definition();

monaco.languages.register({id: 'cppp'});
monaco.languages.setLanguageConfiguration('cppp', cpp.conf);
monaco.languages.setMonarchTokensProvider('cppp', def);

module.exports = def;
<commit_msg>Add missing constinit keyword<commit_after>// Copyright (c) 2017, Rubén Rincón
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
'use strict';
var $ = require('jquery');
var monaco = require('monaco-editor');
var cpp = require('monaco-editor/esm/vs/basic-languages/cpp/cpp');

// We need to create a new definition for cpp so we can remove invalid keywords

function definition() {
    var cppp = $.extend(true, {}, cpp.language); // deep copy

    function removeKeyword(keyword) {
        var index = cppp.keywords.indexOf(keyword);
        if (index > -1) {
            cppp.keywords.splice(index, 1);
        }
    }

    function removeKeywords(keywords) {
        for (var i = 0; i < keywords.length; ++i) {
            removeKeyword(keywords[i]);
        }
    }

    function addKeywords(keywords) {
        // (Ruben) Done one by one as if you just push them all, Monaco complains that they're not strings, but as
        // far as I can tell, they indeed are all strings. This somehow fixes it. If you know how to fix it, plz go
        for (var i = 0; i < keywords.length; ++i) {
            cppp.keywords.push(keywords[i]);
        }
    }

    // We remove everything that's not an identifier, underscore reserved name and not an official C++ keyword...
    // Regarding #617, final is a identifier with special meaning, not a fully qualified keyword
    removeKeywords(["abstract", "amp", "array", "cpu", "delegate", "each", "event", "finally", "gcnew",
        "generic", "in", "initonly", "interface", "interior_ptr", "internal", "literal", "partial", "pascal",
        "pin_ptr", "property", "ref", "restrict", "safe_cast", "sealed", "title_static", "where"]);

    addKeywords(["alignas", "alignof", "and", "and_eq", "asm", "bitand", "bitor", "char8_t", "char16_t",
        "char32_t", "compl", "concept", "consteval", "constinit", "co_await", "co_return", "co_yield", "not", "not_eq", "or",
        "or_eq", "requires", "xor", "xor_eq"]);

    // #880, patch annotations to handle [ [ something ] ]
    function patchAnnotation(root) {
        for (var i = 0; i < root.length; ++i) {
            if (root[i][1] === 'annotation') {
                root[i][0] = /\[\s*\[[^\]]*]\s*]/;
            }
        }
    }

    patchAnnotation(cppp.tokenizer.root);

    return cppp;
}

var def = definition();

monaco.languages.register({id: 'cppp'});
monaco.languages.setLanguageConfiguration('cppp', cpp.conf);
monaco.languages.setMonarchTokensProvider('cppp', def);

module.exports = def;
<|endoftext|>