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

    function WavesAssetListController($scope, $timeout, $interval, events,
                                      applicationContext, apiService, formattingService) {
        var ctrl = this;
        var refreshPromise;
        var refreshDelay = 10 * 1000; // refreshing every 10 seconds

        ctrl.wavesBalance = new Money(0, Currency.WAVES);
        ctrl.assets = [];
        ctrl.noData = true;
        ctrl.assetTransfer = assetTransfer;
        ctrl.assetDetails = assetDetails;
        ctrl.assetReissue = assetReissue;
        ctrl.assetMassPay = assetMassPay;
        loadDataFromBackend();

        $scope.$on('$destroy', function () {
            if (angular.isDefined(refreshPromise)) {
                $interval.cancel(refreshPromise);
                refreshPromise = undefined;
            }
        });

        function loadDataFromBackend() {
            refreshAssets();
            refreshBalance();

            refreshPromise = $interval(function() {
                refreshAssets();
                refreshBalance();
            }, refreshDelay);
        }

        function assetTransfer(assetId) {
            $scope.$broadcast(events.ASSET_TRANSFER, {
                assetId: assetId,
                wavesBalance: ctrl.wavesBalance
            });
        }

        function assetDetails(assetId) {
            $scope.$broadcast(events.ASSET_DETAILS, assetId);
        }

        function assetReissue(assetId) {
            $scope.$broadcast(events.ASSET_REISSUE, {
                assetId: assetId,
                wavesBalance: ctrl.wavesBalance
            });
        }

        function assetMassPay(assetId) {
            $scope.$broadcast(events.ASSET_MASSPAY, {
                assetId: assetId,
                wavesBalance: ctrl.wavesBalance
            });
        }

        function loadAssetDataFromCache(asset) {
            var cached = applicationContext.cache.assets[asset.id];
            asset.balance = cached.balance;
            asset.name = cached.currency.displayName;
            asset.total = cached.totalTokens.formatAmount();
            asset.timestamp = formattingService.formatTimestamp(cached.timestamp);
            asset.reissuable = cached.reissuable;
            asset.sender = cached.sender;
        }

        function refreshBalance() {
            apiService.address.balance(applicationContext.account.address)
                .then(function (response) {
                    ctrl.wavesBalance = Money.fromCoins(response.balance, Currency.WAVES);
                });
        }

        function refreshAssets() {
            var assets = [];
            apiService.assets.balance(applicationContext.account.address).then(function (response) {
                _.forEach(response.balances, function (assetBalance) {
                    var id = assetBalance.assetId;
                    var asset = {
                        id: id,
                        name: ''
                    };

                    // adding asset details to cache
                    applicationContext.cache.putAsset(assetBalance.issueTransaction);
                    applicationContext.cache.updateAsset(id, assetBalance.balance,
                        assetBalance.reissuable, assetBalance.quantity);

                    // adding an asset with positive balance only or your reissuable assets
                    var yourReissuableAsset = assetBalance.reissuable &&
                        assetBalance.issueTransaction.sender === applicationContext.account.address;
                    if (assetBalance.balance !== 0 || yourReissuableAsset) {
                        loadAssetDataFromCache(asset);
                        assets.push(asset);
                    }
                });

                var delay = 1;
                // handling the situation when some assets appeared on the account
                if (ctrl.assets.length === 0 && assets.length > 0) {
                    ctrl.noData = false;
                    delay = 500; // waiting for 0.5 sec on first data loading attempt
                }

                // handling the situation when all assets were transferred from the account
                if (ctrl.assets.length > 0 && assets.length === 0) {
                    ctrl.noData = true;
                    delay = 500;
                }

                // to prevent no data message and asset list from displaying simultaneously
                // we need to update
                $timeout(function() {
                    ctrl.assets = assets.sort(function (a, b) {
                        var aVerified = (a.balance.currency.verified === true) ? 1 : 0,
                            bVerified = (b.balance.currency.verified === true) ? 1 : 0;

                        // The verified assets go first, then we sort them by timestamp
                        aVerified += parseFloat('0.' + new Date(a.timestamp).getTime());
                        bVerified += parseFloat('0.' + new Date(b.timestamp).getTime());

                        return bVerified - aVerified;
                    });
                }, delay);
            });
        }
    }

    WavesAssetListController.$inject = ['$scope', '$timeout', '$interval', 'portfolio.events',
        'applicationContext', 'apiService', 'formattingService'];

    angular
        .module('app.portfolio')
        .controller('assetListController', WavesAssetListController);
})();
<commit_msg>Sorted with strings instead of floats<commit_after>(function () {
    'use strict';

    function WavesAssetListController($scope, $timeout, $interval, events,
                                      applicationContext, apiService, formattingService) {
        var ctrl = this;
        var refreshPromise;
        var refreshDelay = 10 * 1000; // refreshing every 10 seconds

        ctrl.wavesBalance = new Money(0, Currency.WAVES);
        ctrl.assets = [];
        ctrl.noData = true;
        ctrl.assetTransfer = assetTransfer;
        ctrl.assetDetails = assetDetails;
        ctrl.assetReissue = assetReissue;
        ctrl.assetMassPay = assetMassPay;
        loadDataFromBackend();

        $scope.$on('$destroy', function () {
            if (angular.isDefined(refreshPromise)) {
                $interval.cancel(refreshPromise);
                refreshPromise = undefined;
            }
        });

        function loadDataFromBackend() {
            refreshAssets();
            refreshBalance();

            refreshPromise = $interval(function() {
                refreshAssets();
                refreshBalance();
            }, refreshDelay);
        }

        function assetTransfer(assetId) {
            $scope.$broadcast(events.ASSET_TRANSFER, {
                assetId: assetId,
                wavesBalance: ctrl.wavesBalance
            });
        }

        function assetDetails(assetId) {
            $scope.$broadcast(events.ASSET_DETAILS, assetId);
        }

        function assetReissue(assetId) {
            $scope.$broadcast(events.ASSET_REISSUE, {
                assetId: assetId,
                wavesBalance: ctrl.wavesBalance
            });
        }

        function assetMassPay(assetId) {
            $scope.$broadcast(events.ASSET_MASSPAY, {
                assetId: assetId,
                wavesBalance: ctrl.wavesBalance
            });
        }

        function loadAssetDataFromCache(asset) {
            var cached = applicationContext.cache.assets[asset.id];
            asset.balance = cached.balance;
            asset.name = cached.currency.displayName;
            asset.total = cached.totalTokens.formatAmount();
            asset.timestamp = formattingService.formatTimestamp(cached.timestamp);
            asset.reissuable = cached.reissuable;
            asset.sender = cached.sender;
        }

        function refreshBalance() {
            apiService.address.balance(applicationContext.account.address)
                .then(function (response) {
                    ctrl.wavesBalance = Money.fromCoins(response.balance, Currency.WAVES);
                });
        }

        function refreshAssets() {
            var assets = [];
            apiService.assets.balance(applicationContext.account.address).then(function (response) {
                _.forEach(response.balances, function (assetBalance) {
                    var id = assetBalance.assetId;
                    var asset = {
                        id: id,
                        name: ''
                    };

                    // adding asset details to cache
                    applicationContext.cache.putAsset(assetBalance.issueTransaction);
                    applicationContext.cache.updateAsset(id, assetBalance.balance,
                        assetBalance.reissuable, assetBalance.quantity);

                    // adding an asset with positive balance only or your reissuable assets
                    var yourReissuableAsset = assetBalance.reissuable &&
                        assetBalance.issueTransaction.sender === applicationContext.account.address;
                    if (assetBalance.balance !== 0 || yourReissuableAsset) {
                        loadAssetDataFromCache(asset);
                        assets.push(asset);
                    }
                });

                var delay = 1;
                // handling the situation when some assets appeared on the account
                if (ctrl.assets.length === 0 && assets.length > 0) {
                    ctrl.noData = false;
                    delay = 500; // waiting for 0.5 sec on first data loading attempt
                }

                // handling the situation when all assets were transferred from the account
                if (ctrl.assets.length > 0 && assets.length === 0) {
                    ctrl.noData = true;
                    delay = 500;
                }

                // to prevent no data message and asset list from displaying simultaneously
                // we need to update
                $timeout(function() {
                    ctrl.assets = assets.sort(function (a, b) {
                        var aVerified = (a.balance.currency.verified === true) ? '1' : '0',
                            bVerified = (b.balance.currency.verified === true) ? '1' : '0';

                        // The verified assets go first, then we sort them by timestamp
                        aVerified += new Date(a.timestamp).getTime();
                        bVerified += new Date(b.timestamp).getTime();

                        return bVerified - aVerified;
                    });
                }, delay);
            });
        }
    }

    WavesAssetListController.$inject = ['$scope', '$timeout', '$interval', 'portfolio.events',
        'applicationContext', 'apiService', 'formattingService'];

    angular
        .module('app.portfolio')
        .controller('assetListController', WavesAssetListController);
})();
<|endoftext|>


<commit_before>// The AR Drone 2.0 allows a tcp client to receive H264 (MPEG4.10 AVC) video
// from the drone. However, the frames are wrapped by Parrot Video
// Encapsulation (PaVE), which this class parses.

var util   = require('util');
var Stream = require('stream').Stream;
var buffy  = require('buffy');

module.exports = PaVEParser;
util.inherits(PaVEParser, Stream);
function PaVEParser() {
  Stream.call(this);

  this.writable = true;
  this.readable = true;

  this._parser = buffy.createReader();
  this._state  = 'header';
  this._frame  = undefined;
}

PaVEParser.HEADER_SIZE = 64;

PaVEParser.prototype.write = function(buffer) {
  var parser = this._parser;

  parser.write(buffer);

  while (true) {
    switch (this._state) {
      case 'header':
        if (parser.bytesAhead() < PaVEParser.HEADER_SIZE) {
          return;
        }

        this._frame = {
          signature               : parser.ascii(4),
          version                 : parser.uint8(),
          video_codec             : parser.uint8(),
          header_size             : parser.uint16LE(),
          payload_size            : parser.uint32LE(),
          encoded_stream_width    : parser.uint16LE(),
          encoded_stream_height   : parser.uint16LE(),
          display_width           : parser.uint16LE(),
          display_height          : parser.uint16LE(),
          frame_number            : parser.uint32LE(),
          timestamp               : parser.uint32LE(),
          total_chunks            : parser.uint8(),
          chunk_index             : parser.uint8(),
          frame_type              : parser.uint8(),
          control                 : parser.uint8(),
          stream_byte_position_lw : parser.uint32LE(),
          stream_byte_position_uw : parser.uint32LE(),
          stream_id               : parser.uint16LE(),
          total_slices            : parser.uint8(),
          slice_index             : parser.uint8(),
          header1_size            : parser.uint8(),
          header2_size            : parser.uint8(),
          reserved2               : parser.buffer(2),
          advertised_size         : parser.uint32LE(),
          reserved3               : parser.buffer(12),
          payload                 : null,
        };

        if (this._frame.signature !== 'PaVE') {
          this.emit('error', new Error('Invalid signature: ' + JSON.stringify(this._frame.signature)));
        }

        this._state = 'payload';
        break;
      case 'payload':
        if (parser.bytesAhead() < this._frame.payload_size) {
          return;
        }

        this._frame.payload = parser.buffer(this._frame.payload_size);

        this.emit('data', this._frame);
        this._frame = undefined;
        this._state = 'header';
        break;
    }
  }

  return true;
};

