<commit_before>// Schedule Controller
//

coreModule.controller('scheduleCtrl', ['$scope', '$window', '$rootScope', 'scheduleFactory', function($scope, $window, $rootScope, scheduleFactory) {

    // Full Calendar Config ////////////////////////////
    ////////////////////////////////////////////////////
    //
    // Event array
    $scope.events = [ ];

    // EventSources array
    $scope.eventSources = [$scope.events];

    // General calendar config
    // Note: It is displaying the current week (with the day number hidden)
    $scope.uiConfig = {
        calendar: {
            height: 1000,
            //editable: true,
            header: false,

            // Format
            defaultView: 'agendaWeek',
            columnFormat: "dddd",
            hiddenDays: [0,6],
            minTime: '08:00:00',
            maxTime: '21:00:00',
            allDaySlot: false,

            // Event settings
            allDayDefault: false
            //timezoneParam: 'local'
        }
    };

    // Get Schedules /////////////////////////////////
    //////////////////////////////////////////////////
    //

    var scheduleInstance,
        scheduleLength,
        scheduleIndex = 0;

    // Event handle for gen-sched button
    //
    $scope.getSchedules = function () {
        scheduleFactory.getSchedules($rootScope.addedCourses).
            success(function (data) {
                disableGenerateSchedules();

                var scheduleListing = angular.fromJson(data);

                // Create closure with current scheduleListing
                scheduleInstance = renderSchedule(scheduleListing);

                // Invoke the closure
                scheduleInstance(0);
            }).
            error(function() {
                $window.alert("Schedules not found.");
            });
    };

    // Event handle for prev/next buttons
    //
    $scope.displayDifferentSchedule = function (forward) {

        // Adjust schedule index
        if (forward) {
            scheduleIndex = scheduleIndex + 1;
            if (scheduleIndex >= scheduleLength) {
                scheduleIndex = scheduleLength - 1;
            }
        }
        else {
            scheduleIndex = scheduleIndex - 1;
            if (scheduleIndex < 0) {
                scheduleIndex = 0;
            }
        }

        clearEvents();

        // Invoke the closure on the new index
        scheduleInstance(scheduleIndex);
    };

    // Event handle for clear button
    //
    $scope.clearAdded = function () {
        $rootScope.addedCourses = [];
        $rootScope.shoppingCartSize = 0;

        clearEvents();

        enableGenerateSchedules();
    };

    // @callee: $scope.getSchedules
    //
    // @returns: updated $scope.events
    function renderSchedule (scheduleListing) {

        scheduleLength = scheduleListing.objects.length;

        // Return closure of scheduleListing
        //
        // @param {int}: schedule index within the JSON response
        //
        // @return {void}: updates $scope.events
        return function (i) {

            scheduleListing.objects[i].sections.forEach(function (classtime) {

                // Null check
                //
                // TODO: Maybe want to flag the skipped class
                if (classtime.startTime === null ||
                    classtime.endTime === null   ||
                    classtime.day === null         ) {
                    return;
                }

                // Time //
                //
                //
                var startTimeString = classtime.startTime.match(/(\d+):(\d+)/),
                    endTimeString = classtime.endTime.match(/(\d+):(\d+)/);

                // Minute
                var startMinute = parseInt(startTimeString[2]),
                    endMinute = parseInt(endTimeString[2]);

                // Hour
                var startHour;
                if (classtime.startTime.match(/PM/) && startTimeString[1] != 12) {
                    // PM
                    startHour = parseInt(startTimeString[1]) + 12;
                }
                else {
                    // AM
                    startHour = parseInt(startTimeString[1]);
                }

                var endHour;
                if (classtime.endTime.match(/PM/) && endTimeString[1] != 12) {
                    // PM
                    endHour = parseInt(endTimeString[1]) + 12;
                }
                else {
                    // AM
                    endHour = parseInt(endTimeString[1]);
                }

                // Day //
                //
                //
                var date = new Date(),
                    d = date.getDate(),
                    m = date.getMonth(),
                    y = date.getFullYear();

                // JavaScript function Date.getDay()
                //
                // Sunday 0
                // Monday 1
                // Tuesday 2
                // Wednesday 3
                // Thursday 4
                // Friday 5
                // Saturday 6
                //
                // @return {int} enumeration of current day of the week
                var dayNumber = date.getDay(),
                    offset;

                // Use the current day {int:0:6} of the week
                // Enumerate each day of the week {int:0:6}
                // and find the offset {int:0:6}
                // Use this offset to find calendar day number  {int:0:31}
                if (classtime.day.match(/M/)) {
                    offset = 1 - dayNumber;
                }

                if (classtime.day.match(/T/)) {
                    offset = 2 - dayNumber;
                }

                if (classtime.day.match(/W/)) {
                    offset = 3 - dayNumber;
                }

                if (classtime.day.match(/R/)) {
                    offset = 4 - dayNumber;
                }

                if (classtime.day.match(/F/)) {
                    offset = 5 - dayNumber;
                }

                // Add event using parameters:
                // asString
                // Time
                // Day
                //
                // @returns {void} an event is added to $scope.events array
                function addEvent(offset) {
                    $scope.events.push({
                        title: classtime.asString,
                        start: new Date(y,m,d + offset, startHour, startMinute),
                        end: new Date(y,m,d + offset,endHour,endMinute)
                    });
                }

                // Add event //
                //
                $scope.events.push({
                    title: classtime.asString,
                    start: new Date(y,m,d + offset, startHour, startMinute),
                    end: new Date(y,m,d + offset,endHour,endMinute),
                    color: currentColor
                });
            });
        };
    }

    // Helper functions
    //
    function disableGenerateSchedules() {
        // Disable
        document.getElementById('get-classes').disabled = true;
    }

    function enableGenerateSchedules() {
        // Enable
        document.getElementById('get-classes').disabled = false;
    }

    function clearEvents() {
        // Clear current events
        while ($scope.events.length > 0) {
            $scope.events.pop();
        }
    }
}]);
<commit_msg>feat(schedule-rendering): LEC, LAB, SEM from same course are color coded<commit_after>// Schedule Controller
//

coreModule.controller('scheduleCtrl', ['$scope', '$window', '$rootScope', 'scheduleFactory', function($scope, $window, $rootScope, scheduleFactory) {

    // Full Calendar Config ////////////////////////////
    ////////////////////////////////////////////////////
    //
    // Event array
    $scope.events = [ ];

    // EventSources array
    $scope.eventSources = [$scope.events];

    // General calendar config
    // Note: It is displaying the current week (with the day number hidden)
    $scope.uiConfig = {
        calendar: {
            height: 1000,
            //editable: true,
            header: false,

            // Format
            defaultView: 'agendaWeek',
            columnFormat: "dddd",
            hiddenDays: [0,6],
            minTime: '08:00:00',
            maxTime: '21:00:00',
            allDaySlot: false,

            // Event settings
            allDayDefault: false
            //timezoneParam: 'local'
        }
    };

    // Get Schedules /////////////////////////////////
    //////////////////////////////////////////////////
    //

    var scheduleInstance,
        scheduleLength,
        scheduleIndex = 0;

    // Event handle for gen-sched button
    //
    $scope.getSchedules = function () {
        scheduleFactory.getSchedules($rootScope.addedCourses).
            success(function (data) {
                disableGenerateSchedules();

                var scheduleListing = angular.fromJson(data);

                // Create closure with current scheduleListing
                scheduleInstance = renderSchedule(scheduleListing);

                // Invoke the closure
                scheduleInstance(0);
            }).
            error(function() {
                $window.alert("Schedules not found.");
            });
    };

    // Event handle for prev/next buttons
    //
    $scope.displayDifferentSchedule = function (forward) {

        // Adjust schedule index
        if (forward) {
            scheduleIndex = scheduleIndex + 1;
            if (scheduleIndex >= scheduleLength) {
                scheduleIndex = scheduleLength - 1;
            }
        }
        else {
            scheduleIndex = scheduleIndex - 1;
            if (scheduleIndex < 0) {
                scheduleIndex = 0;
            }
        }

        clearEvents();

        // Invoke the closure on the new index
        scheduleInstance(scheduleIndex);
    };

    // Event handle for clear button
    //
    $scope.clearAdded = function () {
        $rootScope.addedCourses = [];
        $rootScope.shoppingCartSize = 0;

        clearEvents();

        enableGenerateSchedules();
    };

    // @callee: $scope.getSchedules
    //
    // @returns: updated $scope.events
    function renderSchedule (scheduleListing) {

        scheduleLength = scheduleListing.objects.length;

        // Return closure of scheduleListing
        //
        // @param {int}: schedule index within the JSON response
        //
        // @return {void}: updates $scope.events
        return function (i) {

            var cachedColors = [];
            var colorPallet = ['#443111', '#d0c8b3', '#af9b56', '#2a4560', '#83a283'];
            var colorPalletIndex = 0;

            scheduleListing.objects[i].sections.forEach(function (classtime) {

                // Null check
                //
                // TODO: Maybe want to flag the skipped class
                if (classtime.startTime === null ||
                    classtime.endTime === null   ||
                    classtime.day === null         ) {
                    return;
                }

                // Time //
                //
                //
                var startTimeString = classtime.startTime.match(/(\d+):(\d+)/),
                    endTimeString = classtime.endTime.match(/(\d+):(\d+)/);

                // Minute
                var startMinute = parseInt(startTimeString[2]),
                    endMinute = parseInt(endTimeString[2]);

                // Hour
                var startHour;
                if (classtime.startTime.match(/PM/) && startTimeString[1] != 12) {
                    // PM
                    startHour = parseInt(startTimeString[1]) + 12;
                }
                else {
                    // AM
                    startHour = parseInt(startTimeString[1]);
                }

                var endHour;
                if (classtime.endTime.match(/PM/) && endTimeString[1] != 12) {
                    // PM
                    endHour = parseInt(endTimeString[1]) + 12;
                }
                else {
                    // AM
                    endHour = parseInt(endTimeString[1]);
                }

                // Day //
                //
                //
                var date = new Date(),
                    d = date.getDate(),
                    m = date.getMonth(),
                    y = date.getFullYear();

                // JavaScript function Date.getDay()
                //
                // Sunday 0
                // Monday 1
                // Tuesday 2
                // Wednesday 3
                // Thursday 4
                // Friday 5
                // Saturday 6
                //
                // @return {int} enumeration of current day of the week
                var dayNumber = date.getDay(),
                    offset;

                // Use the current day {int:0:6} of the week
                // Enumerate each day of the week {int:0:6}
                // and find the offset {int:0:6}
                // Use this offset to find calendar day number  {int:0:31}
                if (classtime.day.match(/M/)) {
                    offset = 1 - dayNumber;
                }

                if (classtime.day.match(/T/)) {
                    offset = 2 - dayNumber;
                }

                if (classtime.day.match(/W/)) {
                    offset = 3 - dayNumber;
                }

                if (classtime.day.match(/R/)) {
                    offset = 4 - dayNumber;
                }

                if (classtime.day.match(/F/)) {
                    offset = 5 - dayNumber;
                }

                // Color //
                //
                var currentColor;
                var foundColor = false;

                cachedColors.forEach(function (cachedCourse) {
                    // Already in cached colors
                    if (cachedCourse.name === classtime.course) {
                        currentColor = cachedCourse.color;
                        foundColor = true;
                    }
                });

                // Not already in cache
                if (!foundColor) {
                    currentColor = colorPallet[colorPalletIndex];
                    cachedColors.push({name: classtime.course, color: currentColor});
                    colorPalletIndex = colorPalletIndex + 1;
                }

                // Add event //
                //
                $scope.events.push({
                    title: classtime.asString,
                    start: new Date(y,m,d + offset, startHour, startMinute),
                    end: new Date(y,m,d + offset,endHour,endMinute),
                    color: currentColor
                });
            });
        };
    }

    // Helper functions
    //
    function disableGenerateSchedules() {
        // Disable
        document.getElementById('get-classes').disabled = true;
    }

    function enableGenerateSchedules() {
        // Enable
        document.getElementById('get-classes').disabled = false;
    }

    function clearEvents() {
        // Clear current events
        while ($scope.events.length > 0) {
            $scope.events.pop();
        }
    }
}]);
<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { UserPresence } from 'meteor/konecty:user-presence';
import { InstanceStatus } from 'meteor/konecty:multiple-instances-status';
import { check } from 'meteor/check';
import _ from 'underscore';
import { DDP } from 'meteor/ddp';
import { DDPCommon } from 'meteor/ddp-common';

import { Logger, LoggerManager } from '../../app/logger';
import { hasPermission } from '../../app/authorization';
import { settings } from '../../app/settings';
import { isDocker, getURL } from '../../app/utils';
import { Users } from '../../app/models/server';

process.env.PORT = String(process.env.PORT).trim();
process.env.INSTANCE_IP = String(process.env.INSTANCE_IP).trim();

const startMonitor = typeof process.env.DISABLE_PRESENCE_MONITOR === 'undefined'
	|| !['true', 'yes'].includes(String(process.env.DISABLE_PRESENCE_MONITOR).toLowerCase());

const connections = {};
this.connections = connections;

const logger = new Logger('StreamBroadcast', {
	sections: {
		connection: 'Connection',
		auth: 'Auth',
		stream: 'Stream',
	},
});

function _authorizeConnection(instance) {
	logger.auth.info(`Authorizing with ${ instance }`);

	return connections[instance].call('broadcastAuth', InstanceStatus.id(), connections[instance].instanceId, function(err, ok) {
		if (err != null) {
			return logger.auth.error(`broadcastAuth error ${ instance } ${ InstanceStatus.id() } ${ connections[instance].instanceId }`, err);
		}

		connections[instance].broadcastAuth = ok;
		return logger.auth.info(`broadcastAuth with ${ instance }`, ok);
	});
}

function authorizeConnection(instance) {
	const query = {
		_id: InstanceStatus.id(),
	};

	if (!InstanceStatus.getCollection().findOne(query)) {
		return Meteor.setTimeout(function() {
			return authorizeConnection(instance);
		}, 500);
	}

	return _authorizeConnection(instance);
}

const originalSetDefaultStatus = UserPresence.setDefaultStatus;
function startMatrixBroadcast() {
	if (!startMonitor) {
		UserPresence.setDefaultStatus = originalSetDefaultStatus;
	}

	const query = {
		'extraInformation.port': {
			$exists: true,
		},
	};

	const options = {
		sort: {
			_createdAt: -1,
		},
	};

	return InstanceStatus.getCollection().find(query, options).observe({
		added(record) {
			const subPath = getURL('', { cdn: false, full: false });
			let instance = `${ record.extraInformation.host }:${ record.extraInformation.port }${ subPath }`;

			if (record.extraInformation.port === process.env.PORT && record.extraInformation.host === process.env.INSTANCE_IP) {
				logger.auth.info('prevent self connect', instance);
				return;
			}

			if (record.extraInformation.host === process.env.INSTANCE_IP && isDocker() === false) {
				instance = `localhost:${ record.extraInformation.port }${ subPath }`;
			}

			if (connections[instance] && connections[instance].instanceRecord) {
				if (connections[instance].instanceRecord._createdAt < record._createdAt) {
					connections[instance].disconnect();
					delete connections[instance];
				} else {
					return;
				}
			}

			logger.connection.info('connecting in', instance);

			connections[instance] = DDP.connect(instance, {
				_dontPrintErrors: LoggerManager.logLevel < 2,
			});

			connections[instance].instanceRecord = record;
			connections[instance].instanceId = record._id;

			connections[instance].onReconnect = function() {
				return authorizeConnection(instance);
			};
		},

		removed(record) {
			const subPath = getURL('', { cdn: false, full: false });
			let instance = `${ record.extraInformation.host }:${ record.extraInformation.port }${ subPath }`;

			if (record.extraInformation.host === process.env.INSTANCE_IP && isDocker() === false) {
				instance = `localhost:${ record.extraInformation.port }${ subPath }`;
			}

			const query = {
				'extraInformation.host': record.extraInformation.host,
				'extraInformation.port': record.extraInformation.port,
			};

			if (connections[instance] && !InstanceStatus.getCollection().findOne(query)) {
				logger.connection.info('disconnecting from', instance);
				connections[instance].disconnect();
				return delete connections[instance];
			}
		},
	});
}

Meteor.methods({
	broadcastAuth(remoteId, selfId) {
		check(selfId, String);
		check(remoteId, String);

		const query = {
			_id: remoteId,
		};

		if (selfId === InstanceStatus.id() && remoteId !== InstanceStatus.id() && InstanceStatus.getCollection().findOne(query)) {
			this.connection.broadcastAuth = true;
		}

		return this.connection.broadcastAuth === true;
	},

	stream(streamName, eventName, args) {
		if (!this.connection) {
			return 'self-not-authorized';
		}

		if (this.connection.broadcastAuth !== true) {
			return 'not-authorized';
		}

		const instance = Meteor.StreamerCentral.instances[streamName];
		if (!instance) {
			return 'stream-not-exists';
		}

		if (instance.serverOnly) {
			const scope = {};
			instance.emitWithScope(eventName, scope, ...args);
		} else {
			Meteor.StreamerCentral.instances[streamName]._emit(eventName, args);
		}
	},
});

function startStreamCastBroadcast(value) {
	const instance = 'StreamCast';

	logger.connection.info('connecting in', instance, value);

	if (!startMonitor) {
		UserPresence.setDefaultStatus = (id, status) => {
			Users.updateDefaultStatus(id, status);
		};
	}

	const connection = DDP.connect(value, {
		_dontPrintErrors: LoggerManager.logLevel < 2,
	});

	connections[instance] = connection;
	connection.instanceId = instance;
	connection.onReconnect = function() {
		return authorizeConnection(instance);
	};

	connection._stream.on('message', function(raw_msg) {
		const msg = DDPCommon.parseDDP(raw_msg);
		if (!msg || msg.msg !== 'changed' || !msg.collection || !msg.fields) {
			return;
		}

		const { streamName, eventName, args } = msg.fields;

		if (!streamName || !eventName || !args) {
			return;
		}

		if (connection.broadcastAuth !== true) {
			return 'not-authorized';
		}

		const instance = Meteor.StreamerCentral.instances[streamName];
		if (!instance) {
			return 'stream-not-exists';
		}

		if (instance.serverOnly) {
			const scope = {};
			return instance.emitWithScope(eventName, scope, args);
		}
		return instance._emit(eventName, args);
	});

	return connection.subscribe('stream');
}

function startStreamBroadcast() {
	if (!process.env.INSTANCE_IP) {
		process.env.INSTANCE_IP = 'localhost';
	}

	logger.info('startStreamBroadcast');

	settings.get('Stream_Cast_Address', function(key, value) {
		// var connection, fn, instance;
		const fn = function(instance, connection) {
			connection.disconnect();
			return delete connections[instance];
		};

		for (const instance of Object.keys(connections)) {
			const connection = connections[instance];
			fn(instance, connection);
		}

		if (value && value.trim() !== '') {
			return startStreamCastBroadcast(value);
		}
		return startMatrixBroadcast();
	});

	function broadcast(streamName, eventName, args/* , userId*/) {
		const fromInstance = `${ process.env.INSTANCE_IP }:${ process.env.PORT }`;
		const results = [];

		for (const instance of Object.keys(connections)) {
			const connection = connections[instance];

			if (connection.status().connected === true) {
				connection.call('stream', streamName, eventName, args, function(error, response) {
					if (error) {
						logger.error('Stream broadcast error', error);
					}

					switch (response) {
						case 'self-not-authorized':
							logger.stream.error(`Stream broadcast from '${ fromInstance }' to '${ connection._stream.endpoint }' with name ${ streamName } to self is not authorized`.red);
							logger.stream.debug('    -> connection authorized'.red, connection.broadcastAuth);
							logger.stream.debug('    -> connection status'.red, connection.status());
							return logger.stream.debug('    -> arguments'.red, eventName, args);
						case 'not-authorized':
							logger.stream.error(`Stream broadcast from '${ fromInstance }' to '${ connection._stream.endpoint }' with name ${ streamName } not authorized`.red);
							logger.stream.debug('    -> connection authorized'.red, connection.broadcastAuth);
							logger.stream.debug('    -> connection status'.red, connection.status());
							logger.stream.debug('    -> arguments'.red, eventName, args);
							return authorizeConnection(instance);
						case 'stream-not-exists':
							logger.stream.error(`Stream broadcast from '${ fromInstance }' to '${ connection._stream.endpoint }' with name ${ streamName } does not exist`.red);
							logger.stream.debug('    -> connection authorized'.red, connection.broadcastAuth);
							logger.stream.debug('    -> connection status'.red, connection.status());
							return logger.stream.debug('    -> arguments'.red, eventName, args);
					}
				});
			}
		}
		return results;
	}

	return Meteor.StreamerCentral.on('broadcast', function(streamName, eventName, args) {
		return broadcast(streamName, eventName, args);
	});
}

Meteor.startup(function() {
	return startStreamBroadcast();
});