PaVEParser.prototype.end = function() {
  // nothing to do, just here so pipe() does not complain
};
<commit_msg>add workaround for 68 byte PaVE headers<commit_after>// The AR Drone 2.0 allows a tcp client to receive H264 (MPEG4.10 AVC) video
// from the drone. However, the frames are wrapped by Parrot Video
// Encapsulation (PaVE), which this class parses.

var util   = require('util');
var Stream = require('stream').Stream;
var buffy  = require('buffy');

module.exports = PaVEParser;
util.inherits(PaVEParser, Stream);
function PaVEParser() {
  Stream.call(this);

  this.writable = true;
  this.readable = true;

  this._parser = buffy.createReader();
  this._state  = 'header';
  this._frame  = undefined;
}

PaVEParser.HEADER_SIZE = 64;

PaVEParser.prototype.write = function(buffer) {
  var parser = this._parser;

  parser.write(buffer);

  while (true) {
    switch (this._state) {
      case 'header':
        if (parser.bytesAhead() < PaVEParser.HEADER_SIZE) {
          return;
        }

        this._frame = {
          signature               : parser.ascii(4),
          version                 : parser.uint8(),
          video_codec             : parser.uint8(),
          header_size             : parser.uint16LE(),
          payload_size            : parser.uint32LE(),
          encoded_stream_width    : parser.uint16LE(),
          encoded_stream_height   : parser.uint16LE(),
          display_width           : parser.uint16LE(),
          display_height          : parser.uint16LE(),
          frame_number            : parser.uint32LE(),
          timestamp               : parser.uint32LE(),
          total_chunks            : parser.uint8(),
          chunk_index             : parser.uint8(),
          frame_type              : parser.uint8(),
          control                 : parser.uint8(),
          stream_byte_position_lw : parser.uint32LE(),
          stream_byte_position_uw : parser.uint32LE(),
          stream_id               : parser.uint16LE(),
          total_slices            : parser.uint8(),
          slice_index             : parser.uint8(),
          header1_size            : parser.uint8(),
          header2_size            : parser.uint8(),
          reserved2               : parser.buffer(2),
          advertised_size         : parser.uint32LE(),
          reserved3               : parser.buffer(12),
          payload                 : null,
        };

        // stupid kludge for https://projects.ardrone.org/issues/show/159
        if (this._frame.header_size > PaVEParser.HEADER_SIZE) {
            buffer(this._frame.header_size - PaVEParser.HEADER_SIZE);
        }

        if (this._frame.signature !== 'PaVE') {
          this.emit('error', new Error('Invalid signature: ' + JSON.stringify(this._frame.signature)));
        }

        this._state = 'payload';
        break;
      case 'payload':
        if (parser.bytesAhead() < this._frame.payload_size) {
          return;
        }

        this._frame.payload = parser.buffer(this._frame.payload_size);

        this.emit('data', this._frame);
        this._frame = undefined;
        this._state = 'header';
        break;
    }
  }

  return true;
};

PaVEParser.prototype.end = function() {
  // nothing to do, just here so pipe() does not complain
};
<|endoftext|>


<commit_before><commit_msg>Update project webtorrent to 0.74.1<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide('frappe.datetime');

frappe.defaultDateFormat = "YYYY-MM-DD";
frappe.defaultTimeFormat = "HH:mm:ss";
frappe.defaultDatetimeFormat = frappe.defaultDateFormat + " " + frappe.defaultTimeFormat;
moment.defaultFormat = frappe.defaultDateFormat;

frappe.provide("frappe.datetime");

$.extend(frappe.datetime, {
	convert_to_user_tz: function(date, format) {
		// format defaults to true
		// Converts the datetime string to system time zone first since the database only stores datetime in
		// system time zone and then convert the string to user time zone(from User doctype).
		let date_obj = null;
		if (frappe.boot.time_zone && frappe.boot.time_zone.system && frappe.boot.time_zone.user) {
			date_obj = moment.tz(date, frappe.boot.time_zone.system)
				.clone()
				.tz(frappe.boot.time_zone.user);
		} else {
			date_obj = moment(date);
		}

		return format === false ? date_obj : date_obj.format(frappe.defaultDatetimeFormat);
	},

	convert_to_system_tz: function(date, format) {
		// format defaults to true
		// Converts the datetime string to user time zone (from User doctype) first since this fn is called in datetime which accepts datetime
		// in user time zone then convert the string to user time zone.
		// This is done so that only one timezone is present in database and we do not end up storing local timezone since it changes
		// as per the location of user.
		let date_obj = null;
		if (frappe.boot.time_zone && frappe.boot.time_zone.system && frappe.boot.time_zone.user) {
			date_obj = moment.tz(date, frappe.boot.time_zone.user)
				.clone()
				.tz(frappe.boot.time_zone.system);
		} else {
			date_obj = moment(date);
		}

		return format===false ? date_obj : date_obj.format(frappe.defaultDatetimeFormat);
	},

	is_system_time_zone: function() {
		if (frappe.boot.time_zone && frappe.boot.time_zone.system && frappe.boot.time_zone.user) {
			return moment().tz(frappe.boot.time_zone.system).utcOffset() === moment().tz(frappe.boot.time_zone.user).utcOffset();
		}

		return true;
	},

	is_timezone_same: function() {
		return frappe.datetime.is_system_time_zone();
	},

	str_to_obj: function(d) {
		return moment(d, frappe.defaultDatetimeFormat)._d;
	},

	obj_to_str: function(d) {
		return moment(d).locale("en").format();
	},

	obj_to_user: function(d) {
		return moment(d).format(frappe.datetime.get_user_date_fmt().toUpperCase());
	},

	get_diff: function(d1, d2) {
		return moment(d1).diff(d2, "days");
	},

	get_hour_diff: function(d1, d2) {
		return moment(d1).diff(d2, "hours");
	},

	get_day_diff: function(d1, d2) {
		return moment(d1).diff(d2, "days");
	},

	add_days: function(d, days) {
		return moment(d).add(days, "days").format();
	},

	add_months: function(d, months) {
		return moment(d).add(months, "months").format();
	},

	week_start: function() {
		return moment().startOf("week").format();
	},

	week_end: function() {
		return moment().endOf("week").format();
	},

	month_start: function() {
		return moment().startOf("month").format();
	},

	month_end: function() {
		return moment().endOf("month").format();
	},

	quarter_start: function() {
		return moment().startOf("quarter").format();
	},

	quarter_end: function() {
		return moment().endOf("quarter").format();
	},

	year_start: function() {
		return moment().startOf("year").format();
	},

	year_end: function() {
		return moment().endOf("year").format();
	},

	get_user_time_fmt: function() {
		return frappe.sys_defaults && frappe.sys_defaults.time_format || "HH:mm:ss";
	},

	get_user_date_fmt: function() {
		return frappe.sys_defaults && frappe.sys_defaults.date_format || "yyyy-mm-dd";
	},

	get_user_fmt: function() {  // For backwards compatibility only
		return frappe.sys_defaults && frappe.sys_defaults.date_format || "yyyy-mm-dd";
	},

	str_to_user: function(val, only_time=false, only_date=false) {
		if (!val) return "";
		const user_date_fmt = frappe.datetime.get_user_date_fmt().toUpperCase();
		const user_time_fmt = frappe.datetime.get_user_time_fmt();
		let user_format = user_time_fmt;

		if (only_time) {
			let date_obj = moment(val, frappe.defaultTimeFormat);
			return date_obj.format(user_format);
		} else if (only_date) {
			let date_obj = moment(val, frappe.defaultDateFormat);
			return date_obj.format(user_date_fmt);
		} else {
			let date_obj = moment.tz(val, frappe.boot.time_zone.system);
			if (typeof val !== "string" || val.indexOf(" ") === -1) {
				user_format = user_date_fmt;
			} else {
				user_format = user_date_fmt + " " + user_time_fmt;
			}
			return date_obj
				.clone()
				.tz(frappe.boot.time_zone.user)
				.format(user_format);
		}
	},

	get_datetime_as_string: function(d) {
		return moment(d).format("YYYY-MM-DD HH:mm:ss");
	},

	user_to_str: function(val, only_time = false) {

		var user_time_fmt = frappe.datetime.get_user_time_fmt();
		if(only_time) {
			return moment(val, user_time_fmt)
				.format(frappe.defaultTimeFormat);
		}

		var user_fmt = frappe.datetime.get_user_date_fmt().toUpperCase();
		var system_fmt = "YYYY-MM-DD";

		if(val.indexOf(" ")!==-1) {
			user_fmt += " " + user_time_fmt;
			system_fmt += " HH:mm:ss";
		}

		// user_fmt.replace("YYYY", "YY")? user might only input 2 digits of the year, which should also be parsed
		return moment(val, [user_fmt.replace("YYYY", "YY"),
			user_fmt]).locale("en").format(system_fmt);
	},

	user_to_obj: function(d) {
		return frappe.datetime.str_to_obj(frappe.datetime.user_to_str(d));
	},

	global_date_format: function(d) {
		var m = moment(d);
		if(m._f && m._f.indexOf("HH")!== -1) {
			return m.format("Do MMMM YYYY, h:mma")
		} else {
			return m.format('Do MMMM YYYY');
		}
	},

	now_date: function(as_obj = false) {
		return frappe.datetime._date(frappe.defaultDateFormat, as_obj);
	},

	now_time: function(as_obj = false) {
		return frappe.datetime._date(frappe.defaultTimeFormat, as_obj);
	},

	now_datetime: function(as_obj = false) {
		return frappe.datetime._date(frappe.defaultDatetimeFormat, as_obj);
	},

	_date: function(format, as_obj = false) {
		/**
		 * Whenever we are getting now_date/datetime, always make sure dates are fetched using user time zone.
		 * This is to make sure that time is as per user time zone set in User doctype, If a user had to change the timezone,
		 * we will end up having multiple timezone by not honouring timezone in User doctype.
		 * This will make sure that at any point we know which timezone the user if following and not have random timezone
		 * when the timezone of the local machine changes.
		 */
		let time_zone = frappe.boot.time_zone ? frappe.boot.time_zone.user || frappe.boot.time_zone.system : frappe.sys_defaults.time_zone;
		let date = moment.tz(time_zone);

		return as_obj ? frappe.datetime.moment_to_date_obj(date) : date.format(format);
	},

	moment_to_date_obj: function(moment_obj) {
		const date_obj = new Date();
		const date_array = moment_obj.toArray();
		date_obj.setFullYear(date_array[0]);
		date_obj.setMonth(date_array[1]);
		date_obj.setDate(date_array[2]);
		date_obj.setHours(date_array[3]);
		date_obj.setMinutes(date_array[4]);
		date_obj.setSeconds(date_array[5]);
		date_obj.setMilliseconds(date_array[6]);
		return date_obj;
	},

	nowdate: function() {
		return frappe.datetime.now_date();
	},

	get_today: function() {
		return frappe.datetime.now_date();
	},

	get_time: (timestamp) => {
		// return time with AM/PM
		return moment(timestamp).format('hh:mm A');
	},

	validate: function(d) {
		return moment(d, [
			frappe.defaultDateFormat,
			frappe.defaultDatetimeFormat,
			frappe.defaultTimeFormat
		], true).isValid();
	},

	get_first_day_of_the_week_index() {
		const first_day_of_the_week = frappe.sys_defaults.first_day_of_the_week || "Sunday";
		return moment.weekdays().indexOf(first_day_of_the_week);
	}

});

// Proxy for dateutil and get_today
Object.defineProperties(window, {
	'dateutil': {
		get: function() {
			console.warn('Please use `frappe.datetime` instead of `dateutil`. It will be deprecated soon.');
			return frappe.datetime;
		},
		configurable: true
	},
	'date': {
		get: function() {
			console.warn('Please use `frappe.datetime` instead of `date`. It will be deprecated soon.');
			return frappe.datetime;
		},
		configurable: true
	},
	'get_today': {
		get: function() {
			console.warn('Please use `frappe.datetime.get_today` instead of `get_today`. It will be deprecated soon.');
			return frappe.datetime.get_today;
		},
		configurable: true
	}
});
<commit_msg>fix: Global datetime format: show AM/PM in capital letter<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide('frappe.datetime');

frappe.defaultDateFormat = "YYYY-MM-DD";
frappe.defaultTimeFormat = "HH:mm:ss";
frappe.defaultDatetimeFormat = frappe.defaultDateFormat + " " + frappe.defaultTimeFormat;
moment.defaultFormat = frappe.defaultDateFormat;

frappe.provide("frappe.datetime");

$.extend(frappe.datetime, {
	convert_to_user_tz: function(date, format) {
		// format defaults to true
		// Converts the datetime string to system time zone first since the database only stores datetime in
		// system time zone and then convert the string to user time zone(from User doctype).
		let date_obj = null;
		if (frappe.boot.time_zone && frappe.boot.time_zone.system && frappe.boot.time_zone.user) {
			date_obj = moment.tz(date, frappe.boot.time_zone.system)
				.clone()
				.tz(frappe.boot.time_zone.user);
		} else {
			date_obj = moment(date);
		}

		return format === false ? date_obj : date_obj.format(frappe.defaultDatetimeFormat);
	},

	convert_to_system_tz: function(date, format) {
		// format defaults to true
		// Converts the datetime string to user time zone (from User doctype) first since this fn is called in datetime which accepts datetime
		// in user time zone then convert the string to user time zone.
		// This is done so that only one timezone is present in database and we do not end up storing local timezone since it changes
		// as per the location of user.
		let date_obj = null;
		if (frappe.boot.time_zone && frappe.boot.time_zone.system && frappe.boot.time_zone.user) {
			date_obj = moment.tz(date, frappe.boot.time_zone.user)
				.clone()
				.tz(frappe.boot.time_zone.system);
		} else {
			date_obj = moment(date);
		}

		return format===false ? date_obj : date_obj.format(frappe.defaultDatetimeFormat);
	},

	is_system_time_zone: function() {
		if (frappe.boot.time_zone && frappe.boot.time_zone.system && frappe.boot.time_zone.user) {
			return moment().tz(frappe.boot.time_zone.system).utcOffset() === moment().tz(frappe.boot.time_zone.user).utcOffset();
		}

		return true;
	},

	is_timezone_same: function() {
		return frappe.datetime.is_system_time_zone();
	},

	str_to_obj: function(d) {
		return moment(d, frappe.defaultDatetimeFormat)._d;
	},

	obj_to_str: function(d) {
		return moment(d).locale("en").format();
	},

	obj_to_user: function(d) {
		return moment(d).format(frappe.datetime.get_user_date_fmt().toUpperCase());
	},

	get_diff: function(d1, d2) {
		return moment(d1).diff(d2, "days");
	},

	get_hour_diff: function(d1, d2) {
		return moment(d1).diff(d2, "hours");
	},

	get_day_diff: function(d1, d2) {
		return moment(d1).diff(d2, "days");
	},

	add_days: function(d, days) {
		return moment(d).add(days, "days").format();
	},

	add_months: function(d, months) {
		return moment(d).add(months, "months").format();
	},

	week_start: function() {
		return moment().startOf("week").format();
	},

	week_end: function() {
		return moment().endOf("week").format();
	},

	month_start: function() {
		return moment().startOf("month").format();
	},

	month_end: function() {
		return moment().endOf("month").format();
	},

	quarter_start: function() {
		return moment().startOf("quarter").format();
	},

	quarter_end: function() {
		return moment().endOf("quarter").format();
	},

	year_start: function() {
		return moment().startOf("year").format();
	},

	year_end: function() {
		return moment().endOf("year").format();
	},

	get_user_time_fmt: function() {
		return frappe.sys_defaults && frappe.sys_defaults.time_format || "HH:mm:ss";
	},

	get_user_date_fmt: function() {
		return frappe.sys_defaults && frappe.sys_defaults.date_format || "yyyy-mm-dd";
	},

	get_user_fmt: function() {  // For backwards compatibility only
		return frappe.sys_defaults && frappe.sys_defaults.date_format || "yyyy-mm-dd";
	},

	str_to_user: function(val, only_time=false, only_date=false) {
		if (!val) return "";
		const user_date_fmt = frappe.datetime.get_user_date_fmt().toUpperCase();
		const user_time_fmt = frappe.datetime.get_user_time_fmt();
		let user_format = user_time_fmt;

		if (only_time) {
			let date_obj = moment(val, frappe.defaultTimeFormat);
			return date_obj.format(user_format);
		} else if (only_date) {
			let date_obj = moment(val, frappe.defaultDateFormat);
			return date_obj.format(user_date_fmt);
		} else {
			let date_obj = moment.tz(val, frappe.boot.time_zone.system);
			if (typeof val !== "string" || val.indexOf(" ") === -1) {
				user_format = user_date_fmt;
			} else {
				user_format = user_date_fmt + " " + user_time_fmt;
			}
			return date_obj
				.clone()
				.tz(frappe.boot.time_zone.user)
				.format(user_format);
		}
	},

	get_datetime_as_string: function(d) {
		return moment(d).format("YYYY-MM-DD HH:mm:ss");
	},

	user_to_str: function(val, only_time = false) {

		var user_time_fmt = frappe.datetime.get_user_time_fmt();
		if(only_time) {
			return moment(val, user_time_fmt)
				.format(frappe.defaultTimeFormat);
		}

		var user_fmt = frappe.datetime.get_user_date_fmt().toUpperCase();
		var system_fmt = "YYYY-MM-DD";

		if(val.indexOf(" ")!==-1) {
			user_fmt += " " + user_time_fmt;
			system_fmt += " HH:mm:ss";
		}

		// user_fmt.replace("YYYY", "YY")? user might only input 2 digits of the year, which should also be parsed
		return moment(val, [user_fmt.replace("YYYY", "YY"),
			user_fmt]).locale("en").format(system_fmt);
	},

	user_to_obj: function(d) {
		return frappe.datetime.str_to_obj(frappe.datetime.user_to_str(d));
	},

	global_date_format: function(d) {
		var m = moment(d);
		if(m._f && m._f.indexOf("HH")!== -1) {
			return m.format("Do MMMM YYYY, hh:mm A");
		} else {
			return m.format('Do MMMM YYYY');
		}
	},

	now_date: function(as_obj = false) {
		return frappe.datetime._date(frappe.defaultDateFormat, as_obj);
	},

	now_time: function(as_obj = false) {
		return frappe.datetime._date(frappe.defaultTimeFormat, as_obj);
	},

	now_datetime: function(as_obj = false) {
		return frappe.datetime._date(frappe.defaultDatetimeFormat, as_obj);
	},

	_date: function(format, as_obj = false) {
		/**
		 * Whenever we are getting now_date/datetime, always make sure dates are fetched using user time zone.
		 * This is to make sure that time is as per user time zone set in User doctype, If a user had to change the timezone,
		 * we will end up having multiple timezone by not honouring timezone in User doctype.
		 * This will make sure that at any point we know which timezone the user if following and not have random timezone
		 * when the timezone of the local machine changes.
		 */
		let time_zone = frappe.boot.time_zone ? frappe.boot.time_zone.user || frappe.boot.time_zone.system : frappe.sys_defaults.time_zone;
		let date = moment.tz(time_zone);

		return as_obj ? frappe.datetime.moment_to_date_obj(date) : date.format(format);
	},

	moment_to_date_obj: function(moment_obj) {
		const date_obj = new Date();
		const date_array = moment_obj.toArray();
		date_obj.setFullYear(date_array[0]);
		date_obj.setMonth(date_array[1]);
		date_obj.setDate(date_array[2]);
		date_obj.setHours(date_array[3]);
		date_obj.setMinutes(date_array[4]);
		date_obj.setSeconds(date_array[5]);
		date_obj.setMilliseconds(date_array[6]);
		return date_obj;
	},

	nowdate: function() {
		return frappe.datetime.now_date();
	},

	get_today: function() {
		return frappe.datetime.now_date();
	},

	get_time: (timestamp) => {
		// return time with AM/PM
		return moment(timestamp).format('hh:mm A');
	},

	validate: function(d) {
		return moment(d, [
			frappe.defaultDateFormat,
			frappe.defaultDatetimeFormat,
			frappe.defaultTimeFormat
		], true).isValid();
	},

	get_first_day_of_the_week_index() {
		const first_day_of_the_week = frappe.sys_defaults.first_day_of_the_week || "Sunday";
		return moment.weekdays().indexOf(first_day_of_the_week);
	}

});

// Proxy for dateutil and get_today
Object.defineProperties(window, {
	'dateutil': {
		get: function() {
			console.warn('Please use `frappe.datetime` instead of `dateutil`. It will be deprecated soon.');
			return frappe.datetime;
		},
		configurable: true
	},
	'date': {
		get: function() {
			console.warn('Please use `frappe.datetime` instead of `date`. It will be deprecated soon.');
			return frappe.datetime;
		},
		configurable: true
	},
	'get_today': {
		get: function() {
			console.warn('Please use `frappe.datetime.get_today` instead of `get_today`. It will be deprecated soon.');
			return frappe.datetime.get_today;
		},
		configurable: true
	}
});
<|endoftext|>


<commit_before>/**
 *
 *  Copyright 2016 Netflix, Inc.
 *
 *     Licensed under the Apache License, Version 2.0 (the "License");
 *     you may not use this file except in compliance with the License.
 *     You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 *
 */
/* eslint no-underscore-dangle: 0, no-restricted-syntax: 0 */
import _ from 'lodash';

const Console = console;