Meteor.methods({
	'instances/get'() {
		if (!hasPermission(Meteor.userId(), 'view-statistics')) {
			throw new Meteor.Error('error-action-not-allowed', 'List instances is not allowed', {
				method: 'instances/get',
			});
		}

		return Object.keys(connections).map((address) => {
			const conn = connections[address];
			return Object.assign({ address, currentStatus: conn._stream.currentStatus }, _.pick(conn, 'instanceRecord', 'broadcastAuth'));
		});
	},
});
<commit_msg>Fix StreamCast info (#16995)<commit_after>import { Meteor } from 'meteor/meteor';
import { UserPresence } from 'meteor/konecty:user-presence';
import { InstanceStatus } from 'meteor/konecty:multiple-instances-status';
import { check } from 'meteor/check';
import _ from 'underscore';
import { DDP } from 'meteor/ddp';
import { DDPCommon } from 'meteor/ddp-common';

import { Logger, LoggerManager } from '../../app/logger';
import { hasPermission } from '../../app/authorization';
import { settings } from '../../app/settings';
import { isDocker, getURL } from '../../app/utils';
import { Users } from '../../app/models/server';

process.env.PORT = String(process.env.PORT).trim();
process.env.INSTANCE_IP = String(process.env.INSTANCE_IP).trim();

const startMonitor = typeof process.env.DISABLE_PRESENCE_MONITOR === 'undefined'
	|| !['true', 'yes'].includes(String(process.env.DISABLE_PRESENCE_MONITOR).toLowerCase());

const connections = {};
this.connections = connections;

const logger = new Logger('StreamBroadcast', {
	sections: {
		connection: 'Connection',
		auth: 'Auth',
		stream: 'Stream',
	},
});

function _authorizeConnection(instance) {
	logger.auth.info(`Authorizing with ${ instance }`);

	return connections[instance].call('broadcastAuth', InstanceStatus.id(), connections[instance].instanceId, function(err, ok) {
		if (err != null) {
			return logger.auth.error(`broadcastAuth error ${ instance } ${ InstanceStatus.id() } ${ connections[instance].instanceId }`, err);
		}

		connections[instance].broadcastAuth = ok;
		return logger.auth.info(`broadcastAuth with ${ instance }`, ok);
	});
}

function authorizeConnection(instance) {
	const query = {
		_id: InstanceStatus.id(),
	};

	if (!InstanceStatus.getCollection().findOne(query)) {
		return Meteor.setTimeout(function() {
			return authorizeConnection(instance);
		}, 500);
	}

	return _authorizeConnection(instance);
}

const originalSetDefaultStatus = UserPresence.setDefaultStatus;
function startMatrixBroadcast() {
	if (!startMonitor) {
		UserPresence.setDefaultStatus = originalSetDefaultStatus;
	}

	const query = {
		'extraInformation.port': {
			$exists: true,
		},
	};

	const options = {
		sort: {
			_createdAt: -1,
		},
	};

	return InstanceStatus.getCollection().find(query, options).observe({
		added(record) {
			const subPath = getURL('', { cdn: false, full: false });
			let instance = `${ record.extraInformation.host }:${ record.extraInformation.port }${ subPath }`;

			if (record.extraInformation.port === process.env.PORT && record.extraInformation.host === process.env.INSTANCE_IP) {
				logger.auth.info('prevent self connect', instance);
				return;
			}

			if (record.extraInformation.host === process.env.INSTANCE_IP && isDocker() === false) {
				instance = `localhost:${ record.extraInformation.port }${ subPath }`;
			}

			if (connections[instance] && connections[instance].instanceRecord) {
				if (connections[instance].instanceRecord._createdAt < record._createdAt) {
					connections[instance].disconnect();
					delete connections[instance];
				} else {
					return;
				}
			}

			logger.connection.info('connecting in', instance);

			connections[instance] = DDP.connect(instance, {
				_dontPrintErrors: LoggerManager.logLevel < 2,
			});

			connections[instance].instanceRecord = record;
			connections[instance].instanceId = record._id;

			connections[instance].onReconnect = function() {
				return authorizeConnection(instance);
			};
		},

		removed(record) {
			const subPath = getURL('', { cdn: false, full: false });
			let instance = `${ record.extraInformation.host }:${ record.extraInformation.port }${ subPath }`;

			if (record.extraInformation.host === process.env.INSTANCE_IP && isDocker() === false) {
				instance = `localhost:${ record.extraInformation.port }${ subPath }`;
			}

			const query = {
				'extraInformation.host': record.extraInformation.host,
				'extraInformation.port': record.extraInformation.port,
			};

			if (connections[instance] && !InstanceStatus.getCollection().findOne(query)) {
				logger.connection.info('disconnecting from', instance);
				connections[instance].disconnect();
				return delete connections[instance];
			}
		},
	});
}

Meteor.methods({
	broadcastAuth(remoteId, selfId) {
		check(selfId, String);
		check(remoteId, String);

		const query = {
			_id: remoteId,
		};

		if (selfId === InstanceStatus.id() && remoteId !== InstanceStatus.id() && InstanceStatus.getCollection().findOne(query)) {
			this.connection.broadcastAuth = true;
		}

		return this.connection.broadcastAuth === true;
	},

	stream(streamName, eventName, args) {
		if (!this.connection) {
			return 'self-not-authorized';
		}

		if (this.connection.broadcastAuth !== true) {
			return 'not-authorized';
		}

		const instance = Meteor.StreamerCentral.instances[streamName];
		if (!instance) {
			return 'stream-not-exists';
		}

		if (instance.serverOnly) {
			const scope = {};
			instance.emitWithScope(eventName, scope, ...args);
		} else {
			Meteor.StreamerCentral.instances[streamName]._emit(eventName, args);
		}
	},
});

function startStreamCastBroadcast(value) {
	const instance = 'StreamCast';

	logger.connection.info('connecting in', instance, value);

	if (!startMonitor) {
		UserPresence.setDefaultStatus = (id, status) => {
			Users.updateDefaultStatus(id, status);
		};
	}

	const connection = DDP.connect(value, {
		_dontPrintErrors: LoggerManager.logLevel < 2,
	});

	connections[instance] = connection;
	connection.instanceId = instance;
	connection.instanceRecord = {};
	connection.onReconnect = function() {
		return authorizeConnection(instance);
	};

	connection._stream.on('message', function(raw_msg) {
		const msg = DDPCommon.parseDDP(raw_msg);
		if (!msg || msg.msg !== 'changed' || !msg.collection || !msg.fields) {
			return;
		}

		const { streamName, eventName, args } = msg.fields;

		if (!streamName || !eventName || !args) {
			return;
		}

		if (connection.broadcastAuth !== true) {
			return 'not-authorized';
		}

		const instance = Meteor.StreamerCentral.instances[streamName];
		if (!instance) {
			return 'stream-not-exists';
		}

		if (instance.serverOnly) {
			const scope = {};
			return instance.emitWithScope(eventName, scope, args);
		}
		return instance._emit(eventName, args);
	});

	return connection.subscribe('stream');
}

function startStreamBroadcast() {
	if (!process.env.INSTANCE_IP) {
		process.env.INSTANCE_IP = 'localhost';
	}

	logger.info('startStreamBroadcast');

	settings.get('Stream_Cast_Address', function(key, value) {
		// var connection, fn, instance;
		const fn = function(instance, connection) {
			connection.disconnect();
			return delete connections[instance];
		};

		for (const instance of Object.keys(connections)) {
			const connection = connections[instance];
			fn(instance, connection);
		}

		if (value && value.trim() !== '') {
			return startStreamCastBroadcast(value);
		}
		return startMatrixBroadcast();
	});

	function broadcast(streamName, eventName, args/* , userId*/) {
		const fromInstance = `${ process.env.INSTANCE_IP }:${ process.env.PORT }`;
		const results = [];

		for (const instance of Object.keys(connections)) {
			const connection = connections[instance];

			if (connection.status().connected === true) {
				connection.call('stream', streamName, eventName, args, function(error, response) {
					if (error) {
						logger.error('Stream broadcast error', error);
					}

					switch (response) {
						case 'self-not-authorized':
							logger.stream.error(`Stream broadcast from '${ fromInstance }' to '${ connection._stream.endpoint }' with name ${ streamName } to self is not authorized`.red);
							logger.stream.debug('    -> connection authorized'.red, connection.broadcastAuth);
							logger.stream.debug('    -> connection status'.red, connection.status());
							return logger.stream.debug('    -> arguments'.red, eventName, args);
						case 'not-authorized':
							logger.stream.error(`Stream broadcast from '${ fromInstance }' to '${ connection._stream.endpoint }' with name ${ streamName } not authorized`.red);
							logger.stream.debug('    -> connection authorized'.red, connection.broadcastAuth);
							logger.stream.debug('    -> connection status'.red, connection.status());
							logger.stream.debug('    -> arguments'.red, eventName, args);
							return authorizeConnection(instance);
						case 'stream-not-exists':
							logger.stream.error(`Stream broadcast from '${ fromInstance }' to '${ connection._stream.endpoint }' with name ${ streamName } does not exist`.red);
							logger.stream.debug('    -> connection authorized'.red, connection.broadcastAuth);
							logger.stream.debug('    -> connection status'.red, connection.status());
							return logger.stream.debug('    -> arguments'.red, eventName, args);
					}
				});
			}
		}
		return results;
	}

	return Meteor.StreamerCentral.on('broadcast', function(streamName, eventName, args) {
		return broadcast(streamName, eventName, args);
	});
}

Meteor.startup(function() {
	return startStreamBroadcast();
});

Meteor.methods({
	'instances/get'() {
		if (!hasPermission(Meteor.userId(), 'view-statistics')) {
			throw new Meteor.Error('error-action-not-allowed', 'List instances is not allowed', {
				method: 'instances/get',
			});
		}

		return Object.keys(connections).map((address) => {
			const conn = connections[address];
			return Object.assign({ address, currentStatus: conn._stream.currentStatus }, _.pick(conn, 'instanceRecord', 'broadcastAuth'));
		});
	},
});
<|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Shows the about dialog.
 *
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  var VBox = require( 'SCENERY/nodes/VBox' );
  var Text = require( 'SCENERY/nodes/Text' );
  var inherit = require( 'PHET_CORE/inherit' );
  var ScreenView = require( 'JOIST/ScreenView' );
  var Panel = require( 'SUN/Panel' );
  var CheckBox = require( 'SUN/CheckBox' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var TextPushButton = require( 'SUN/TextPushButton' );

  // strings
  var showPointersString = require( 'string!JOIST/showPointers' );
  var doneString = require( 'string!JOIST/done' );
  var settingsString = require( 'string!JOIST/title.settings' );

  /**
   * @param {Sim} sim
   * @constructor
   */
  function SettingsDialog( sim ) {
    var settingsDialog = this;

    //Use view, to help center and scale content
    ScreenView.call( this, {renderer: 'svg'} );

    var content = new VBox( { align: 'center', spacing: 50, children: [
      new Text( settingsString, { font: new PhetFont( 16 ) } ),
      new CheckBox( new Text( showPointersString ), sim.showPointersProperty ),
      new TextPushButton( doneString, {
        listener: function() {
            settingsDialog.doneListeners.forEach( function( listener ) {
              listener();
            } );
          }
        } )
    ]} );

    //Show a gray overlay that will help focus on the about dialog, and prevent clicks on the sim while the dialog is up
    this.addChild( new Panel( content, {centerX: this.layoutBounds.centerX, centerY: this.layoutBounds.centerY, xMargin: 20, yMargin: 20 } ) );

    function resize() {
      settingsDialog.layout( $( window ).width(), $( window ).height() );
    }

    //Fit to the window and render the initial scene
    $( window ).resize( resize );
    resize();
    this.doneListeners = [];
  }

  return inherit( ScreenView, SettingsDialog, {
    addDoneListener: function( listener ) {
      this.doneListeners.push( listener );
    }
  } );
} );
<commit_msg>Deprecated the existing TextPushButton, since it will soon be replaced by a version that uses the new buttons, see https://github.com/phetsims/sun/issues/94.<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Shows the about dialog.
 *
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  var VBox = require( 'SCENERY/nodes/VBox' );
  var Text = require( 'SCENERY/nodes/Text' );
  var inherit = require( 'PHET_CORE/inherit' );
  var ScreenView = require( 'JOIST/ScreenView' );
  var Panel = require( 'SUN/Panel' );
  var CheckBox = require( 'SUN/CheckBox' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var TextPushButtonDeprecated = require( 'SUN/TextPushButtonDeprecated' );

  // strings
  var showPointersString = require( 'string!JOIST/showPointers' );
  var doneString = require( 'string!JOIST/done' );
  var settingsString = require( 'string!JOIST/title.settings' );

  /**
   * @param {Sim} sim
   * @constructor
   */
  function SettingsDialog( sim ) {
    var settingsDialog = this;

    //Use view, to help center and scale content
    ScreenView.call( this, {renderer: 'svg'} );

    var content = new VBox( { align: 'center', spacing: 50, children: [
      new Text( settingsString, { font: new PhetFont( 16 ) } ),
      new CheckBox( new Text( showPointersString ), sim.showPointersProperty ),
      new TextPushButtonDeprecated( doneString, {
        listener: function() {
          settingsDialog.doneListeners.forEach( function( listener ) {
            listener();
          } );
        }
      } )
    ]} );

    //Show a gray overlay that will help focus on the about dialog, and prevent clicks on the sim while the dialog is up
    this.addChild( new Panel( content, {centerX: this.layoutBounds.centerX, centerY: this.layoutBounds.centerY, xMargin: 20, yMargin: 20 } ) );

    function resize() {
      settingsDialog.layout( $( window ).width(), $( window ).height() );
    }

    //Fit to the window and render the initial scene
    $( window ).resize( resize );
    resize();
    this.doneListeners = [];
  }

  return inherit( ScreenView, SettingsDialog, {
    addDoneListener: function( listener ) {
      this.doneListeners.push( listener );
    }
  } );
} );
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 *
 * 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.
 */