function Graph (nodes, edges) {
  this.validateData(nodes, edges);

  this.nodes = nodes;
  this.edges = edges;

  this._entryNodeMap = this.nodes.reduce((val, node) => {
    val[node.name] = true;
    return val;
  }, {});
  this._incomingNodes = {};
  this._outgoingNodes = {};

  this._outgoingEdges = {};

  _.each(edges, (edge) => {
    // Add the connection to the incoming connections object
    this._incomingNodes[edge.target] = this._incomingNodes[edge.target] || {};
    this._incomingNodes[edge.target][edge.source] = true;

    // Add the connection to the outgoing connections object
    this._outgoingNodes[edge.source] = this._outgoingNodes[edge.source] || {};
    this._outgoingNodes[edge.source][edge.target] = true;

    // Add the edge to the outgoing edges map
    this._outgoingEdges[edge.source] = this._outgoingEdges[edge.source] || [];
    this._outgoingEdges[edge.source].push(edge);
  });

  // In the rare case of entry nodes that have circular connections, we need to make sure we do not remove
  // the node from the entry node list.
  _.each(this._incomingNodes, (targetNodes, sourceNodeName) => {
    const incomingNodeNames = Object.keys(targetNodes);
    const outgoingNodeNames = Object.keys(this._outgoingNodes[sourceNodeName] || {});
    const incomingWithoutOutgoing = _.without(incomingNodeNames, ...outgoingNodeNames);
    if (incomingWithoutOutgoing.length !== 0) {
      delete this._entryNodeMap[sourceNodeName];
    }
  });
}

Graph.prototype.validateData = function (nodes, edges) {
  const nodeMap = nodes.reduce((val, node) => {
    val[node.name] = node;
    return val;
  }, {});

  // Warn if connection connects to a node that doesnt exist
  let i;
  for (i in edges) {
    if (nodeMap[edges[i].source] === undefined) {
      Console.warn(`Attempted to layout a connection with non-existent source node: ${edges[i].source}.`);
      edges.splice(i, 1);
    } else {
      nodeMap[edges[i].source].connected = true;
    }
    if (nodeMap[edges[i].target] === undefined) {
      Console.warn(`Attempted to layout a connection with non-existent target node: ${edges[i].target}.`);
      edges.splice(i, 1);
    } else {
      nodeMap[edges[i].target].connected = true;
    }
  }

  if (nodes.length > 1) {
    for (i in nodes) {
      if (!nodeMap[nodes[i].name] || !nodeMap[nodes[i].name].connected) {
        nodes.splice(i, 1);
      }
    }
  }
};

Graph.prototype.outgoingNodes = function (nodeName) {
  if (this._outgoingNodes[nodeName] !== undefined) {
    return Object.keys(this._outgoingNodes[nodeName]);
  }
  return [];
};

Graph.prototype.incomingNodes = function (nodeName) {
  if (this._incomingNodes[nodeName] !== undefined) {
    return Object.keys(this._incomingNodes[nodeName]);
  }
  return [];
};

Graph.prototype.outgoingEdges = function (nodeName) {
  return this._outgoingEdges[nodeName] || [];
};

Graph.prototype.entryNodes = function () {
  if (this._entryNodeMap !== undefined) {
    return Object.keys(this._entryNodeMap);
  }
  return [];
};

Graph.prototype.buildGraph = function () {
  this.validateData();
};

Graph.prototype.removeEdge = function (edge) {
  delete this._outgoingNodes[edge.source][edge.target];
  delete this._incomingNodes[edge.target][edge.source];
  if (this._outgoingEdges[edge.source]) {
    _.remove(this._outgoingEdges[edge.source], anEdge => anEdge.source === edge.source && anEdge.target === edge.target);
  }
  _.remove(this.edges, anEdge => anEdge.source === edge.source && anEdge.target === edge.target);
};

Graph.prototype.addEdge = function (edge) {
  this._outgoingNodes[edge.source][edge.target] = true;
  this._incomingNodes[edge.target][edge.source] = true;
  this._outgoingEdges[edge.source] = this._outgoingEdges[edge.source] || [];
  this._outgoingEdges[edge.source].push(edge);
};

Graph.prototype.reverseEdge = function (edge) {
  this.removeEdge(edge);
  const oldSource = edge.source;
  edge.source = edge.target;
  edge.target = oldSource;
  edge.reversed = !edge.reversed;
  this.addEdge(edge);
};

Graph.prototype.removeSameEdges = function () {
  this.storedSameEdges = this.storedSameEdges || [];
  _.each(this.edges, (edge) => {
    if (edge && edge.source === edge.target) {
      this.storedSameEdges.push(edge);
      this.removeEdge(edge);
    }
  });
};

Graph.prototype.restoreSameEdges = function () {
  _.each(this.storedSameEdges, (edge) => {
    this.addEdge(edge);
  });
  this.storedSameEdges.length = 0;
};

Graph.prototype.getNode = function (nodeName) {
  return _.find(this.nodes, ['name', nodeName]);
};

module.exports = Graph;
<commit_msg>Replaced two for..in style for-loops (in ltrTreeGraph.js) that contained a nested splice call with a safer for-loop.  The for..in style with a nested splice call can cause the array being iterated over to change length, which causes the for..in to change its indexing range.  Ultimately, resulting skipped array elements.  Now, using safe setup where the array is iterated starting with the end element and decrementing to the beginning so that removing the 'current' index doesn't affect the indexing.<commit_after>/**
 *
 *  Copyright 2016 Netflix, Inc.
 *
 *     Licensed under the Apache License, Version 2.0 (the "License");
 *     you may not use this file except in compliance with the License.
 *     You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *     Unless required by applicable law or agreed to in writing, software
 *     distributed under the License is distributed on an "AS IS" BASIS,
 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *     See the License for the specific language governing permissions and
 *     limitations under the License.
 *
 */
/* eslint no-underscore-dangle: 0, no-restricted-syntax: 0 */
import _ from 'lodash';

const Console = console;

function Graph (nodes, edges) {
  this.validateData(nodes, edges);

  this.nodes = nodes;
  this.edges = edges;

  this._entryNodeMap = this.nodes.reduce((val, node) => {
    val[node.name] = true;
    return val;
  }, {});
  this._incomingNodes = {};
  this._outgoingNodes = {};

  this._outgoingEdges = {};

  _.each(edges, (edge) => {
    // Add the connection to the incoming connections object
    this._incomingNodes[edge.target] = this._incomingNodes[edge.target] || {};
    this._incomingNodes[edge.target][edge.source] = true;

    // Add the connection to the outgoing connections object
    this._outgoingNodes[edge.source] = this._outgoingNodes[edge.source] || {};
    this._outgoingNodes[edge.source][edge.target] = true;

    // Add the edge to the outgoing edges map
    this._outgoingEdges[edge.source] = this._outgoingEdges[edge.source] || [];
    this._outgoingEdges[edge.source].push(edge);
  });

  // In the rare case of entry nodes that have circular connections, we need to make sure we do not remove
  // the node from the entry node list.
  _.each(this._incomingNodes, (targetNodes, sourceNodeName) => {
    const incomingNodeNames = Object.keys(targetNodes);
    const outgoingNodeNames = Object.keys(this._outgoingNodes[sourceNodeName] || {});
    const incomingWithoutOutgoing = _.without(incomingNodeNames, ...outgoingNodeNames);
    if (incomingWithoutOutgoing.length !== 0) {
      delete this._entryNodeMap[sourceNodeName];
    }
  });
}

Graph.prototype.validateData = function (nodes, edges) {
  const nodeMap = nodes.reduce((val, node) => {
    val[node.name] = node;
    return val;
  }, {});

  // Warn if connection connects to a node that doesnt exist
  for (let i = edges.length - 1; i >= 0; i--) {
    if (nodeMap[edges[i].source] === undefined) {
      Console.warn(`Attempted to layout a connection with non-existent source node: ${edges[i].source}.`);
      edges.splice(i, 1);
    } else {
      nodeMap[edges[i].source].connected = true;
    }
    if (nodeMap[edges[i].target] === undefined) {
      Console.warn(`Attempted to layout a connection with non-existent target node: ${edges[i].target}.`);
      edges.splice(i, 1);
    } else {
      nodeMap[edges[i].target].connected = true;
    }
  }

  if (nodes.length > 1) {
    for (let i = nodes.length - 1; i >= 0; i--) {
      if (!nodeMap[nodes[i].name] || !nodeMap[nodes[i].name].connected) {
        nodes.splice(i, 1);
      }
    }
  }
};

Graph.prototype.outgoingNodes = function (nodeName) {
  if (this._outgoingNodes[nodeName] !== undefined) {
    return Object.keys(this._outgoingNodes[nodeName]);
  }
  return [];
};

Graph.prototype.incomingNodes = function (nodeName) {
  if (this._incomingNodes[nodeName] !== undefined) {
    return Object.keys(this._incomingNodes[nodeName]);
  }
  return [];
};

Graph.prototype.outgoingEdges = function (nodeName) {
  return this._outgoingEdges[nodeName] || [];
};

Graph.prototype.entryNodes = function () {
  if (this._entryNodeMap !== undefined) {
    return Object.keys(this._entryNodeMap);
  }
  return [];
};

Graph.prototype.buildGraph = function () {
  this.validateData();
};

Graph.prototype.removeEdge = function (edge) {
  delete this._outgoingNodes[edge.source][edge.target];
  delete this._incomingNodes[edge.target][edge.source];
  if (this._outgoingEdges[edge.source]) {
    _.remove(this._outgoingEdges[edge.source], anEdge => anEdge.source === edge.source && anEdge.target === edge.target);
  }
  _.remove(this.edges, anEdge => anEdge.source === edge.source && anEdge.target === edge.target);
};

Graph.prototype.addEdge = function (edge) {
  this._outgoingNodes[edge.source][edge.target] = true;
  this._incomingNodes[edge.target][edge.source] = true;
  this._outgoingEdges[edge.source] = this._outgoingEdges[edge.source] || [];
  this._outgoingEdges[edge.source].push(edge);
};

Graph.prototype.reverseEdge = function (edge) {
  this.removeEdge(edge);
  const oldSource = edge.source;
  edge.source = edge.target;
  edge.target = oldSource;
  edge.reversed = !edge.reversed;
  this.addEdge(edge);
};

Graph.prototype.removeSameEdges = function () {
  this.storedSameEdges = this.storedSameEdges || [];
  _.each(this.edges, (edge) => {
    if (edge && edge.source === edge.target) {
      this.storedSameEdges.push(edge);
      this.removeEdge(edge);
    }
  });
};

Graph.prototype.restoreSameEdges = function () {
  _.each(this.storedSameEdges, (edge) => {
    this.addEdge(edge);
  });
  this.storedSameEdges.length = 0;
};

Graph.prototype.getNode = function (nodeName) {
  return _.find(this.nodes, ['name', nodeName]);
};

module.exports = Graph;
<|endoftext|>


<commit_before>import React, {useState, useEffect} from 'react';

import { Accordion, Button, Card } from 'react-bootstrap';

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import authUtils from '../AuthUtils';
import { getDateRangeString, timestampToISOString } from "../Utils/time.js";
import DeleteTripButton from './delete-trip-button.js';
import ViewActivitiesButton from './view-activities-button.js';
import * as DB from '../../constants/database.js';

/**
 * A trip object containing the data stored in a trip document in Firestore.
 * @typedef {Object} TripData
 * @property {string} title The trips's title.
 * @property {string} description A description of the trip.
 * @property {string} destination The general destination of the trip.
 * @property {firebase.firestore.Timestamp} start_date Start date Firestore
 *     timestamp object.
 * @property {firebase.firestore.Timestamp} end_date End date Firestore
 *     timestamp object
 * @property {!string[]} collaborators An array of collaborator uids.
 */