foam.CLASS({
  package: 'foam.box',
  name: 'Context',
  swiftName: 'BoxContext',

  requires: [
    'foam.box.BoxRegistryBox',
    'foam.box.NamedBox',
    'foam.box.ClassWhitelistContext',
    'foam.box.LoggedLookupContext',
  ],

  exports: [
    'creationContext',
    'me',
    'messagePortService',
    'registry',
    'root',
    'socketService',
    'webSocketService'
  ],

  properties: [
    {
      name: 'messagePortService',
      hidden: true,
      factory: function() {
        var model = this.__context__.lookup('foam.messageport.MessagePortService', true);
        if ( model ) {
          return model.create({
            delegate: this.registry
          }, this);
        }
      }
    },
    {
      name: 'socketService',
      hidden: true,
      factory: function() {
        var model = this.__context__.lookup('foam.net.node.SocketService', true);
        if ( model ) {
          return model.create({
            port: Math.floor( 10000 + ( Math.random() * 10000 ) ),
            delegate: this.registry
          }, this);
        }
      },
      swiftFactory: `
return __context__.lookup("foam.swift.net.SocketService")!.create(args: [
  "delegate$": registry$,
], x: __subContext__)
      `,
    },
    {
      name: 'webSocketService',
      hidden: true,
      factory: function() {
        var model = this.__context__.lookup('foam.net.node.WebSocketService', true) ||
            this.__context__.lookup('foam.net.web.WebSocketService', true);

        if ( model ) {
          return model.create({
            delegate: this.registry
          }, this);
        }
      }
    },
    {
      class: 'FObjectProperty',
      of: 'foam.box.Box',
      name: 'registry',
      hidden: true,
      factory: function() {
        return this.BoxRegistryBox.create();
      },
      swiftFactory: 'return BoxRegistryBox_create()',
    },
    {
      class: 'FObjectProperty',
      of: 'foam.box.Box',
      name: 'root',
      hidden: true,
      postSet: function(_, root) {
        foam.box.NamedBox.create({ name: '' }).delegate = root;
      },
      swiftPostSet: 'NamedBox_create(["name": ""]).delegate = newValue!',
    },
    {
      class: 'String',
      name: 'myname',
      hidden: true,
      swiftFactory:
          'return "/com/foamdev/anonymous/" + UUID().uuidString',
      factory: function() {
        return foam.isServer ?
          '/proc/' + require('process').pid + '/' + foam.uuid.randomGUID() :
          '/com/foamdev/anonymous/' + foam.uuid.randomGUID();
      }
    },
    {
      name: 'me',
      hidden: true,
      transient: true,
      factory: function() {
        var me = this.NamedBox.create({ name: this.myname });
        me.delegate = this.registry;
        return me;
      },
      swiftFactory: function() {/*
        let me = NamedBox_create(["name": self.myname])
        me.delegate = self.registry!
        return me
      */},
    },
    {
      class: 'Boolean',
      name: 'unsafe',
      value: true
    },
    {
      class: 'StringArray',
      name: 'classWhitelist'
    },
    {
      name: 'creationContext',
      documentation: `Provides required import for boxes that parse strings into
          FObjects.`,
      hidden: true,
      factory: function() {
        // TODO: Better way to inject the class whitelist.
        if ( this.unsafe ) {
          console.warn('**** Boxes are running in UNSAFE mode.  Turn this off before you go to production!');
          return this.LoggedLookupContext.create(null, this).__subContext__;
        }

        return this.ClassWhitelistContext.create({
          whitelist: this.classWhitelist
        }, this).__subContext__;
      },
      swiftFactory: `
return ClassWhitelistContext_create([
  "whitelist$": classWhitelist$,
]).__subContext__
      `,
    }
  ]
});
<commit_msg>Some cleanup around foam.net.WebSocketService in foam.box.Context.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 *
 * 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.
 */

foam.CLASS({
  package: 'foam.box',
  name: 'Context',
  swiftName: 'BoxContext',

  requires: [
    'foam.box.BoxRegistryBox',
    'foam.box.NamedBox',
    'foam.box.ClassWhitelistContext',
    'foam.box.LoggedLookupContext',
    'foam.net.WebSocketService',
  ],

  exports: [
    'creationContext',
    'me',
    'messagePortService',
    'registry',
    'root',
    'socketService',
    'webSocketService'
  ],

  properties: [
    {
      name: 'messagePortService',
      hidden: true,
      factory: function() {
        var model = this.__context__.lookup('foam.messageport.MessagePortService', true);
        if ( model ) {
          return model.create({
            delegate: this.registry
          }, this);
        }
      }
    },
    {
      name: 'socketService',
      hidden: true,
      factory: function() {
        var model = this.__context__.lookup('foam.net.node.SocketService', true);
        if ( model ) {
          return model.create({
            port: Math.floor( 10000 + ( Math.random() * 10000 ) ),
            delegate: this.registry
          }, this);
        }
      },
      swiftFactory: `
return __context__.lookup("foam.swift.net.SocketService")!.create(args: [
  "delegate$": registry$,
], x: __subContext__)
      `,
    },
    {
      name: 'webSocketService',
      hidden: true,
      factory: function() {
        return this.WebSocketService.create({
          delegate: this.registry
        });
      }
    },
    {
      class: 'FObjectProperty',
      of: 'foam.box.Box',
      name: 'registry',
      hidden: true,
      factory: function() {
        return this.BoxRegistryBox.create();
      },
      swiftFactory: 'return BoxRegistryBox_create()',
    },
    {
      class: 'FObjectProperty',
      of: 'foam.box.Box',
      name: 'root',
      hidden: true,
      postSet: function(_, root) {
        foam.box.NamedBox.create({ name: '' }).delegate = root;
      },
      swiftPostSet: 'NamedBox_create(["name": ""]).delegate = newValue!',
    },
    {
      class: 'String',
      name: 'myname',
      hidden: true,
      swiftFactory:
          'return "/com/foamdev/anonymous/" + UUID().uuidString',
      factory: function() {
        return foam.isServer ?
          '/proc/' + require('process').pid + '/' + foam.uuid.randomGUID() :
          '/com/foamdev/anonymous/' + foam.uuid.randomGUID();
      }
    },
    {
      name: 'me',
      hidden: true,
      transient: true,
      factory: function() {
        var me = this.NamedBox.create({ name: this.myname });
        me.delegate = this.registry;
        return me;
      },
      swiftFactory: function() {/*
        let me = NamedBox_create(["name": self.myname])
        me.delegate = self.registry!
        return me
      */},
    },
    {
      class: 'Boolean',
      name: 'unsafe',
      value: true
    },
    {
      class: 'StringArray',
      name: 'classWhitelist'
    },
    {
      name: 'creationContext',
      documentation: `Provides required import for boxes that parse strings into
          FObjects.`,
      hidden: true,
      factory: function() {
        // TODO: Better way to inject the class whitelist.
        if ( this.unsafe ) {
          console.warn('**** Boxes are running in UNSAFE mode.  Turn this off before you go to production!');
          return this.LoggedLookupContext.create(null, this).__subContext__;
        }

        return this.ClassWhitelistContext.create({
          whitelist: this.classWhitelist
        }, this).__subContext__;
      },
      swiftFactory: `
return ClassWhitelistContext_create([
  "whitelist$": classWhitelist$,
]).__subContext__
      `,
    }
  ]
});
<|endoftext|>


<commit_before>var Util = {};

Util.argmin = function(list, func) {
    if (typeof func === 'undefined') {
        func = function (x){return x;};
    }
    var minValue = Infinity,
        minIndex = -1;
    for (var i = 0; i < list.length; i++) {
        if (func(list[i]) < minValue) {
            minIndex = i;
            minValue = func(list[i]);
        }
    }
    return minIndex;
};

Util.argmax = function(list, func) {
    if (typeof func === 'undefined') {
        func = function (x){return x;};
    }
    var maxValue = -Infinity,
        maxIndex = -1;
    for (var i = 0; i < list.length; i++) {
        if (func(list[i]) > maxValue) {
            maxIndex = i;
            maxValue = func(list[i]);
        }
    }
    return maxIndex;
};

var Point = function(x, y) {
    if (typeof x === 'undefined' || typeof y === 'undefined'){
        throw new Error("Need to specify x and y");
    }

    this.x = x;
    this.y = y;
};

Point.getX = function(p) {
    return p.x;
};

Point.getY = function(p) {
    return p.y;
};

Point.prototype.toJSON = function() {
    return {
        x: this.x,
        y: this.y
    };
};

Point.prototype.equals = function(p) {
    return p.x === this.x && p.y === this.y;
};

var Zone = function(points) {
    if (typeof points === 'undefined'){
        this.points = [];
    } else {
        // list of point
        this.points = points;
    }

};

Zone.prototype.toJSON = function() {
    var json_points = [];
    for (var i = 0; i < this.points.length; i++) {
        json_points.push(this.points[i].toJSON());
    }

    return {
        points: json_points
    };
};

Zone.prototype.findPointIdx = function(x, y) {
    var p = new Point(x, y);
    for (var i = 0; i < this.points.length; i++) {
        if (this.points[i].equals(p)) {
            return i;
        }
    }
    // Point does not exist within collection
    return -1;
};

Zone.prototype.getPoints = function() {
    return this.points;
};

Zone.prototype.getTopLeftPoint = function() {
    var xIdx = Util.argmin(this.points, Point.getX),
        yIdx = Util.argmin(this.points, Point.getY);
    return this.points[this.findPointIdx(this.points[xIdx].x, this.points[yIdx].y)];
};

Zone.prototype.getTopRightPoint = function() {
    var xIdx = Util.argmax(this.points, Point.getX),
        yIdx = Util.argmin(this.points, Point.getY);
    return this.points[this.findPointIdx(this.points[xIdx].x, this.points[yIdx].y)];
};

Zone.prototype.getBottomRightPoint = function() {
    var xIdx = Util.argmax(this.points, Point.getX),
        yIdx = Util.argmax(this.points, Point.getY);
    return this.points[this.findPointIdx(this.points[xIdx].x, this.points[yIdx].y)];
};