/**
 * Return collaborator emails corresponding to the collaborator uid's
 * `collaboratorUidArr` in a comma separated string.
 *
 * @param {!string[]} collaboratorEmailArr Array of user emails sorted in
 *     alphabetical order.
 * @return {!string[]} Array of user emails where first element is the current
 *     user email and the following elements maintain their previous order.
 */
export function moveCurUserEmailToFront(collaboratorEmailArr) {
  collaboratorEmailArr = collaboratorEmailArr.filter(email => {
    return email !== authUtils.getCurUserEmail();
  });
  return [authUtils.getCurUserEmail()].concat(collaboratorEmailArr);
}

/**
 * Component corresponding to the container containing an individual trip.
 *
 * Trip object fields are cleaned and vetted with firestore security rules
 * when trips are added and/or editted. Thus, no error checking is done here
 * on the 'display' side.
 *
 * @property {Object} props These are the props for this component:
 * @property {TripData} props.tripData Object holding a Trip document data.
 * @property {string} props.tripId The document id associated with the trip.
 * @property {Function} props.handleEditTrip Event handler responsible for
 *     displaying the edit trip modal.
 */
const Trip = (props) => {
  // Unpack trip document data.
  const title = props.tripData[DB.TRIPS_TITLE];
  const description = props.tripData[DB.TRIPS_DESCRIPTION];
  const destination = props.tripData[DB.TRIPS_DESTINATION];
  const startDateTimestamp = props.tripData[DB.TRIPS_START_DATE];
  const endDateTimestamp = props.tripData[DB.TRIPS_END_DATE];
  const collaboratorUidArr = props.tripData[DB.TRIPS_COLLABORATORS];
  const [collaboratorEmailsStr, setCollaboratorEmailsStr] = useState('');

  useEffect(() => {
    // Only set state collaboratorEmailsStr if component is mounted. This is
    // a precautionary to mitigate warnings that occur when setting state on
    // a component that has already unmounted. See more here
    // https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component.
    let componentStillMounted = true;

    async function fetchCollaboratorEmails() {
      let collaboratorEmailArr =
          await authUtils.getUserEmailArrFromUserUidArr(collaboratorUidArr);
      collaboratorEmailArr = moveCurUserEmailToFront(collaboratorEmailArr);
      if (componentStillMounted) {
        setCollaboratorEmailsStr(collaboratorEmailArr.join(', '));
      }
    }

    fetchCollaboratorEmails();
    return () => { componentStillMounted = false; };
  }, [collaboratorUidArr]);

  // Re-package trip document data with correctly formatted data for the
  // SaveTripModal component to use when filling out form input default values.
  const formattedTripData = {
    [DB.TRIPS_TITLE]: title,
    [DB.TRIPS_DESCRIPTION]: description,
    [DB.TRIPS_DESTINATION]: destination,
    [DB.TRIPS_START_DATE]: timestampToISOString(startDateTimestamp),
    [DB.TRIPS_END_DATE]: timestampToISOString(endDateTimestamp),
    [DB.TRIPS_COLLABORATORS]: collaboratorEmailsStr.split(', '),
  };

  return (
    <Card>
      <Accordion.Toggle as={Card.Header} eventKey={props.eventKey}>

{title}

      </Accordion.Toggle>
      <Accordion.Collapse eventKey={props.eventKey}>
        <Card.Body>
          <FontAwesomeIcon icon={['fas', 'map-marker-alt']} />

{destination}

{getDateRangeString(startDateTimestamp, endDateTimestamp)}

{description}

{collaboratorEmailsStr}

          <DeleteTripButton tripId={props.tripId} />
          <Button
            type='button'
            onClick={() => props.handleEditTrip(props.tripId, formattedTripData)}
            variant='primary'
          >
            Edit
          </Button>
          <ViewActivitiesButton tripId={props.tripId} />
        </Card.Body>
      </Accordion.Collapse>
    </Card>
  );
};

export default Trip;
<commit_msg>Revert import changes in trip.js.<commit_after>import React, {useState, useEffect} from 'react';

import { Accordion, Button, Card } from 'react-bootstrap';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import authUtils from '../AuthUtils';
import { timestampToISOString, getDateRangeString } from '../Utils/time.js';
import DeleteTripButton from './delete-trip-button.js';
import ViewActivitiesButton from './view-activities-button.js';
import * as DB from '../../constants/database.js';

/**
 * A trip object containing the data stored in a trip document in Firestore.
 * @typedef {Object} TripData
 * @property {string} title The trips's title.
 * @property {string} description A description of the trip.
 * @property {string} destination The general destination of the trip.
 * @property {firebase.firestore.Timestamp} start_date Start date Firestore
 *     timestamp object.
 * @property {firebase.firestore.Timestamp} end_date End date Firestore
 *     timestamp object
 * @property {!string[]} collaborators An array of collaborator uids.
 */

/**
 * Return collaborator emails corresponding to the collaborator uid's
 * `collaboratorUidArr` in a comma separated string.
 *
 * @param {!string[]} collaboratorEmailArr Array of user emails sorted in
 *     alphabetical order.
 * @return {!string[]} Array of user emails where first element is the current
 *     user email and the following elements maintain their previous order.
 */
export function moveCurUserEmailToFront(collaboratorEmailArr) {
  collaboratorEmailArr = collaboratorEmailArr.filter(email => {
    return email !== authUtils.getCurUserEmail();
  });
  return [authUtils.getCurUserEmail()].concat(collaboratorEmailArr);
}

/**
 * Component corresponding to the container containing an individual trip.
 *
 * Trip object fields are cleaned and vetted with firestore security rules
 * when trips are added and/or editted. Thus, no error checking is done here
 * on the 'display' side.
 *
 * @property {Object} props These are the props for this component:
 * @property {TripData} props.tripData Object holding a Trip document data.
 * @property {string} props.tripId The document id associated with the trip.
 * @property {Function} props.handleEditTrip Event handler responsible for
 *     displaying the edit trip modal.
 */
const Trip = (props) => {
  // Unpack trip document data.
  const title = props.tripData[DB.TRIPS_TITLE];
  const description = props.tripData[DB.TRIPS_DESCRIPTION];
  const destination = props.tripData[DB.TRIPS_DESTINATION];
  const startDateTimestamp = props.tripData[DB.TRIPS_START_DATE];
  const endDateTimestamp = props.tripData[DB.TRIPS_END_DATE];
  const collaboratorUidArr = props.tripData[DB.TRIPS_COLLABORATORS];
  const [collaboratorEmailsStr, setCollaboratorEmailsStr] = useState('');

  useEffect(() => {
    // Only set state collaboratorEmailsStr if component is mounted. This is
    // a precautionary to mitigate warnings that occur when setting state on
    // a component that has already unmounted. See more here
    // https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component.
    let componentStillMounted = true;

    async function fetchCollaboratorEmails() {
      let collaboratorEmailArr =
          await authUtils.getUserEmailArrFromUserUidArr(collaboratorUidArr);
      collaboratorEmailArr = moveCurUserEmailToFront(collaboratorEmailArr);
      if (componentStillMounted) {
        setCollaboratorEmailsStr(collaboratorEmailArr.join(', '));
      }
    }

    fetchCollaboratorEmails();
    return () => { componentStillMounted = false; };
  }, [collaboratorUidArr]);

  // Re-package trip document data with correctly formatted data for the
  // SaveTripModal component to use when filling out form input default values.
  const formattedTripData = {
    [DB.TRIPS_TITLE]: title,
    [DB.TRIPS_DESCRIPTION]: description,
    [DB.TRIPS_DESTINATION]: destination,
    [DB.TRIPS_START_DATE]: timestampToISOString(startDateTimestamp),
    [DB.TRIPS_END_DATE]: timestampToISOString(endDateTimestamp),
    [DB.TRIPS_COLLABORATORS]: collaboratorEmailsStr.split(', '),
  };

  return (
    <Card>
      <Accordion.Toggle as={Card.Header} eventKey={props.eventKey}>

{title}

      </Accordion.Toggle>
      <Accordion.Collapse eventKey={props.eventKey}>
        <Card.Body>
          <FontAwesomeIcon icon={['fas', 'map-marker-alt']} />

{destination}

{getDateRangeString(startDateTimestamp, endDateTimestamp)}

{description}

{collaboratorEmailsStr}

          <DeleteTripButton tripId={props.tripId} />
          <Button
            type='button'
            onClick={() => props.handleEditTrip(props.tripId, formattedTripData)}
            variant='primary'
          >
            Edit
          </Button>
          <ViewActivitiesButton tripId={props.tripId} />
        </Card.Body>
      </Accordion.Collapse>
    </Card>
  );
};

export default Trip;
<|endoftext|>


<commit_before>var parse = require('./parse');
var clean = require('./clean');

module.exports = Engine = {};

var pythons = {};
var outputs = {};
var pythonNum = 0;

Engine.runPython = function(operation, func, options, cb) {
  var cleanup = clean.clean(func, options, cb);
  func    = cleanup.func;
  options = cleanup.options;
  cb      = cleanup.callback;

  options = JSON.stringify(options);

  // immediately invoked function in case user invokes runPython multiple times,
  // starting multiple child processes, causing race condition,
  // causing stdouts to potentially overlap.
  (function (num){
    pythons[num] = require('child_process').spawn(
      'python',
      [__dirname + '/../py/exec.py', operation, func, options]);
    outputs[num] = '';
    pythons[num].stdout.on('data', function(data){
      outputs[num] += data;
    });
    pythons[num].stdout.on('close', function(){
      // cb(outputs[num]);
      cb(JSON.parse(outputs[num]));
    });
  })(pythonNum++)
}
<commit_msg>Generalize engine to work for nnlg and min<commit_after>var parse = require('./parse');
var clean = require('./clean');

module.exports = Engine = {};

var pythons = {};
var outputs = {};
var pythonNum = 0;

Engine.runPython = function(operation, a, b, cb) {
  if (operation === 'minimize') {
    var cleanup = clean.cleanMin(a, b, cb);
    a   = cleanup.func;
    b   = cleanup.options;
    cb  = cleanup.callback;
    b = JSON.stringify(b);
  } else if (operation === 'nnls') {
    cb = clean.cleanCB(cb);
    a = JSON.stringify(a);
    b = JSON.stringify(b);
  }

  // immediately invoked function in case user invokes runPython multiple times,
  // starting multiple child processes, causing race condition,
  // causing stdouts to potentially overlap.
  (function (num){
    pythons[num] = require('child_process').spawn(
      'python',
      [__dirname + '/../py/exec.py', operation, a, b]);
    outputs[num] = '';
    pythons[num].stdout.on('data', function(data){
      outputs[num] += data;
    });
    pythons[num].stdout.on('close', function(){
      // cb(outputs[num]);
      cb(JSON.parse(outputs[num]));
    });
  })(pythonNum++)
}
<|endoftext|>


<commit_before>/**
  Copyright 2010-2011 Ondrej Certik <ondrej@certik.cz>
  Copyright 2010-2011 Mateusz Paprocki <mattpap@gmail.com>
  Copyright 2011 Christian Iversen <ci@sikkerhed.org>

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
**/

var iter = __inherit(object, "iter");

$PY.iter = iter;

iter.PY$__init__ = function(obj) {
    this.index = 0;
    if (obj === undefined) {
        throw py_builtins.TypeError("iter() expects at least 1 argument");
    } else if (obj instanceof Array) {
        this.seq = obj;
    } else if (typeof(obj) === "string") {
        this.seq = obj.split("");
        for (var i = 0; i < this.seq.length; i++) {
            this.seq[i] = str(this.seq[i]);
        }
    } else {
        throw py_builtins.TypeError("object is not iterable");
    }
};

var __iter_real__ = iter.PY$__create__;

iter.PY$__create__ = function(cls, obj) {
    if (obj.PY$__class__ == iter) {
       return obj;
    } else if (obj.PY$__iter__ !== undefined) {
        return obj.PY$__iter__();
    } else {
        return __iter_real__(cls, obj);
    }
};

iter.PY$__str__ = function () {
    return str("<iterator of " + this.seq + " at " + this.index + ">");
};

iter.PY$next = function() {
    var value = this.seq[this.index++];

    if (this.index <= this.seq.length) {
        if (value === undefined) {
            return None;
        } else {
            return value;
        }
    } else {
        throw py_builtins.StopIteration('no more items');
    }
};
<commit_msg> * Style fix<commit_after>/**
  Copyright 2010-2011 Ondrej Certik <ondrej@certik.cz>
  Copyright 2010-2011 Mateusz Paprocki <mattpap@gmail.com>
  Copyright 2011 Christian Iversen <ci@sikkerhed.org>

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
**/

var iter = __inherit(object, "iter");

$PY.iter = iter;

iter.PY$__init__ = function(obj) {
    this.index = 0;
    if (obj === undefined) {
        throw py_builtins.TypeError("iter() expects at least 1 argument");
    } else if (obj instanceof Array) {
        this.seq = obj;
    } else if (typeof obj === "string") {
        this.seq = obj.split("");
        for (var i = 0; i < this.seq.length; i++) {
            this.seq[i] = str(this.seq[i]);
        }
    } else {
        throw py_builtins.TypeError("object is not iterable");
    }
};

var __iter_real__ = iter.PY$__create__;

iter.PY$__create__ = function(cls, obj) {
    if (obj.PY$__class__ == iter) {
       return obj;
    } else if (obj.PY$__iter__ !== undefined) {
        return obj.PY$__iter__();
    } else {
        return __iter_real__(cls, obj);
    }
};

iter.PY$__str__ = function () {
    return str("<iterator of " + this.seq + " at " + this.index + ">");
};

iter.PY$next = function() {
    var value = this.seq[this.index++];

    if (this.index <= this.seq.length) {
        if (value === undefined) {
            return None;
        } else {
            return value;
        }
    } else {
        throw py_builtins.StopIteration('no more items');
    }
};
<|endoftext|>