Zone.prototype.getBottomLeftPoint = function() {
    var xIdx = Util.argmin(this.points, Point.getX),
        yIdx = Util.argmax(this.points, Point.getY);
    return this.points[this.findPointIdx(this.points[xIdx].x, this.points[yIdx].y)];
};

Zone.prototype.setPoints = function(points) {
    this.points = points;
};

Zone.prototype.setFromTopLeftPoint = function(x, y) {
    this.points = [
        new Point(x, y),
        new Point(x, y),
        new Point(x, y),
        new Point(x, y)
    ];
};

var Floor = function(input_zones) {
    if (typeof input_zones === 'undefined') {
        this.zones = [];
    } else {
        this.zones = input_zones;
    }
};

Floor.prototype.toJSON = function() {
    var json_zones = [];
    for (var i = 0; i < this.zones.length; i++) {
        json_points.push(this.zones[i].toJSON());
    }
    return {
        zones: json_zones
    };
};

if (typeof module === 'undefined') {
    var module = {};
}
module.exports = {
    Point: Point,
    Zone: Zone,
    Floor: Floor,
    Util: Util
};

/*

How points should be used:
class Point
    - should have x,y
    - 2 edges
    - other possible metadata

class Edge
    - should have start/end Point

How points should look be stored with respect to everything:

var floors = [
    {
        floorNumber: 4
        descriptiveFloorNumber: "4",
        rooms: [
            {
                extras: {
                    'owner': 'David Culler'
                },
                label: '465',
                // must be in order (clockwise)
                points: [
                    {
                        x: 130,
                        y: 200
                    },
                    {
                        x: 230,
                        y: 200
                    },
                    {
                        x: 230,
                        y: 100
                    }
                ]
            }
        ]
    },
    {
        floorNumber: 1
        descriptiveFloorNumber: "Basement",
        rooms: [
            {
                extras: {
                    'owner': 'server room'
                },
                label: '123',
                points: [
                    {
                        x: 130,
                        y: 200
                    },
                    {
                        x: 230,
                        y: 200
                    },
                    {
                        x: 230,
                        y: 100
                    }
                ]
            }
        ]
    }
]
*/
<commit_msg>added expansion func for updating poitns<commit_after>var Util = {};

Util.argmin = function(list, func) {
    if (typeof func === 'undefined') {
        func = function (x){return x;};
    }
    var minValue = Infinity,
        minIndex = -1;
    for (var i = 0; i < list.length; i++) {
        if (func(list[i]) < minValue) {
            minIndex = i;
            minValue = func(list[i]);
        }
    }
    return minIndex;
};

Util.argmax = function(list, func) {
    if (typeof func === 'undefined') {
        func = function (x){return x;};
    }
    var maxValue = -Infinity,
        maxIndex = -1;
    for (var i = 0; i < list.length; i++) {
        if (func(list[i]) > maxValue) {
            maxIndex = i;
            maxValue = func(list[i]);
        }
    }
    return maxIndex;
};

var Point = function(x, y) {
    if (typeof x === 'undefined' || typeof y === 'undefined'){
        throw new Error("Need to specify x and y");
    }

    this.x = x;
    this.y = y;
};

Point.getX = function(p) {
    return p.x;
};

Point.getY = function(p) {
    return p.y;
};

Point.prototype.toJSON = function() {
    return {
        x: this.x,
        y: this.y
    };
};

Point.prototype.equals = function(p) {
    return p.x === this.x && p.y === this.y;
};

var Zone = function(points) {
    if (typeof points === 'undefined'){
        this.points = [];
    } else {
        // list of point
        this.points = points;
    }

};

Zone.prototype.toJSON = function() {
    var json_points = [];
    for (var i = 0; i < this.points.length; i++) {
        json_points.push(this.points[i].toJSON());
    }

    return {
        points: json_points
    };
};

Zone.prototype.findPointIdx = function(x, y) {
    var p = new Point(x, y);
    for (var i = 0; i < this.points.length; i++) {
        if (this.points[i].equals(p)) {
            return i;
        }
    }
    // Point does not exist within collection
    return -1;
};

Zone.prototype.getPoints = function() {
    return this.points;
};

Zone.prototype.getTopLeftPoint = function() {
    var xIdx = Util.argmin(this.points, Point.getX),
        yIdx = Util.argmin(this.points, Point.getY);
    return this.points[this.findPointIdx(this.points[xIdx].x, this.points[yIdx].y)];
};

Zone.prototype.getTopRightPoint = function() {
    var xIdx = Util.argmax(this.points, Point.getX),
        yIdx = Util.argmin(this.points, Point.getY);
    return this.points[this.findPointIdx(this.points[xIdx].x, this.points[yIdx].y)];
};

Zone.prototype.getBottomRightPoint = function() {
    var xIdx = Util.argmax(this.points, Point.getX),
        yIdx = Util.argmax(this.points, Point.getY);
    return this.points[this.findPointIdx(this.points[xIdx].x, this.points[yIdx].y)];
};

Zone.prototype.getBottomLeftPoint = function() {
    var xIdx = Util.argmin(this.points, Point.getX),
        yIdx = Util.argmax(this.points, Point.getY);
    return this.points[this.findPointIdx(this.points[xIdx].x, this.points[yIdx].y)];
};

Zone.prototype.setPoints = function(points) {
    this.points = points;
};

Zone.prototype.setFromTopLeftPoint = function(x, y) {
    this.points = [
        new Point(x, y),
        new Point(x, y),
        new Point(x, y),
        new Point(x, y)
    ];
};

Zone.prototype.updateFromBottomRight = function(x, y) {
    // top left
        // nothing
    // top right
    this.points[1].x = x;
    // bottom left
    this.points[2].y = y;
    // bottom right
    this.points[3].x = x;
    this.points[3].y = y;
};

var Floor = function(input_zones) {
    if (typeof input_zones === 'undefined') {
        this.zones = [];
    } else {
        this.zones = input_zones;
    }
};

Floor.prototype.toJSON = function() {
    var json_zones = [];
    for (var i = 0; i < this.zones.length; i++) {
        json_points.push(this.zones[i].toJSON());
    }
    return {
        zones: json_zones
    };
};

if (typeof module === 'undefined') {
    var module = {};
}
module.exports = {
    Point: Point,
    Zone: Zone,
    Floor: Floor,
    Util: Util
};

/*

How points should be used:
class Point
    - should have x,y
    - 2 edges
    - other possible metadata

class Edge
    - should have start/end Point

How points should look be stored with respect to everything:

var floors = [
    {
        floorNumber: 4
        descriptiveFloorNumber: "4",
        rooms: [
            {
                extras: {
                    'owner': 'David Culler'
                },
                label: '465',
                // must be in order (clockwise)
                points: [
                    {
                        x: 130,
                        y: 200
                    },
                    {
                        x: 230,
                        y: 200
                    },
                    {
                        x: 230,
                        y: 100
                    }
                ]
            }
        ]
    },
    {
        floorNumber: 1
        descriptiveFloorNumber: "Basement",
        rooms: [
            {
                extras: {
                    'owner': 'server room'
                },
                label: '123',
                points: [
                    {
                        x: 130,
                        y: 200
                    },
                    {
                        x: 230,
                        y: 200
                    },
                    {
                        x: 230,
                        y: 100
                    }
                ]
            }
        ]
    }
]
*/
<|endoftext|>


<commit_before>import React from 'react';
import '../assets/css/ToolBarButton.css';
import NewTaskIcon from '../assets/icons/NewTaskIcon.svg';
import RemoveTaskIcon from '../assets/icons/RemoveTaskIcon.svg';
import NewTaskListIcon from '../assets/icons/NewTaskListIcon.svg';
import RemoveTaskListIcon from '../assets/icons/RemoveTaskListIcon.svg';
import LockIcon from '../assets/icons/LockIcon.svg';
import SettingsIcon from '../assets/icons/SettingsIcon.svg';
import KeyboardIcon from '../assets/icons/KeyboardIcon.svg';
import EyeOpenIcon from '../assets/icons/EyeOpenIcon.svg';
import EyeClosedIcon from '../assets/icons/EyeClosedIcon.svg';
import Button from './Button';

class ProjectToolBar extends React.Component {
    constructor(props) {
        super(props);

        // Refs.
        this.showCompletedTasksCheckboxRef = React.createRef();
        this.layoutSelectorRef = React.createRef();

        this.handleAddTaskButtonClick = this.handleAddTaskButtonClick.bind(this);
        this.handleRemoveTaskButtonClick = this.handleRemoveTaskButtonClick.bind(this);
        this.handleAddTaskListButtonClick = this.handleAddTaskListButtonClick.bind(this);
        this.handleRemoveTaskListButtonClick = this.handleRemoveTaskListButtonClick.bind(this);
        this.handleLockButtonClick = this.handleLockButtonClick.bind(this);
        this.handleAppSettingsButtonClick = this.handleAppSettingsButtonClick.bind(this);
        this.getLockButtonJSX = this.getLockButtonJSX.bind(this);
        this.handleKeyboardShortcutsButtonClick = this.handleKeyboardShortcutsButtonClick.bind(this);
        this.handleShowCompletedTasksCheckboxChanged = this.handleShowCompletedTasksCheckboxChanged.bind(this);
        this.getShowCompletedTasksButtonJSX = this.getShowCompletedTasksButtonJSX.bind(this);
        this.handleShowCompletedTasksButtonClick = this.handleShowCompletedTasksButtonClick.bind(this);
        this.handleLayoutSelectorChange = this.handleLayoutSelectorChange.bind(this);
        this.getProjectLayoutTypeSelectorJSX = this.getProjectLayoutTypeSelectorJSX.bind(this);

    }

    render() {
        var lockButtonJSX = this.getLockButtonJSX();
        var showCompletedTasksButtonJSX = this.getShowCompletedTasksButtonJSX();
        var projectLayoutTypeSelectorJSX = this.getProjectLayoutTypeSelectorJSX();

        return (

                    <Button iconSrc={NewTaskListIcon} onClick={this.handleAddTaskListButtonClick}
                        isEnabled={this.props.buttonEnableStates.isAddTaskListButtonEnabled}
                        tooltip="Add new Task List" />

                    <Button iconSrc={RemoveTaskListIcon} onClick={this.handleRemoveTaskListButtonClick}
                        isEnabled={this.props.buttonEnableStates.isRemoveTaskListButtonEnabled}
                        tooltip="Delete selected Task List" />

                    <Button iconSrc={NewTaskIcon} onClick={this.handleAddTaskButtonClick}
                        isEnabled={this.props.buttonEnableStates.isAddTaskButtonEnabled}
                        tooltip="Add new Task" />

                    <Button iconSrc={RemoveTaskIcon} onClick={this.handleRemoveTaskButtonClick}
                        isEnabled={this.props.buttonEnableStates.isRemoveTaskButtonEnabled}
                        tooltip="Delete selected Task" />

                        {showCompletedTasksButtonJSX}

                    {projectLayoutTypeSelectorJSX}

                    <Button iconSrc={KeyboardIcon} onClick={this.handleKeyboardShortcutsButtonClick}
                        tooltip="View keyboard shortcuts" />

                    <Button iconSrc={SettingsIcon} onClick={this.handleAppSettingsButtonClick}
                        tooltip="Settings" />
                    {lockButtonJSX}

        )
    }