<commit_before>(function () {

EJSON = {}; // Global!
var customTypes = {};
// Add a custom type, using a method of your choice to get to and
// from a basic JSON-able representation.  The factory argument
// is a function of JSON-able --> your object
// The type you add must have:
// - A clone() method, so that Meteor can deep-copy it when necessary.
// - A equals() method, so that Meteor can compare it
// - A toJSONValue() method, so that Meteor can serialize it
// - a typeName() method, to show how to look it up in our type table.
// It is okay if these methods are monkey-patched on.
EJSON.addType = function (name, factory) {
  if (_.has(customTypes, name))
    throw new Error("Type " + name + " already present");
  customTypes[name] = factory;
};

var builtinConverters = [
  { // undefined
    matchJSONValue: function (obj) {
      return _.has(obj, '$undefined') && _.size(obj) === 1;
    },
    matchObject: function (obj) {
      return obj === undefined;
    },
    toJSONValue: function (obj) {
      return {$undefined: null};
    },
    fromJSONValue: function (obj) {
      return undefined;
    }
  },
  { // Date
    matchJSONValue: function (obj) {
      return _.has(obj, '$date') && _.size(obj) === 1;
    },
    matchObject: function (obj) {
      return obj instanceof Date;
    },
    toJSONValue: function (obj) {
      return {$date: obj.getTime()};
    },
    fromJSONValue: function (obj) {
      return new Date(obj.$date);
    }
  },
  { // Binary
    matchJSONValue: function (obj) {
      return _.has(obj, '$binary') && _.size(obj) === 1;
    },
    matchObject: function (obj) {
      return typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array
        || (obj && _.has(obj, '$Uint8ArrayPolyfill'));
    },
    toJSONValue: function (obj) {
      return {$binary: EJSON._base64Encode(obj)};
    },
    fromJSONValue: function (obj) {
      return EJSON._base64Decode(obj.$binary);
    }
  },
  { // Escaping one level
    matchJSONValue: function (obj) {
      return _.has(obj, '$escape') && _.size(obj) === 1;
    },
    matchObject: function (obj) {
      if (_.isEmpty(obj) || _.size(obj) > 2) {
        return false;
      }
      return _.any(builtinConverters, function (converter) {
        return converter.matchJSONValue(obj);
      });
    },
    toJSONValue: function (obj) {
      var newObj = {};
      _.each(obj, function (value, key) {
        newObj[key] = EJSON.toJSONValue(value);
      });
      return {$escape: newObj};
    },
    fromJSONValue: function (obj) {
      var newObj = {};
      _.each(obj.$escape, function (value, key) {
        newObj[key] = EJSON.fromJSONValue(value);
      });
      return newObj;
    }
  },
  { // Custom
    matchJSONValue: function (obj) {
      return _.has(obj, '$type') && _.has(obj, '$value') && _.size(obj) === 2;
    },
    matchObject: function (obj) {
      return obj &&
        typeof obj.toJSONValue === 'function' &&
        typeof obj.typeName === 'function' &&
        _.has(customTypes, obj.typeName());
    },
    toJSONValue: function (obj) {
      return {$type: obj.typeName(), $value: obj.toJSONValue()};
    },
    fromJSONValue: function (obj) {
      var typeName = obj.$type;
      var converter = customTypes[typeName];
      return converter(obj.$value);
    }
  }
];

//for both arrays and objects, in-place modification.
var adjustTypesToJSONValue =
EJSON._adjustTypesToJSONValue = function (obj) {
  if (obj === null)
    return;
  _.each(obj, function (value, key) {
    if (typeof value !== 'object' && value !== undefined)
      return; // continue
    var changed = toJSONValueHelper(value);
    if (changed) {
      obj[key] = changed;
      return; // on to the next key
    }
    // if we get here, value is an object but not adjustable
    // at this level.  recurse.
    adjustTypesToJSONValue(value);
  });
};

// Either return the JSON-compatible version of the argument, or undefined (if
// the item isn't itself replaceable, but maybe some fields in it are)
var toJSONValueHelper = function (item) {
  for (var i = 0; i < builtinConverters.length; i++) {
    var converter = builtinConverters[i];
    if (converter.matchObject(item)) {
      return converter.toJSONValue(item);
    }
  }
  return undefined;
};

EJSON.toJSONValue = function (item) {
  var changed = toJSONValueHelper(item);
  if (changed !== undefined)
    return changed;
  if (typeof item === 'object') {
    item = EJSON.clone(item);
    adjustTypesToJSONValue(item);
  }
  return item;
};

//for both arrays and objects
var adjustTypesFromJSONValue =
EJSON._adjustTypesFromJSONValue = function (obj) {
  if (obj === null)
    return;
  _.each(obj, function (value, key) {
    if (typeof value === 'object') {
      var changed = fromJSONValueHelper(value);
      if (value !== changed) {
        obj[key] = changed;
        return;
      }
      // if we get here, value is an object but not adjustable
      // at this level.  recurse.
      adjustTypesFromJSONValue(value);
    }
  });
};

// Either return the argument changed to have the non-json
// rep of itself (the Object version) or the argument itself.

// DOES NOT RECURSE.  For actually getting the fully-changed value, use
// EJSON.fromJSONValue
var fromJSONValueHelper = function (value) {
  if (typeof value === 'object' && value !== null) {
    if (_.size(value) <= 2
        && _.all(value, function (v, k) {
          return typeof k === 'string' && k.substr(0, 1) === '$';
        })) {
      for (var i = 0; i < builtinConverters.length; i++) {
        var converter = builtinConverters[i];
        if (converter.matchJSONValue(value)) {
          return converter.fromJSONValue(value);
        }
      }
    }
  }
  return value;
};

EJSON.fromJSONValue = function (item) {
  var changed = fromJSONValueHelper(item);
  if (changed === item && typeof item === 'object') {
    item = EJSON.clone(item);
    adjustTypesFromJSONValue(item);
    return item;
  } else {
    return changed;
  }
};

EJSON.stringify = function (item) {
  return JSON.stringify(EJSON.toJSONValue(item));
};

EJSON.parse = function (item) {
  return EJSON.fromJSONValue(JSON.parse(item));
};

EJSON.isBinary = function (obj) {
  return obj instanceof Uint8Array || obj.$Uint8ArrayPolyfill;
};

EJSON.equals = function (a, b, options) {
  var i;
  var keyOrderSensitive = !!(options && options.keyOrderSensitive);
  if (a === b)
    return true;
  if (!(typeof a === 'object' && typeof b === 'object'))
    return false;
  if (a instanceof Date && b instanceof Date)
    return a.valueOf() === b.valueOf();
  if (EJSON.isBinary(a) && EJSON.isBinary(b)) {
    if (a.length !== b.length)
      return false;
    for (i = 0; i < a.length; i++) {
      if (a[i] !== b[i])
        return false;
    }
    return true;
  }
  if (typeof (a.equals) === 'function')
    return a.equals(b, options);
  // fall back to structural equality.
  if (a instanceof Array) {
    if (!(b instanceof Array))
      return false;
    if (a.length !== b.length)
      return false;
    for (i = 0; i < a.length; i++) {
      if (!EJSON.equals(a[i], b[i], options))
        return false;
    }
    return true;
  }
  if (keyOrderSensitive) {
      var b_keys = [];
      for (var x in b)
        b_keys.push(x);
      i = 0;
      for (var x in a) {
        if (i >= b_keys.length)
          return false;
        if (x !== b_keys[i])
          return false;
        if (!EJSON.equals(a[x], b[b_keys[i]], options))
          return false;
        i++;
      }
      if (i !== b_keys.length)
        return false;
      return true;
  } else {
    return _.isEqual(a, b);
  }
};

EJSON.clone = function (v) {
  var ret;
  if (typeof v !== "object")
    return v;
  if (v === null)
    return null; // null has typeof "object"
  if (v instanceof Date)
    return new Date(v.getTime());
  if (EJSON.isBinary(v)) {
    ret = EJSON.newBinary(v.length);
    for (i = 0; i < v.length; i++) {
      ret[i] = v[i];
    }
    return ret;
  }
  if (_.isArray(v)) {
    ret = v.slice(0);
    for (var i = 0; i < v.length; i++)
      ret[i] = EJSON.clone(ret[i]);
    return ret;
  }
  // handle general user-defined typed Objects if they have a clone method
  if (typeof v.clone === 'function') {
    return v.clone();
  }
  // handle other objects
  ret = {};
  for (var key in v)
    ret[key] = EJSON.clone(v[key]);
  return ret;
};

EJSON._each2 = function (iterable, iterator) {
  var lastVal = null;
  var lastKey = null;
  _.each(iterable, function (nextVal, nextKey) {
    iterator(lastVal, lastKey, nextVal, nextKey);
    lastVal = nextVal;
    lastKey = nextKey;
  });
  iterator(lastVal, lastKey, null, null);
};
})();
<commit_msg>Remove undefined from EJSON<commit_after>(function () {

EJSON = {}; // Global!
var customTypes = {};
// Add a custom type, using a method of your choice to get to and
// from a basic JSON-able representation.  The factory argument
// is a function of JSON-able --> your object
// The type you add must have:
// - A clone() method, so that Meteor can deep-copy it when necessary.
// - A equals() method, so that Meteor can compare it
// - A toJSONValue() method, so that Meteor can serialize it
// - a typeName() method, to show how to look it up in our type table.
// It is okay if these methods are monkey-patched on.
EJSON.addType = function (name, factory) {
  if (_.has(customTypes, name))
    throw new Error("Type " + name + " already present");
  customTypes[name] = factory;
};

var builtinConverters = [
  { // Date
    matchJSONValue: function (obj) {
      return _.has(obj, '$date') && _.size(obj) === 1;
    },
    matchObject: function (obj) {
      return obj instanceof Date;
    },
    toJSONValue: function (obj) {
      return {$date: obj.getTime()};
    },
    fromJSONValue: function (obj) {
      return new Date(obj.$date);
    }
  },
  { // Binary
    matchJSONValue: function (obj) {
      return _.has(obj, '$binary') && _.size(obj) === 1;
    },
    matchObject: function (obj) {
      return typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array
        || (obj && _.has(obj, '$Uint8ArrayPolyfill'));
    },
    toJSONValue: function (obj) {
      return {$binary: EJSON._base64Encode(obj)};
    },
    fromJSONValue: function (obj) {
      return EJSON._base64Decode(obj.$binary);
    }
  },
  { // Escaping one level
    matchJSONValue: function (obj) {
      return _.has(obj, '$escape') && _.size(obj) === 1;
    },
    matchObject: function (obj) {
      if (_.isEmpty(obj) || _.size(obj) > 2) {
        return false;
      }
      return _.any(builtinConverters, function (converter) {
        return converter.matchJSONValue(obj);
      });
    },
    toJSONValue: function (obj) {
      var newObj = {};
      _.each(obj, function (value, key) {
        newObj[key] = EJSON.toJSONValue(value);
      });
      return {$escape: newObj};
    },
    fromJSONValue: function (obj) {
      var newObj = {};
      _.each(obj.$escape, function (value, key) {
        newObj[key] = EJSON.fromJSONValue(value);
      });
      return newObj;
    }
  },
  { // Custom
    matchJSONValue: function (obj) {
      return _.has(obj, '$type') && _.has(obj, '$value') && _.size(obj) === 2;
    },
    matchObject: function (obj) {
      return obj &&
        typeof obj.toJSONValue === 'function' &&
        typeof obj.typeName === 'function' &&
        _.has(customTypes, obj.typeName());
    },
    toJSONValue: function (obj) {
      return {$type: obj.typeName(), $value: obj.toJSONValue()};
    },
    fromJSONValue: function (obj) {
      var typeName = obj.$type;
      var converter = customTypes[typeName];
      return converter(obj.$value);
    }
  }
];

//for both arrays and objects, in-place modification.
var adjustTypesToJSONValue =
EJSON._adjustTypesToJSONValue = function (obj) {
  if (obj === null)
    return;
  _.each(obj, function (value, key) {
    if (typeof value !== 'object' && value !== undefined)
      return; // continue
    var changed = toJSONValueHelper(value);
    if (changed) {
      obj[key] = changed;
      return; // on to the next key
    }
    // if we get here, value is an object but not adjustable
    // at this level.  recurse.
    adjustTypesToJSONValue(value);
  });
};

// Either return the JSON-compatible version of the argument, or undefined (if
// the item isn't itself replaceable, but maybe some fields in it are)
var toJSONValueHelper = function (item) {
  for (var i = 0; i < builtinConverters.length; i++) {
    var converter = builtinConverters[i];
    if (converter.matchObject(item)) {
      return converter.toJSONValue(item);
    }
  }
  return undefined;
};

EJSON.toJSONValue = function (item) {
  var changed = toJSONValueHelper(item);
  if (changed !== undefined)
    return changed;
  if (typeof item === 'object') {
    item = EJSON.clone(item);
    adjustTypesToJSONValue(item);
  }
  return item;
};

//for both arrays and objects
var adjustTypesFromJSONValue =
EJSON._adjustTypesFromJSONValue = function (obj) {
  if (obj === null)
    return;
  _.each(obj, function (value, key) {
    if (typeof value === 'object') {
      var changed = fromJSONValueHelper(value);
      if (value !== changed) {
        obj[key] = changed;
        return;
      }
      // if we get here, value is an object but not adjustable
      // at this level.  recurse.
      adjustTypesFromJSONValue(value);
    }
  });
};

// Either return the argument changed to have the non-json
// rep of itself (the Object version) or the argument itself.

// DOES NOT RECURSE.  For actually getting the fully-changed value, use
// EJSON.fromJSONValue
var fromJSONValueHelper = function (value) {
  if (typeof value === 'object' && value !== null) {
    if (_.size(value) <= 2
        && _.all(value, function (v, k) {
          return typeof k === 'string' && k.substr(0, 1) === '$';
        })) {
      for (var i = 0; i < builtinConverters.length; i++) {
        var converter = builtinConverters[i];
        if (converter.matchJSONValue(value)) {
          return converter.fromJSONValue(value);
        }
      }
    }
  }
  return value;
};

EJSON.fromJSONValue = function (item) {
  var changed = fromJSONValueHelper(item);
  if (changed === item && typeof item === 'object') {
    item = EJSON.clone(item);
    adjustTypesFromJSONValue(item);
    return item;
  } else {
    return changed;
  }
};

EJSON.stringify = function (item) {
  return JSON.stringify(EJSON.toJSONValue(item));
};

EJSON.parse = function (item) {
  return EJSON.fromJSONValue(JSON.parse(item));
};

EJSON.isBinary = function (obj) {
  return obj instanceof Uint8Array || obj.$Uint8ArrayPolyfill;
};

EJSON.equals = function (a, b, options) {
  var i;
  var keyOrderSensitive = !!(options && options.keyOrderSensitive);
  if (a === b)
    return true;
  if (!(typeof a === 'object' && typeof b === 'object'))
    return false;
  if (a instanceof Date && b instanceof Date)
    return a.valueOf() === b.valueOf();
  if (EJSON.isBinary(a) && EJSON.isBinary(b)) {
    if (a.length !== b.length)
      return false;
    for (i = 0; i < a.length; i++) {
      if (a[i] !== b[i])
        return false;
    }
    return true;
  }
  if (typeof (a.equals) === 'function')
    return a.equals(b, options);
  // fall back to structural equality.
  if (a instanceof Array) {
    if (!(b instanceof Array))
      return false;
    if (a.length !== b.length)
      return false;
    for (i = 0; i < a.length; i++) {
      if (!EJSON.equals(a[i], b[i], options))
        return false;
    }
    return true;
  }
  if (keyOrderSensitive) {
      var b_keys = [];
      for (var x in b)
        b_keys.push(x);
      i = 0;
      for (var x in a) {
        if (i >= b_keys.length)
          return false;
        if (x !== b_keys[i])
          return false;
        if (!EJSON.equals(a[x], b[b_keys[i]], options))
          return false;
        i++;
      }
      if (i !== b_keys.length)
        return false;
      return true;
  } else {
    return _.isEqual(a, b);
  }
};

EJSON.clone = function (v) {
  var ret;
  if (typeof v !== "object")
    return v;
  if (v === null)
    return null; // null has typeof "object"
  if (v instanceof Date)
    return new Date(v.getTime());
  if (EJSON.isBinary(v)) {
    ret = EJSON.newBinary(v.length);
    for (i = 0; i < v.length; i++) {
      ret[i] = v[i];
    }
    return ret;
  }
  if (_.isArray(v)) {
    ret = v.slice(0);
    for (var i = 0; i < v.length; i++)
      ret[i] = EJSON.clone(ret[i]);
    return ret;
  }
  // handle general user-defined typed Objects if they have a clone method
  if (typeof v.clone === 'function') {
    return v.clone();
  }
  // handle other objects
  ret = {};
  for (var key in v)
    ret[key] = EJSON.clone(v[key]);
  return ret;
};

EJSON._each2 = function (iterable, iterator) {
  var lastVal = null;
  var lastKey = null;
  _.each(iterable, function (nextVal, nextKey) {
    iterator(lastVal, lastKey, nextVal, nextKey);
    lastVal = nextVal;
    lastKey = nextKey;
  });
  iterator(lastVal, lastKey, null, null);
};
})();
<|endoftext|>


<commit_before>// SensorTag object
var sensortag;

// Various variables
var tossMode = true;

var pressed = false;    // Button press indicator (default false)
var caught = true;      // Device caught indicator (default true)
var peaked = false;     // Apogee reached indicator (default false)

var prevAccel;          // Previous z-axis acceleration measurement

var acc_rate = 100; 	// Accelerometer sample rate in ms (1-1000)
var throw_level = 8;    // Lower bound for velocity change indicating a throw (large)
var catch_level = 2;    // Lower bound for velocity change indicating a catch (medium)
var peak_level = 0.05;  // Upper bound for velocity change indicating apogee reached (small)

var spinAcc = [0];      // An array containing each instance of acceleration data
var prevVel = 0;        // The previously logged velocity

var startTime = 0;      // Start time variable
var peakTime = 0;       // Apogee time variable
var caughtTime = 0;     // Catch time variable

// Score limits
var maxThrow = 1895;    // The highest throw
var minThrow = 184;     // The lowest throw

var maxSpin = 281.0;    // The highest spin
var minSpin = 0.4;      // the lowest spin

// SensorTag characteristic id
var characteristicId = '00002a24-0000-1000-8000-00805f9b34fb';

// SensorTag connection variables
var attemptToConnect = false;   // Whether or not the user is currently attempting to connect
var connected = false;          // Whether or not a SensorTag is currently connected
var connecting = false;         // Whether or not the app is actively trying to connect
var readTimeout = null;         // Timeout for reading characteristics

function getTossMode() {
    return tossMode;
}

function setTossMode(value) {
    tossMode = value;
}

function getPressed() {
    return pressed;
}

function setPressed(value) {
    pressed = value;
}

function initializeSensorTag() {
    sensortag = evothings.tisensortag.createInstance(evothings.tisensortag.CC2650_BLUETOOTH_SMART);

    sensortag.statusCallback(statusHandler);
    sensortag.errorCallback(errorHandler);
    sensortag.accelerometerCallback(accelerometerHandler, acc_rate);
    sensortag.gyroscopeCallback(gyroscopeHandler, acc_rate);
    sensortag.keypressCallback(function(){}, 1000);
    sensortag.temperatureCallback(function(){}, 1000);
    sensortag.barometerCallback(function(){}, 1000);
    sensortag.magnetometerCallback(function(){}, 1000);
    sensortag.humidityCallback(function(){}, 1000);
    sensortag.luxometerCallback(function(){}, 1000);
}