    getProjectLayoutTypeSelectorJSX() {
        if (this.props.showProjectLayoutTypeSelector) {
            return (

                    <select className="LayoutSelector" ref={this.layoutSelectorRef} value={this.props.projectLayoutType}
                        onChange={this.handleLayoutSelectorChange}>
                        <option value="global">
                            Global Layout
                            </option>

                        <option value="local">
                            Local Layout
                            </option>
                    </select>

            )
        }
    }

    handleLayoutSelectorChange() {
        var value = this.layoutSelectorRef.current.value;

        this.props.onLayoutSelectorChange(value);
    }

    getShowCompletedTasksButtonJSX() {
        var iconSrc = this.props.showCompletedTasks ? EyeClosedIcon : EyeOpenIcon;
        var tooltipText = this.props.showCompletedTasks ? "Hide completed tasks" : "Show completed tasks";
        return (
            <Button iconSrc={iconSrc}  onClick={this.handleShowCompletedTasksButtonClick}
            tooltip={tooltipText} isEnabled={this.props.buttonEnableStates.isShowCompletedTasksButtonEnabled} />
        )
    }

    handleShowCompletedTasksButtonClick() {
        this.props.onShowCompletedTasksChanged(!this.props.showCompletedTasks);
    }

    handleShowCompletedTasksCheckboxChanged() {
        var value = this.showCompletedTasksCheckboxRef.current.checked;
        this.props.onShowCompletedTasksChanged(value);
    }

    handleKeyboardShortcutsButtonClick() {
        this.props.onKeyboardShortcutsButtonClick();
    }

    getLockButtonJSX() {
        var hideLockButton = this.props.hideLockButton === undefined ? false : this.props.hideLockButton;

        if (!hideLockButton) {
            return (
                <React.Fragment>

                    <Button iconSrc={LockIcon} onClick={this.handleLockButtonClick}
                        tooltip="Lock Application" />
                </React.Fragment>
            )
        }
    }

    handleAppSettingsButtonClick() {
        this.props.onAppSettingsButtonClick();
    }

    handleLockButtonClick(e) {
        this.props.onLockButtonClick();
    }

    handleAddTaskButtonClick(e) {
        this.props.onAddTaskButtonClick();
    }

    handleRemoveTaskButtonClick(e) {
        this.props.onRemoveTaskButtonClick();
    }

    handleAddTaskListButtonClick(e) {
        this.props.onAddTaskListButtonClick();
    }

    handleRemoveTaskListButtonClick(e) {
        this.props.onRemoveTaskListButtonClick();
    }
}

export default ProjectToolBar;<commit_msg>Feature: Added tooltip to Project Layout Selector.<commit_after>import React from 'react';
import '../assets/css/ToolBarButton.css';
import NewTaskIcon from '../assets/icons/NewTaskIcon.svg';
import RemoveTaskIcon from '../assets/icons/RemoveTaskIcon.svg';
import NewTaskListIcon from '../assets/icons/NewTaskListIcon.svg';
import RemoveTaskListIcon from '../assets/icons/RemoveTaskListIcon.svg';
import LockIcon from '../assets/icons/LockIcon.svg';
import SettingsIcon from '../assets/icons/SettingsIcon.svg';
import KeyboardIcon from '../assets/icons/KeyboardIcon.svg';
import EyeOpenIcon from '../assets/icons/EyeOpenIcon.svg';
import EyeClosedIcon from '../assets/icons/EyeClosedIcon.svg';
import Button from './Button';
import ReactTooltip from 'react-tooltip';

class ProjectToolBar extends React.Component {
    constructor(props) {
        super(props);

        // Refs.
        this.showCompletedTasksCheckboxRef = React.createRef();
        this.layoutSelectorRef = React.createRef();

        this.handleAddTaskButtonClick = this.handleAddTaskButtonClick.bind(this);
        this.handleRemoveTaskButtonClick = this.handleRemoveTaskButtonClick.bind(this);
        this.handleAddTaskListButtonClick = this.handleAddTaskListButtonClick.bind(this);
        this.handleRemoveTaskListButtonClick = this.handleRemoveTaskListButtonClick.bind(this);
        this.handleLockButtonClick = this.handleLockButtonClick.bind(this);
        this.handleAppSettingsButtonClick = this.handleAppSettingsButtonClick.bind(this);
        this.getLockButtonJSX = this.getLockButtonJSX.bind(this);
        this.handleKeyboardShortcutsButtonClick = this.handleKeyboardShortcutsButtonClick.bind(this);
        this.handleShowCompletedTasksCheckboxChanged = this.handleShowCompletedTasksCheckboxChanged.bind(this);
        this.getShowCompletedTasksButtonJSX = this.getShowCompletedTasksButtonJSX.bind(this);
        this.handleShowCompletedTasksButtonClick = this.handleShowCompletedTasksButtonClick.bind(this);
        this.handleLayoutSelectorChange = this.handleLayoutSelectorChange.bind(this);
        this.getProjectLayoutTypeSelectorJSX = this.getProjectLayoutTypeSelectorJSX.bind(this);

    }

    render() {
        var lockButtonJSX = this.getLockButtonJSX();
        var showCompletedTasksButtonJSX = this.getShowCompletedTasksButtonJSX();
        var projectLayoutTypeSelectorJSX = this.getProjectLayoutTypeSelectorJSX();

        return (

                    <Button iconSrc={NewTaskListIcon} onClick={this.handleAddTaskListButtonClick}
                        isEnabled={this.props.buttonEnableStates.isAddTaskListButtonEnabled}
                        tooltip="Add new Task List" />

                    <Button iconSrc={RemoveTaskListIcon} onClick={this.handleRemoveTaskListButtonClick}
                        isEnabled={this.props.buttonEnableStates.isRemoveTaskListButtonEnabled}
                        tooltip="Delete selected Task List" />

                    <Button iconSrc={NewTaskIcon} onClick={this.handleAddTaskButtonClick}
                        isEnabled={this.props.buttonEnableStates.isAddTaskButtonEnabled}
                        tooltip="Add new Task" />

                    <Button iconSrc={RemoveTaskIcon} onClick={this.handleRemoveTaskButtonClick}
                        isEnabled={this.props.buttonEnableStates.isRemoveTaskButtonEnabled}
                        tooltip="Delete selected Task" />

                        {showCompletedTasksButtonJSX}

                    {projectLayoutTypeSelectorJSX}

                    <Button iconSrc={KeyboardIcon} onClick={this.handleKeyboardShortcutsButtonClick}
                        tooltip="View keyboard shortcuts" />

                    <Button iconSrc={SettingsIcon} onClick={this.handleAppSettingsButtonClick}
                        tooltip="Settings" />
                    {lockButtonJSX}

        )
    }

    getProjectLayoutTypeSelectorJSX() {
        if (this.props.showProjectLayoutTypeSelector) {
            return (

                    <select className="LayoutSelector" ref={this.layoutSelectorRef} value={this.props.projectLayoutType}
                        onChange={this.handleLayoutSelectorChange}>
                        <option value="global">
                            Global Layout
                       </option>

                        <option value="local">
                            Personal Layout
                       </option>
                    </select>
                    <ReactTooltip />

            )
        }
    }

    handleLayoutSelectorChange() {
        var value = this.layoutSelectorRef.current.value;

        this.props.onLayoutSelectorChange(value);
    }

    getShowCompletedTasksButtonJSX() {
        var iconSrc = this.props.showCompletedTasks ? EyeClosedIcon : EyeOpenIcon;
        var tooltipText = this.props.showCompletedTasks ? "Hide completed tasks" : "Show completed tasks";
        return (
            <Button iconSrc={iconSrc}  onClick={this.handleShowCompletedTasksButtonClick}
            tooltip={tooltipText} isEnabled={this.props.buttonEnableStates.isShowCompletedTasksButtonEnabled} />
        )
    }

    handleShowCompletedTasksButtonClick() {
        this.props.onShowCompletedTasksChanged(!this.props.showCompletedTasks);
    }

    handleShowCompletedTasksCheckboxChanged() {
        var value = this.showCompletedTasksCheckboxRef.current.checked;
        this.props.onShowCompletedTasksChanged(value);
    }

    handleKeyboardShortcutsButtonClick() {
        this.props.onKeyboardShortcutsButtonClick();
    }

    getLockButtonJSX() {
        var hideLockButton = this.props.hideLockButton === undefined ? false : this.props.hideLockButton;

        if (!hideLockButton) {
            return (
                <React.Fragment>

                    <Button iconSrc={LockIcon} onClick={this.handleLockButtonClick}
                        tooltip="Lock Application" />
                </React.Fragment>
            )
        }
    }

    handleAppSettingsButtonClick() {
        this.props.onAppSettingsButtonClick();
    }

    handleLockButtonClick(e) {
        this.props.onLockButtonClick();
    }

    handleAddTaskButtonClick(e) {
        this.props.onAddTaskButtonClick();
    }

    handleRemoveTaskButtonClick(e) {
        this.props.onRemoveTaskButtonClick();
    }

    handleAddTaskListButtonClick(e) {
        this.props.onAddTaskListButtonClick();
    }

    handleRemoveTaskListButtonClick(e) {
        this.props.onRemoveTaskListButtonClick();
    }
}

export default ProjectToolBar;<|endoftext|>


<commit_before>var annosource;