function checkConnection(attemptToConnect) {
    if (sensortag && sensortag.device) {
        readTimeout = setTimeout(
            function() {
                connected = false;
            },
            400
        );

        sensortag.device.readCharacteristic(
            characteristicId,
            function() {
                if (readTimeout != null) {
                    clearTimeout(readTimeout);
                }
                connected = true;
            },
            function() {
                connected = false;
            }
        );
    } else {
        connected = false;
    }

    if (attemptToConnect) {
        if (connected) {
            // set bluetooth-connected.png
            console.log('checkConnection(): bluetooth-connected');
        } else {
            // set bluetooth-connecting.png
            console.log('checkConnection(): bluetooth-connecting');
            if (!connecting)
            {
                connecting = true;
                setTimeout(
                    function() {
                        connecting = false
                    },
                    5000
                );
                if (sensortag != null) {
                    sensortag.connectToNearestDevice();
                }
            }
        }
    } else {
        // set bluetooth-off.png
        console.log('checkConnection(): bluetooth-off');
        if (sensortag != null) {
            sensortag.disconnectDevice();
        }
    }
}

function statusHandler(status)
{
    console.log(status);

    if ('CONNECTED' == status)
    {
        if (readTimeout != null) {
            clearTimeout(readTimeout);
        }
        connected = true;
    }
    if ('EASYBLE_ERROR_DISCONNECTED' == status)
    {
        connected = false;
    }
}

function errorHandler(error) {
    console.log('Error: ' + error);
}

// Capture the acceleration data
function accelerometerHandler(data) {
    if (tossMode) {
        var values = sensortag.getAccelerometerValues(data);
        var x = values.x;
        var y = values.y;
        var z = values.z;

        var total = z;

        if (pressed) {
            // Calculate and store change in velocity
            var velocityChange = Math.abs(total-prevAccel)/(acc_rate/1000);

            // When thrown
            if (caught && (velocityChange >= throw_level)) {
                startTime = (new Date()).getTime();
                caught = false;
                peaked = false;
                displayValue("scoreField", "Thrown");

                // When apogee reached
            } else if (!caught && !peaked && (velocityChange <= peak_level)) {
                peakTime = (new Date()).getTime() - startTime;
                peaked = true;
                displayValue("scoreField", "Peaked");

                // When caught (and apogee detected)
            } else if (!caught && peaked && (velocityChange >= catch_level)) {
                caughtTime = (new Date()).getTime() - startTime;
                caught = true;
                displayValue("scoreField", "Caught");
                stopButtonPress(); // Don't wait for a button press
            }
        }

        // Update last accelerometer value
        prevAccel = total;
    }
}

function gyroscopeHandler(data) {
    if (!tossMode) {
        // Calculate the gyroscope values from raw sensor data.
        var values = sensortag.getGyroscopeValues(data);
        var x = values.x;
        var y = values.y;
        var z = values.z;

        if (pressed) {
            prevVel = prevVel + acc_rate*z/1000;
            spinAcc.push(prevVel);
        }
    }
}

// Ends tracking, displays values & resets data
function stopTracking() {
    if (tossMode) {
        var time = (caughtTime - peakTime);

        var heightScore = calculateScore(time, maxThrow, minThrow);
        displayValue('scoreField', "" + heightScore);

        caught = true;
        peaked = false;
        startTime = 0;
        peakTime = 0;
        caughtTime = 0;
    } else {
        // Calculate score of spin data
        var spinScore = calculateScore(median(spinAcc), maxSpin, minSpin);
        displayValue('scoreField', Math.abs(spinScore));
        spinAcc = [];
        HighSpin = 0;
        prevVel = 0;
    }
}

// Takes a variable representing time of throw in ms, returns a score from 0-10 which ranks the throw relative to the maximum and minimum throws possible
function calculateScore(time, max, min) {
    var score = Math.round((time/(max-min))*10);

    return (score > 10) ? 10 : score;
}

function median(values) {
    values.sort( function(a,b) {return a - b;} );

    var half = Math.floor(values.length/2);

    if (values.length % 2) {
        return values[half];
    } else {
        return (values[half-1] + values[half]) / 2.0;
    }
}

document.addEventListener(
    'deviceready',
    function() {
        evothings.scriptsLoaded(initializeSensorTag);
    },
    false
);

setInterval(
    function() {
        checkConnection(document.getElementById('connectCheckbox').checked);
    },
    500
);
<commit_msg>Throw Detection Bugfix<commit_after>// SensorTag object
var sensortag;

// Various variables
var tossMode = true;

var pressed = false;    // Button press indicator (default false)
var caught = true;      // Device caught indicator (default true)
var peaked = false;     // Apogee reached indicator (default false)

var prevAccel;          // Previous z-axis acceleration measurement

var acc_rate = 100; 	// Accelerometer sample rate in ms (1-1000)
var throw_level = 8;    // Lower bound for velocity change indicating a throw (large)
var catch_level = 2;    // Lower bound for velocity change indicating a catch (medium)
var peak_level = 0.05;  // Upper bound for velocity change indicating apogee reached (small)

var spinAcc = [0];      // An array containing each instance of acceleration data
var prevVel = 0;        // The previously logged velocity

var startTime = 0;      // Start time variable
var peakTime = 0;       // Apogee time variable
var caughtTime = 0;     // Catch time variable

// Score limits
var maxThrow = 1895;    // The highest throw
var minThrow = 184;     // The lowest throw

var maxSpin = 281.0;    // The highest spin
var minSpin = 0.4;      // the lowest spin

// SensorTag characteristic id
var characteristicId = '00002a24-0000-1000-8000-00805f9b34fb';

// SensorTag connection variables
var attemptToConnect = false;   // Whether or not the user is currently attempting to connect
var connected = false;          // Whether or not a SensorTag is currently connected
var connecting = false;         // Whether or not the app is actively trying to connect
var readTimeout = null;         // Timeout for reading characteristics

function getTossMode() {
    return tossMode;
}

function setTossMode(value) {
    tossMode = value;
}

function getPressed() {
    return pressed;
}

function setPressed(value) {
    pressed = value;
}

function initializeSensorTag() {
    sensortag = evothings.tisensortag.createInstance(evothings.tisensortag.CC2650_BLUETOOTH_SMART);

    sensortag.statusCallback(statusHandler);
    sensortag.errorCallback(errorHandler);
    sensortag.accelerometerCallback(accelerometerHandler, acc_rate);
    sensortag.gyroscopeCallback(gyroscopeHandler, acc_rate);
    sensortag.keypressCallback(function(){}, 1000);
    sensortag.temperatureCallback(function(){}, 1000);
    sensortag.barometerCallback(function(){}, 1000);
    sensortag.magnetometerCallback(function(){}, 1000);
    sensortag.humidityCallback(function(){}, 1000);
    sensortag.luxometerCallback(function(){}, 1000);
}

function checkConnection(attemptToConnect) {
    if (sensortag && sensortag.device) {
        readTimeout = setTimeout(
            function() {
                connected = false;
            },
            400
        );

        sensortag.device.readCharacteristic(
            characteristicId,
            function() {
                if (readTimeout != null) {
                    clearTimeout(readTimeout);
                }
                connected = true;
            },
            function() {
                connected = false;
            }
        );
    } else {
        connected = false;
    }

    if (attemptToConnect) {
        if (connected) {
            // set bluetooth-connected.png
            console.log('checkConnection(): bluetooth-connected');
        } else {
            // set bluetooth-connecting.png
            console.log('checkConnection(): bluetooth-connecting');
            if (!connecting)
            {
                connecting = true;
                setTimeout(
                    function() {
                        connecting = false
                    },
                    5000
                );
                if (sensortag != null) {
                    sensortag.connectToNearestDevice();
                }
            }
        }
    } else {
        // set bluetooth-off.png
        console.log('checkConnection(): bluetooth-off');
        if (sensortag != null) {
            sensortag.disconnectDevice();
        }
    }
}

function statusHandler(status)
{
    console.log(status);

    if ('CONNECTED' == status)
    {
        if (readTimeout != null) {
            clearTimeout(readTimeout);
        }
        connected = true;
    }
    if ('EASYBLE_ERROR_DISCONNECTED' == status)
    {
        connected = false;
    }
}

function errorHandler(error) {
    console.log('Error: ' + error);
}

// Capture the acceleration data
function accelerometerHandler(data) {
    if (tossMode) {
        var values = sensortag.getAccelerometerValues(data);
        var x = values.x;
        var y = values.y;
        var z = values.z;

        var total = Math.sqrt(Math.pow(x,2)+Math.pow(y,2)+Math.pow(z,2)); // Get the total magnitude of acceleration on the device.

        if (pressed) {
            // Calculate and store change in velocity
            var velocityChange = Math.abs(total-prevAccel)/(acc_rate/1000);

            // When thrown
            if (caught && (velocityChange >= throw_level)) {
                startTime = (new Date()).getTime();
                caught = false;
                peaked = false;
                displayValue("scoreField", "Thrown");

                // When apogee reached
            } else if (!caught && !peaked && (velocityChange <= peak_level)) {
                peakTime = (new Date()).getTime() - startTime;
                peaked = true;
                displayValue("scoreField", "Peaked");

                // When caught (and apogee detected)
            } else if (!caught && peaked && (velocityChange >= catch_level)) {
                caughtTime = (new Date()).getTime() - startTime;
                caught = true;
                displayValue("scoreField", "Caught");
                stopButtonPress(); // Don't wait for a button press
            }
        }

        // Update last accelerometer value
        prevAccel = total;
    }
}

function gyroscopeHandler(data) {
    if (!tossMode) {
        // Calculate the gyroscope values from raw sensor data.
        var values = sensortag.getGyroscopeValues(data);
        var x = values.x;
        var y = values.y;
        var z = values.z;

        if (pressed) {
            prevVel = prevVel + acc_rate*z/1000;
            spinAcc.push(prevVel);
        }
    }
}

// Ends tracking, displays values & resets data
function stopTracking() {
    if (tossMode) {
        var time = (caughtTime - peakTime);

        var heightScore = calculateScore(time, maxThrow, minThrow);
        displayValue('scoreField', "" + heightScore);

        caught = true;
        peaked = false;
        startTime = 0;
        peakTime = 0;
        caughtTime = 0;
    } else {
        // Calculate score of spin data
        var spinScore = calculateScore(median(spinAcc), maxSpin, minSpin);
        displayValue('scoreField', Math.abs(spinScore));
        spinAcc = [];
        HighSpin = 0;
        prevVel = 0;
    }
}

// Takes a variable representing time of throw in ms, returns a score from 0-10 which ranks the throw relative to the maximum and minimum throws possible
function calculateScore(time, max, min) {
    var score = Math.round((time/(max-min))*10);

    return (score > 10) ? 10 : score;
}

function median(values) {
    values.sort( function(a,b) {return a - b;} );

    var half = Math.floor(values.length/2);

    if (values.length % 2) {
        return values[half];
    } else {
        return (values[half-1] + values[half]) / 2.0;
    }
}

document.addEventListener(
    'deviceready',
    function() {
        evothings.scriptsLoaded(initializeSensorTag);
    },
    false
);

setInterval(
    function() {
        checkConnection(document.getElementById('connectCheckbox').checked);
    },
    500
);
<|endoftext|>