(function($){

	var inputs = {};

	annoSource = {
		init : function() {
			var t = this;
			t.widgets = [];
			inputs.dialog = $('#anno-popup-source');
			inputs.validate = $('#anno-source-validate');
			inputs.insert = $('#anno-source-insert');
			inputs.close = $('#anno-source-close');
			t.validationStatusID = '#validation-status';

			//inputs.dialog.bind('wpdialogrefresh', annoSource.refresh);
			inputs.dialog.bind('wpdialogclose', annoSource.onClose);

			t.codemirror = CodeMirror.fromTextArea(document.getElementById('htmlSource'), {
				lineNumbers: true,
				//theme: 'elegant'
			});

			// Prevents CodeMirror from bugging out during show/hide/resize process
			$('body').on('click', '.mce_annosource', function(e) {
				e.preventDefault();
				t.codemirror.refresh();
			});

			// Re-evaluate the current xml
			inputs.validate.on('click', function(e) {
				e.preventDefault();
				t.validate();
			});

			// Insert the current XML back into the dom
			inputs.insert.on('click', function(e) {
				e.preventDefault();
				$(document).on('annoValidation', annoSource.insertAlert);
				t.validate();
			});

			// Close the xml editor without inserting content
			inputs.close.on('click', function(e) {
				e.preventDefault();
				inputs.dialog.unbind('wpdialogclose', annoSource.onClose);
				tinyMCEPopup.close();
				t._cleanup();
			});

			$('body').on('click', t.validationStatusID + ' a, .cm-error', function(e) {
				e.preventDefault();
				t.codemirror.focus();
				t.codemirror.setCursor($(this).data('line'), $(this).data('col'));
			});

			inputs.dialog.bind('wpdialogbeforeopen', annoSource.beforeOpen);
		},
		insertAlert : function(e, result) {
			var t = annoSource;
			$(document).off('annoValidation', t.insertAlert);
			if (result == 'error') {
				if (confirm('There are validation errors still. Are you sure you want to insert this content?')) { //@TODO i18n
					tinyMCEPopup.close();
					// onClose triggers cleanup
				}
			}
			else {
				tinyMCEPopup.close();
				// onClose triggers cleanup
			}
		},
		beforeOpen : function () {
			var t = annoSource;
			t.editor = tinyMCEPopup.editor;
			t.editorVal = t.editor.getContent({source_view : true});
			t.editorVal = t.editorVal.replace(/^<!DOCTYPE[^>]*?>/, '');
			t.codemirror.setValue(t.editorVal);
			t._validate(t.editorVal);
		},
		onClose : function () {
			var t = annoSource;
			t._cleanup();
			// Insert code back into the editor,
			// Add doctype back in
			t.editor.setContent('<!DOCTYPE sec SYSTEM "http://dtd.nlm.nih.gov/publishing/3.0/journalpublishing3.dtd">' + t.codemirror.getValue(), {source_view : true});
		},
		validate : function() {
			this._cleanup();
			this._validate(this.codemirror.getValue());
		},
		_validate : function (content) {
			var t = this;
			content = ''+content+'';
			$.post(ajaxurl,
				{
					content: content,
					action: 'anno_validate'
				},
				function (data) {
					var widget, errors = data.errors, insertEl, msg, $statusUL = $(t.validationStatusID);
					if (data.status == 'error') {
						for (var i = 0; i <= errors.length - 1; i++) {

							// Insert error at top of editor
							insertEl = document.createElement('a');
							$(insertEl).text(errors[i].fullMessage).data('col', errors[i].column).data('line', errors[i].line).attr('href', '#');
							$statusUL.append($(insertEl).wrap('

').parent());

							// Insert error directly into the editor itself
							msg = document.createElement("a");
							msg.className = 'cm-error';
							$(msg).data('col', errors[i].column).data('line', errors[i].line);
							msg.appendChild(document.createTextNode(errors[i].fullMessage));
							widget = t.codemirror.addLineWidget(errors[i].line, msg, {coverGutter: false, noHScroll: true, above: false, handleMouseEvents: true});
							t.widgets.push(widget);

							$.event.trigger('annoValidation', ['error']);
						};
					}
					else if (data.status == 'success') {
						insertEl = document.createElement('li');
						$(insertEl).text(data.message);
						$statusUL.append($(insertEl));

						$.event.trigger('annoValidation', ['success']);
					}
				},
				'json'
			);
		},
		_cleanup : function() {
			$(this.validationStatusID).html('');

			// Loop through the widgets, removing them
			for (var i = this.widgets.length - 1; i >= 0; i--) {
				this.widgets[i].clear();
			};
		}
	};

	$(function(){
		annoSource.init();
	});
})(jQuery);
<commit_msg>Validation based on which editor is being used<commit_after>var annosource;

(function($){
	var inputs = {};

	annoSource = {
		init : function() {
			var t = this;
			t.widgets = [];
			inputs.dialog = $('#anno-popup-source');
			inputs.validate = $('#anno-source-validate');
			inputs.insert = $('#anno-source-insert');
			inputs.close = $('#anno-source-close');
			t.validationStatusID = '#validation-status';

			//inputs.dialog.bind('wpdialogrefresh', annoSource.refresh);
			inputs.dialog.bind('wpdialogclose', annoSource.onClose);

			t.codemirror = CodeMirror.fromTextArea(document.getElementById('htmlSource'), {
				lineNumbers: true,
				//theme: 'elegant'
			});

			// Prevents CodeMirror from bugging out during show/hide/resize process
			$('body').on('click', '.mce_annosource', function(e) {
				e.preventDefault();
				t.codemirror.refresh();
			});

			// Re-evaluate the current xml
			inputs.validate.on('click', function(e) {
				e.preventDefault();
				t.validate();
			});

			// Insert the current XML back into the dom
			inputs.insert.on('click', function(e) {
				e.preventDefault();
				$(document).on('annoValidation', annoSource.insertAlert);
				t.validate();
			});

			// Close the xml editor without inserting content
			inputs.close.on('click', function(e) {
				e.preventDefault();
				inputs.dialog.unbind('wpdialogclose', annoSource.onClose);
				tinyMCEPopup.close();
				t._cleanup();
			});

			$('body').on('click', t.validationStatusID + ' a, .cm-error', function(e) {
				e.preventDefault();
				t.codemirror.focus();
				t.codemirror.setCursor($(this).data('line'), $(this).data('col'));
			});

			inputs.dialog.bind('wpdialogbeforeopen', annoSource.beforeOpen);
		},
		validate : function() {
			this._cleanup();
			this._validate(this.codemirror.getValue());
		},
		_validate : function (content) {
			var t = this;
			if (t.editor.id == 'content') {
				content = ''+content+'';
			}
			else {
				content = '<abstract>'+content+'</abstract>';
			}
			$.post(ajaxurl,
				{
					content: content,
					action: 'anno_validate'
				},
				function (data) {
					var widget, errors = data.errors, insertEl, msg, $statusUL = $(t.validationStatusID);
					if (data.status == 'error') {
						for (var i = 0; i <= errors.length - 1; i++) {

							// Insert error at top of editor
							insertEl = document.createElement('a');
							$(insertEl).text(errors[i].fullMessage).data('col', errors[i].column).data('line', errors[i].line).attr('href', '#');
							$statusUL.append($(insertEl).wrap('

').parent());

							// Insert error directly into the editor itself
							msg = document.createElement("a");
							msg.className = 'cm-error';
							$(msg).data('col', errors[i].column).data('line', errors[i].line);
							msg.appendChild(document.createTextNode(errors[i].fullMessage));
							widget = t.codemirror.addLineWidget(errors[i].line, msg, {coverGutter: false, noHScroll: true, above: false, handleMouseEvents: true});
							t.widgets.push(widget);

							$.event.trigger('annoValidation', ['error']);
						};
					}
					else if (data.status == 'success') {
						insertEl = document.createElement('li');
						$(insertEl).text(data.message);
						$statusUL.append($(insertEl));

						$.event.trigger('annoValidation', ['success']);
					}
				},
				'json'
			);
		},
		_cleanup : function() {
			$(this.validationStatusID).html('');

			// Loop through the widgets, removing them
			for (var i = this.widgets.length - 1; i >= 0; i--) {
				this.widgets[i].clear();
			};
		},
		// * Event Callbacks ***************
		insertAlert : function(e, result) {
			var t = annoSource;
			$(document).off('annoValidation', t.insertAlert);
			if (result == 'error') {
				if (confirm('There are validation errors still. Are you sure you want to insert this content?')) { //@TODO i18n
					tinyMCEPopup.close();
					// onClose triggers cleanup
				}
			}
			else {
				tinyMCEPopup.close();
				// onClose triggers cleanup
			}
		},
		beforeOpen : function () {
			var t = annoSource;
			t.editor = tinyMCEPopup.editor;
			t.editorVal = t.editor.getContent({source_view : true});
			t.editorVal = t.editorVal.replace(/^<!DOCTYPE[^>]*?>/, '');
			t.codemirror.setValue(t.editorVal);
			t._validate(t.editorVal);
		},
		onClose : function () {
			var t = annoSource;
			t._cleanup();
			// Insert code back into the editor,
			// Add doctype back in
			t.editor.setContent('<!DOCTYPE sec SYSTEM "http://dtd.nlm.nih.gov/publishing/3.0/journalpublishing3.dtd">' + t.codemirror.getValue(), {source_view : true});
		}
	};

	$(function(){
		annoSource.init();
	});
})(jQuery);
<|endoftext|>


<commit_before>var gulp = require('flarum-gulp');

gulp({
  modules: {
    'santiagobiali/logo': 'src/**/*.js'
  }
});
<commit_msg>Update Gulpfile.js<commit_after>var flarum = require('flarum-gulp');

flarum({
  modules: {
    'santiagobiali/logo': [
      'src/**/*.js'
    ]
  }
});
<|endoftext|>


<commit_before>const http = require('http');
const EventEmitter = require('events');
const errorLog = require('./errorLog');

class LoadBalancer extends EventEmitter {
  constructor() {
    super();
    this.algo = 'lc';
    this.cache = {};
    this.options = [];
    this.routes = {};
    this.addOptions = this.addOptions.bind(this);
    this.setRoutes = this.setRoutes.bind(this);
    this.healthCheck = this.healthCheck.bind(this);
    this.clearCache = this.clearCache.bind(this);
    this.isStatic = this.isStatic.bind(this);
    this.shouldCache = this.shouldCache.bind(this);
    this.cacheContent = this.cacheContent.bind(this);
    this.init = this.init.bind(this);
    this.lbInit = this.lbInit.bind(this);
  }

  setAlgoRR() {
    this.algo = 'rr';
  }

  setAlgoLC() {
    this.algo = 'lc';
  }

  setRoutes(routes) {
    for (let i = 0; i < routes.length; i++) {
      let temp = routes[i][0].concat(routes[i][1]);
      this.routes[temp] = true;
    }
  };

  addOptions(options) {
    for (let i = 1; i < options.length; i += 1) {
      this.options.push(options[i]);
    }
  }

  healthCheck(interval = null) {
    /*
  15 minute interval healthcheck sends dummy get request to servers(ports) to check server health
  alters 'active' boolean value based on result of health check
    */
    // loops through servers in options & sends mock get request to each
    const options = this.options;
    //console.log(options);
    for (let i = 0; i < options.length; i += 1) {
      http.get(options[i], (res) => {
        if (res.statusCode > 100 && res.statusCode < 400) {
          // console.log('statusCode worked');
          if (options[i].active === false) options[i].active = true;
        } else {
          // errorLog.write(res.statusCode);
          options[i].active = false;
          // console.log('statusCode did not meet criteria, server active set to false');
        }
        res.on('end', () => {
          // response from server received, reset value to true if prev false
          if (options[i].active === false) options[i].active = true;
        });
      }).on('error', (e) => {
        e.name = "HealthCheck Error";
        errorLog.write(e);
        // console.log('Got Error: '.concat(e.message));
        // if error occurs, set boolean of 'active' to false to ensure no further requests to server
        if (e) {
          options[i].active = false;
        }
      });
    }
    if (interval !== null) {
      setTimeout(() => {
        this.healthCheck(interval);
      }, interval);
    }
  };

  clearCache(interval = null) {
    this.cache = {};
    if (interval !== null) {
      setTimeout(() => {
        this.clearCache(this.cache, interval);
      }, interval);
    }
  };

  iisStatic(bReq) {
    // if file is html/css/javascript
    if (bReq.url.slice(bReq.url.length - 5) === '.html' || bReq.url.slice(bReq.url.length - 4) === '.css' || bReq.url.slice(bReq.url.length - 3) === '.jsx') {
      // flag variable set to true to enable caching before sending response to browser
      return true;
    }
    return false;
  };

  shouldCache(bReq, routes) {
    // user input 'all' to allow cacheEverything method to always work
    // if (bReq === 'all') return true;
    // console.log(routes);
    return this.isStatic(bReq) || routes[bReq.method + bReq.url];
  };

  cacheContent(body, cache, bReq, routes) {
    // console.log(loadBalancer.shouldCache(bReq, routes));
    if (this.shouldCache(bReq, routes) === true) {
      // console.log('Successfully cached request result');
      // cache response
      cache[bReq.method + bReq.url] = body;
    }
  };

  init(bReq, bRes) {
    const options = this.options;
    const cache = this.cache;
    const routes = this.routes;
    if (cache[bReq.method + bReq.url]) {
      this.emit('cacheRes');
      // console.log('Request response exists, pulling from cache');
      bRes.end(cache[bReq.method + bReq.url]);
    } else {
      this.emit('targetRes');
      let body = '';
      // check for valid request & edge case removes request to '/favicon.ico'
      if (bReq.url !== null && bReq.url !== '/favicon.ico') {
        let INDEXTEST = 0;
        let target = null;
        if (this.algo === 'rr') {
          options.push(options.shift());
          while (!options[0].active) options.push(options.shift());
          target = options[0];
        } else if (this.algo === 'lc') {
          // console.log('here')
          while (!options[0].active) options.push(options.shift());
          const min = {};
          min.reqs = options[0].openRequests;
          min.option = 0;
          for (let i = 1; i < options.length; i += 1) {
            // console.log(options[i].openRequests);
            if (options[i].openRequests < min.reqs && options[i].active) {
              min.reqs = options[i].openRequests;
              min.option = i;
              INDEXTEST = i;
              // console.log(min);
            }
          }
          target = options[min.option];
        }

        // options[0].method = bReq.method;
        // options[0].path = bReq.url;
        // options[0].headers = bReq.headers;
        const serverOptions = {};
        serverOptions.method = bReq.method;
        serverOptions.path = bReq.url;
        serverOptions.headers = bReq.headers;
        serverOptions.hostname = target.hostname;
        serverOptions.port = target.port;

        // Call origin server!!!!!!
        console.log(INDEXTEST, options[0].openRequests, options[1].openRequests, options[2].openRequests);
        target.openRequests += 1;
        const originServer = http.request(serverOptions, (sRes) => {
          // console.log('connected');
          sRes.on('data', (data) => {
            // if (err) {--------------- DARRICK PLS :)
            //   err.name = 'Server Response on Data Error'
            //   errorLog.write(err);
            // }
            body += data;
            // bRes.write(data);
          });
          sRes.on('end', (err) => {
            if (err) errorLog.write(err);
            target.openRequests -= 1;
            this.cacheContent(body, cache, bReq, routes);
            // console.log(cache);

            if (sRes.headers['set-cookie']) {
              // console.log(sRes.headers['set-cookie'][0]);
              // bRes.writeHead(sRes.headers);
              bRes.writeHead(200, {
                'Set-Cookie': sRes.headers['set-cookie'][0],
              });
            }
            bRes.end(body);
          });
        });
        originServer.on('error', e => {
          e.name = 'Target Server Error';
          errorLog.write(e);
        })
        bReq.pipe(originServer);
        // originServer.end();
      }
    }
  }

  lbInit(options, cb) {
    this.options = options;
    cb();
    return this;
  }
}

const loadBalancer = new LoadBalancer();

module.exports = loadBalancer.lbInit;
<commit_msg>added http/https toggling functionality<commit_after>const https = require('https');
const http = require('http');
const EventEmitter = require('events');
const fs = require('fs');
const errorLog = require('./errorLog');

class LoadBalancer extends EventEmitter {
  constructor() {
    super();
    this.algo = 'lc';
    this.cache = {};
    this.options = [];
    this.routes = {};
    this.addOptions = this.addOptions.bind(this);
    this.setRoutes = this.setRoutes.bind(this);
    this.healthCheck = this.healthCheck.bind(this);
    this.clearCache = this.clearCache.bind(this);
    this.isStatic = this.isStatic.bind(this);
    this.shouldCache = this.shouldCache.bind(this);
    this.cacheContent = this.cacheContent.bind(this);
    this.init = this.init.bind(this);
    this.lbInit = this.lbInit.bind(this);
  };

  setAlgoRR() {
    this.algo = 'rr';
  }

  setAlgoLC() {
    this.algo = 'lc';
  }

  setRoutes(routes) {
    for (let i = 0; i < routes.length; i++) {
      let temp = routes[i][0].concat(routes[i][1]);
      this.routes[temp] = true;
    }
  };

  addOptions(options) {
    for (let i = 1; i < options.length; i += 1) {
      this.options.push(options[i]);
    }
  };

  healthCheck(interval = null) {
    /*
  15 minute interval healthcheck sends dummy get request to servers(ports) to check server health
  alters 'active' boolean value based on result of health check
    */
    // loops through servers in options & sends mock get request to each
    const options = this.options;

    for (let i = 0; i < options.length; i += 1) {
      http.get(options[i], (res) => {
        if (res.statusCode > 100 && res.statusCode < 400) {
          if (options[i].active === false) options[i].active = true;
        } else {
          options[i].active = false;
        }
        res.on('end', () => {
          // response from server received, reset value to true if prev false
          if (options[i].active === false) options[i].active = true;
        });
      }).on('error', (e) => {
        e.name = "HealthCheck Error";
        errorLog.write(e);
        // if error occurs, set boolean of 'active' to false to ensure no further requests to server
        if (e) {
          options[i].active = false;
        }
      });
    }
    if (interval !== null) {
      setTimeout(() => {
        this.healthCheck(interval);
      }, interval);
    }
  }

  healthCheckForHTTPS(interval = null) {
    /*
  15 minute interval healthcheck sends dummy get request to servers(ports) to check server health
  alters 'active' boolean value based on result of health check
    */
    // loops through servers in options & sends mock get request to each
    const options = this.options;

    for (let i = 0; i < options.length; i += 1) {
      https.get(options[i], (res) => {
        if (res.statusCode > 100 && res.statusCode < 400) {
          if (options[i].active === false) options[i].active = true;
        } else {
          options[i].active = false;
        }
        res.on('end', () => {
          // response from server received, reset value to true if prev false
          if (options[i].active === false) options[i].active = true;
        });
      }).on('error', (e) => {
        e.name = "HealthCheck Error";
        errorLog.write(e);
        // if error occurs, set boolean of 'active' to false to ensure no further requests to server
        if (e) {
          options[i].active = false;
        }
      });
    }
    if (interval !== null) {
      setTimeout(() => {
        this.healthCheck(interval);
      }, interval);
    }
  };

  clearCache(interval = null) {
    this.cache = {};
    if (interval !== null) {
      setTimeout(() => {
        this.clearCache(this.cache, interval);
      }, interval);
    }
  };

  isStatic(bReq) {
    // if file is html/css/javascript
    return bReq.url.slice(bReq.url.length - 5) === '.html' || bReq.url.slice(bReq.url.length - 4) === '.css' || bReq.url.slice(bReq.url.length - 3) === '.js';
      // flag variable set to true to enable caching before sending response to browser
  };

  shouldCache(bReq, routes) {
    // user input 'all' to allow cacheEverything method to always work
    // if (bReq === 'all') return true;
    // console.log(routes);
    return this.isStatic(bReq) || routes[bReq.method + bReq.url];
  };

  cacheContent(body, cache, bReq, routes) {
    // console.log(loadBalancer.shouldCache(bReq, routes));
    if (this.shouldCache(bReq, routes)) {
      // cache response
      cache[bReq.method + bReq.url] = body;
    }
  };

  insecureHTTP(options, body, target, cache, routes, bReq, bRes) {
    return http.request(options, (sRes) => {
      // console.log('connected');
      sRes.on('data', (data) => {
        // if (err) {--------------- DARRICK PLS :)
        //   err.name = 'Server Response on Data Error'
        //   errorLog.write(err);
        // }
        body += data;
        // bRes.write(data);
      });
      sRes.on('end', (err) => {
        if (err) errorLog.write(err);
        target.openRequests -= 1;
        this.cacheContent(body, cache, bReq, routes);
        // console.log(cache);

        if (sRes.headers['set-cookie']) {
          // console.log(sRes.headers['set-cookie'][0]);
          // bRes.writeHead(sRes.headers);
          bRes.writeHead(200, {
            'Set-Cookie': sRes.headers['set-cookie'][0],
          });
        }
        bRes.end(body);
      });
    });
  }

  secureHTTP(options, body, target, cache, routes, bReq, bRes) {
    return https.request(options, (sRes) => {
      sRes.on('data', (data) => {
        body += data;
        // bRes.write(data);
      });
      sRes.on('end', (err) => {
        if (err) errorLog.write(err);
        target.openRequests -= 1;
        this.cacheContent(body, cache, bReq, routes);
        // console.log(cache);

        if (sRes.headers['set-cookie']) {
          //console.log(sRes.headers['set-cookie'][0]);
          //bRes.writeHead(sRes.headers);
          bRes.writeHead(200, {
            'Set-Cookie': sRes.headers['set-cookie'][0],
          });
        }
        bRes.end(body);
      });
    });
  };

  init(bReq, bRes, secureHTTP = null) {
    const options = this.options;
    const cache = this.cache;
    const routes = this.routes;

    if (cache[bReq.method + bReq.url]) {
      // STATS GATHERING
      // statsController.countRequests('lb');
      this.emit('cacheRes');

      bRes.end(cache[bReq.method + bReq.url]);
    } else {
      // STATS GATHERING
      // statsController.countRequests(options[0].hostname.concat(':').concat(options[0].port));
      this.emit('targetRes');
      let body = '';
      // check for valid request & edge case removes request to '/favicon.ico'
      if (bReq.url !== null && bReq.url !== '/favicon.ico') {
        // console.log('before options used: ', options);
        let INDEXTEST = 0;
        let target = null;
        if (this.algo === 'rr') {
          options.push(options.shift());
          while (!options[0].active) options.push(options.shift());
          target = options[0];
        } else if (this.algo === 'lc') {
          while (!options[0].active) options.push(options.shift());
          const min = {};
          min.reqs = options[0].openRequests;
          min.option = 0;
          for (let i = 1; i < options.length; i += 1) {
            // console.log(options[i].openRequests);
            if (options[i].openRequests < min.reqs && options[i].active) {
              min.reqs = options[i].openRequests;
              min.option = i;
              INDEXTEST = i;
              // console.log(min);
            }
          }
          target = options[min.option];
        }
        // this is where https will have to happen
        // Call origin server!!!!!!

        // const secureOpts = {
        //   hostname: target.hostname,
        //   port: target.port,
        //   path: bReq.url,
        //   headers: bReq.headers,
        //   method: bReq.method,
        //   key: fs.readFileSync('server-key.pem'),
        //   cert: fs.readFileSync('server-crt.pem'),
        //   ca: fs.readFileSync('ca-crt.pem'),
        // };
        const serverOptions = {};
        serverOptions.method = bReq.method;
        serverOptions.path = bReq.url;
        serverOptions.headers = bReq.headers;
        serverOptions.hostname = target.hostname;
        serverOptions.port = target.port;

        target.openRequests += 1;
        // console.log(options);
        let originServer;
        if (secureHTTP) {
          const secureKeys = Object.keys(secureHTTP);
          for (let i = 0; i < secureKeys.length; i += 1) serverOptions[secureKeys[i]] = secureHTTP[secureKeys[i]];
          originServer = this.secureHTTP(serverOptions, body, target, cache, routes, bReq, bRes);
        } else {
          originServer = this.insecureHTTP(serverOptions, body, target, cache, routes, bReq, bRes);
        }
        originServer.on('error', e => {
          e.name = 'Target Server Error';
          errorLog.write(e);
        });
        bReq.pipe(originServer);
        // originServer.end();
      }
    }
  }

  lbInit(options, cb) {
    this.options = options;
    cb();
    return this;
  }
}

const loadBalancer = new LoadBalancer();

module.exports = loadBalancer.lbInit;
<|endoftext|>