<commit_before>const service = require('feathers-mongoose');
const { Forbidden, NotFound } = require('@feathersjs/errors');
const logger = require('winston');
const { newsModel, newsHistoryModel } = require('./model');
const hooks = require('./hooks');
const newsModelHooks = require('./hooks/newsModel.hooks');
const { flatten, paginate, sort } = require('../../utils/array');

const NEWS_PERMISSIONS = {
	NEWS_VIEW: 'view',
	NEWS_EDIT: 'edit',
};

class NewsService {
	setup(app) {
		this.app = app;
	}

	async getPermissions(userId, dataItem) {
		// scope case: user role in scope must have given permission
		if (dataItem.target && dataItem.targetModel) {
			const scope = this.app.service(`${dataItem.targetModel}/:scopeId/userPermissions/`);
			const params = { route: { scopeId: dataItem.target.toString() } };
			const scopePermissions = await scope.get(userId, params);
			return scopePermissions;
		}

		// default school case: dataItem and users schoolId must match and user permission must exist
		return this.getSchoolPermissions(userId, dataItem.schoolId);
	}

	/**
		 * Checks scoped permission for a user and given news.
		 * @param {BsonId|String} userId
		 * @param {String} permission
		 * @param {BsonId|String} targetId (optional) news target (scope) id. Only valid with targetModel.
		 * @param {String} targetModel (optional) news target (scope) model. Only valid with target.
		 * @returns {Promise<Boolean>} Promise that resolves to true/false
		 * @example
		 * await hasPermission(user._id, 'NEWS_VIEW') => true
		 * await hasPermission(user._id, 'NEWS_CREATE', team._id, 'teams') => false
		 * @memberof NewsService
		 */
	async hasPermission(userId, permission, news) {
		if (!news) {
			// maybe, use hasSchoolPermission instead...
			return false;
		}
		const permissions = await this.getPermissions(userId, news);
		return permissions.includes(permission);
	}

	/**
	 * Tests if a user with given userId has a (global) permission within a school-/scope/.
	 * @param {*} userId
	 * @param {*} schoolId
	 * @param {*} permission
	 * @returns {boolean}
	 */
	async getSchoolPermissions(userId, schoolId) {
		// test user exists
		const user = await this.app.service('users').get(userId);
		if (user == null) return false;

		// test user is school member
		const sameSchool = schoolId.toString() === user.schoolId.toString();
		if (!sameSchool) return false;

		// finally, test user has permission
		return user.permissions;
	}

	async hasSchoolPermission(userId, schoolId, permision) {
		const permissions = await this.getSchoolPermissions(userId, schoolId);
		return permissions.includes(permision);
	}

	/**
	 * Throw an error if the user is not allowed to perform the given operation
	 * @param {News} news news item (required: {schoolId}, optional: {target, targetModel})
	 * @param {Object} { userId, schoolId }
	 * @param {String} permission permision to check
	 * @returns undefined
	 * @example
	 * await authorize(news, params.account, 'NEWS_VIEW') => undefined
	 * await authorize(news, params.account, 'NEWS_CREATE') => (throws Forbidden)
	 * @memberof NewsService
	 */
	async authorize(news = {}, userId, permission) {
		const authorized = await this.hasPermission(userId, permission, news);
		if (!authorized) {
			throw new Forbidden('Not authorized.');
		}
	}

	async getDataItemPermissions(dataItem, userId, permissions) {
		const retValue = [];
		const itemPermissions = await this.getPermissions(userId, dataItem);
		for (const key of Object.keys(permissions)) {
			if (itemPermissions.includes(key)
				&& !retValue.includes(permissions[key])) {
				retValue.push(permissions[key]);
			}
		}
		return retValue;
	}

	checkExistence(news, query) {
		if (!news) {
			logger.error(`Cannot find news item with query "${query}" => "${news}"`);
			throw new NotFound('News item does not exist.');
		}
	}

	/**
	 * Returns all school news the user is allowed to see.
	 * @param {Object} { userId, schoolId } -- The user's Id and schoolId
	 * @returns Array<News Document>
	 * @memberof NewsService
	 */
	findSchoolNews({ userId, schoolId }) {
		return this.hasSchoolPermission(userId, schoolId, 'NEWS_VIEW')
			.then((hasPermission) => {
				if (!hasPermission) {
					return [];
				}
				const query = { schoolId, target: { $exists: false } };
				return this.app.service('newsModel').find({ query, paginate: false });
			}).then(news => Promise.all(news.map(async n => ({
				...n,
				permissions: await this.getDataItemPermissions(n, userId, NEWS_PERMISSIONS),
			}))))
			.catch((err) => {
				logger.error('Cannot find school news', err);
				return [];
			});
	}

	/**
	 * Returns scoped news the user is allowed to see
	 *
	 * @param {BsonId|String} userId the user's Id
	 * @param {BsonId|String} target (optional) Id of the news target (course, team, etc.)
	 * @returns Array<News Document>
	 * @memberof NewsService
	 */
	async findScopedNews(userId, target) {
		const scopes = await newsModel.distinct('targetModel');
		const ops = scopes.map(async (scope) => {
			// For each possible target model, find all targets the user has NEWS_VIEW permissions in.
			const scopeListService = this.app.service(`/users/:scopeId/${scope}`);
			if (scopeListService === undefined) {
				throw new Error(`Missing ScopeListService for scope "${scope}".`);
			}
			let scopeItems = await scopeListService.find({
				route: { scopeId: userId.toString() },
				query: { permissions: ['NEWS_VIEW'] },
			});
			if (target) {
				// if a target id is given, only return news from this target
				scopeItems = scopeItems.filter(i => i._id.toString() === target.toString());
			}
			return Promise.all(scopeItems.map(async (item) => {
				const query = {
					targetModel: scope,
					target: item._id,
					$populate: target,
				};
				const news = await this.app.service('newsModel').find({ query, paginate: false });
				// Manually populate the target (current API requires this):
				return Promise.all(news.map(async n => ({
					...n,
					target: await this.app.service(scope).get(n.target),
					permissions: await this.getDataItemPermissions(n, userId, NEWS_PERMISSIONS),
				})));
			}));
		});
		const results = await Promise.all(ops);
		return flatten(results);
	}

	/**
	 * Create a copy of the original news if it is edited
	 * @param {News} oldItem
	 * @returns {Promise<NewsHistory>} the created news history document
	 * @memberof NewsService
	 * @static
	 */
	static createHistoryEntry(oldItem) {
		const historyEntry = {
			title: oldItem.title,
			content: oldItem.content,
			displayAt: oldItem.displayAt,
			creatorId: oldItem.updaterId ? oldItem.updaterId : oldItem.creatorId,
			parentId: oldItem._id,
		};
		return newsHistoryModel.create(historyEntry);
	}

	/**
	 * GET /news/{id}
	 * Returns the news item specified by id
	 * @param {BsonId|String} id
	 * @param {Object} params
	 * @returns one news item
	 * @throws {Forbidden} if not authorized
	 * @throws {NotFound} if the id does not belong to a news object
	 * @memberof NewsService
	 */
	async get(id, params) {
		const news = await this.app.service('newsModel').get(id);
		this.checkExistence(news, id);
		await this.authorize(news, params.account.userId, 'NEWS_VIEW');
		news.permissions = await this.getDataItemPermissions(news, params.account.userId, NEWS_PERMISSIONS);
		return news;
	}

	/**
	 * GET /news/
	 * Returns all news the user can see.
	 * @param {Object} params
	 * @returns paginated array of news items
	 * @memberof NewsService
	 */
	async find(params) {
		let news = [];
		const scoped = params.query && params.query.target;
		if (scoped) {
			news = news.concat(await this.findScopedNews(params.account.userId, params.query.target));
		} else {
			news = news.concat(await this.findSchoolNews(params.account));
			news = news.concat(await this.findScopedNews(params.account.userId));
		}
		if (params.query) {
			news = sort(news, params.query.$sort);
		}
		// paginate by default, but let $paginate=false through
		news = paginate(news, { $paginate: true, ...params.query });
		return Promise.resolve(news);
	}

	/**
	 * POST /news/
	 * Creates a news item
	 * @param {Object} data @see NewsModel
	 * @param {Object} params
	 * @returns {News} the created news object
	 * @throws {Forbidden} if not authorized
	 * @memberof NewsService
	 */
	async create(data, params) {
		await this.authorize(data, params.account.userId, 'NEWS_CREATE');
		return this.app.service('newsModel').create(data);
	}

	/**
	 * DELETE /news/{id}
	 * Deletes a single news item.
	 * @param {BsonId|String} id The news item's Id
	 * @param {Object} params Note that params.query won't work here
	 * @returns {News} the deleted news item
	 * @throws {Forbidden} if not authorized
	 * @throws {NotFound} if the id does not belong to a news object
	 * @memberof NewsService
	 */
	async remove(id, params) {
		const news = await this.app.service('newsModel').get(id);
		this.checkExistence(news, id);
		await this.authorize(news, params.account.userId, 'NEWS_CREATE');
		await this.app.service('newsModel').remove(id);
		return news;
	}

	/**
	 * PUT /news/{id}
	 * Replaces a single news item
	 * @param {BsonId|String} id The news item's Id
	 * @param {News} data updated news item
	 * @param {Object} params Feathers request params (note that using params.query won't work here)
	 * @returns {News} updated news item
	 * @throws {Forbidden} if not authorized
	 * @throws {NotFound} if the id does not belong to a news object
	 * @memberof NewsService
	 */
	async update(id, data, params) {
		const news = await this.app.service('newsModel').get(id);
		this.checkExistence(news, id);
		await this.authorize(news, params.account.userId, 'NEWS_EDIT');
		const updatedNews = await this.app.service('newsModel').update(id, data);
		await NewsService.createHistoryEntry(news);
		return updatedNews;
	}

	/**
	 * PATCH /news/{id}
	 * Patches a single news item
	 * @param {BsonId|String} id The news item's Id
	 * @param {Object} data object containing updated news item attributes
	 * @param {Object} params Feathers request params (note that using params.query won't work here)
	 * @returns {News} patched news item
	 * @throws {Forbidden} if not authorized
	 * @throws {NotFound} if the id does not belong to a news object
	 * @memberof NewsService
	 */
	async patch(id, data, params) {
		const news = await this.app.service('newsModel').get(id);
		this.checkExistence(news, id);
		await this.authorize(news, params.account.userId, 'NEWS_EDIT');
		const patchedNews = await this.app.service('newsModel').patch(id, data);
		await NewsService.createHistoryEntry(news);
		return patchedNews;
	}
}

module.exports = function news() {
	const app = this;

	// use /news to access a user's news
	app.use('/news', new NewsService());
	app.service('news').hooks(hooks);

	// use /newsModel to directly access the model from other services
	// (external requests are blocked)
	app.use('/newsModel', service({
		Model: newsModel,
		lean: true,
		paginate: {
			default: 25,
		},
	}));
	app.service('/newsModel').hooks(newsModelHooks);
};
<commit_msg>test fixing, inconsistent return, remove permission renaming<commit_after>const service = require('feathers-mongoose');
const { Forbidden, NotFound } = require('@feathersjs/errors');
const logger = require('winston');
const { newsModel, newsHistoryModel } = require('./model');
const hooks = require('./hooks');
const newsModelHooks = require('./hooks/newsModel.hooks');
const { flatten, paginate, sort } = require('../../utils/array');

class NewsService {
	setup(app) {
		this.app = app;
	}

	async getPermissions(userId, dataItem) {
		// scope case: user role in scope must have given permission
		if (dataItem.target && dataItem.targetModel) {
			const scope = this.app.service(`${dataItem.targetModel}/:scopeId/userPermissions/`);
			const params = { route: { scopeId: dataItem.target.toString() } };
			const scopePermissions = await scope.get(userId, params);
			return scopePermissions;
		}

		// default school case: dataItem and users schoolId must match and user permission must exist
		return this.getSchoolPermissions(userId, dataItem.schoolId);
	}

	/**
		 * Checks scoped permission for a user and given news.
		 * @param {BsonId|String} userId
		 * @param {String} permission
		 * @param {BsonId|String} targetId (optional) news target (scope) id. Only valid with targetModel.
		 * @param {String} targetModel (optional) news target (scope) model. Only valid with target.
		 * @returns {Promise<Boolean>} Promise that resolves to true/false
		 * @example
		 * await hasPermission(user._id, 'NEWS_VIEW') => true
		 * await hasPermission(user._id, 'NEWS_CREATE', team._id, 'teams') => false
		 * @memberof NewsService
		 */
	async hasPermission(userId, permission, news) {
		if (!news) {
			// maybe, use hasSchoolPermission instead...
			return false;
		}
		const permissions = await this.getPermissions(userId, news);
		return permissions.includes(permission);
	}

	/**
	 * Tests if a user with given userId has a (global) permission within a school-/scope/.
	 * @param {ObjectId} userId
	 * @param {ObjectId} schoolId
	 * @returns {Array<String>}
	 */
	async getSchoolPermissions(userId, schoolId) {
		// test user exists
		const user = await this.app.service('users').get(userId);
		if (user == null) return [];

		// test user is school member
		const sameSchool = schoolId.toString() === user.schoolId.toString();
		if (!sameSchool) return [];

		// finally, test user has permission
		return user.permissions;
	}

	async hasSchoolPermission(userId, schoolId, permision) {
		const permissions = await this.getSchoolPermissions(userId, schoolId);
		return permissions.includes(permision);
	}

	/**
	 * Throw an error if the user is not allowed to perform the given operation
	 * @param {News} news news item (required: {schoolId}, optional: {target, targetModel})
	 * @param {Object} { userId, schoolId }
	 * @param {String} permission permision to check
	 * @returns undefined
	 * @example
	 * await authorize(news, params.account, 'NEWS_VIEW') => undefined
	 * await authorize(news, params.account, 'NEWS_CREATE') => (throws Forbidden)
	 * @memberof NewsService
	 */
	async authorize(news = {}, userId, permission) {
		const authorized = await this.hasPermission(userId, permission, news);
		if (!authorized) {
			throw new Forbidden('Not authorized.');
		}
	}

	checkExistence(news, query) {
		if (!news) {
			logger.error(`Cannot find news item with query "${query}" => "${news}"`);
			throw new NotFound('News item does not exist.');
		}
	}

	/**
	 * Returns all school news the user is allowed to see.
	 * @param {Object} { userId, schoolId } -- The user's Id and schoolId
	 * @returns Array<News Document>
	 * @memberof NewsService
	 */
	findSchoolNews({ userId, schoolId }) {
		return this.hasSchoolPermission(userId, schoolId, 'NEWS_VIEW')
			.then((hasPermission) => {
				if (!hasPermission) {
					return [];
				}
				const query = { schoolId, target: { $exists: false } };
				return this.app.service('newsModel').find({ query, paginate: false });
			}).then(news => Promise.all(news.map(async n => ({
				...n,
				permissions: await this.getPermissions(userId, n),
			}))))
			.catch((err) => {
				logger.error('Cannot find school news', err);
				return [];
			});
	}

	/**
	 * Returns scoped news the user is allowed to see
	 *
	 * @param {BsonId|String} userId the user's Id
	 * @param {BsonId|String} target (optional) Id of the news target (course, team, etc.)
	 * @returns Array<News Document>
	 * @memberof NewsService
	 */
	async findScopedNews(userId, target) {
		const scopes = await newsModel.distinct('targetModel');
		const ops = scopes.map(async (scope) => {
			// For each possible target model, find all targets the user has NEWS_VIEW permissions in.
			const scopeListService = this.app.service(`/users/:scopeId/${scope}`);
			if (scopeListService === undefined) {
				throw new Error(`Missing ScopeListService for scope "${scope}".`);
			}
			let scopeItems = await scopeListService.find({
				route: { scopeId: userId.toString() },
				query: { permissions: ['NEWS_VIEW'] },
			});
			if (target) {
				// if a target id is given, only return news from this target
				scopeItems = scopeItems.filter(i => i._id.toString() === target.toString());
			}
			return Promise.all(scopeItems.map(async (item) => {
				const query = {
					targetModel: scope,
					target: item._id,
					$populate: target,
				};
				const news = await this.app.service('newsModel').find({ query, paginate: false });
				// Manually populate the target (current API requires this):
				return Promise.all(news.map(async n => ({
					...n,
					target: await this.app.service(scope).get(n.target),
					permissions: await this.getPermissions(userId, n),
				})));
			}));
		});
		const results = await Promise.all(ops);
		return flatten(results);
	}

	/**
	 * Create a copy of the original news if it is edited
	 * @param {News} oldItem
	 * @returns {Promise<NewsHistory>} the created news history document
	 * @memberof NewsService
	 * @static
	 */
	static createHistoryEntry(oldItem) {
		const historyEntry = {
			title: oldItem.title,
			content: oldItem.content,
			displayAt: oldItem.displayAt,
			creatorId: oldItem.updaterId ? oldItem.updaterId : oldItem.creatorId,
			parentId: oldItem._id,
		};
		return newsHistoryModel.create(historyEntry);
	}

	/**
	 * GET /news/{id}
	 * Returns the news item specified by id
	 * @param {BsonId|String} id
	 * @param {Object} params
	 * @returns one news item
	 * @throws {Forbidden} if not authorized
	 * @throws {NotFound} if the id does not belong to a news object
	 * @memberof NewsService
	 */
	async get(id, params) {
		const news = await this.app.service('newsModel').get(id);
		this.checkExistence(news, id);
		await this.authorize(news, params.account.userId, 'NEWS_VIEW');
		news.permissions = await this.getPermissions(params.account.userId, news);
		return news;
	}

	/**
	 * GET /news/
	 * Returns all news the user can see.
	 * @param {Object} params
	 * @returns paginated array of news items
	 * @memberof NewsService
	 */
	async find(params) {
		let news = [];
		const scoped = params.query && params.query.target;
		if (scoped) {
			news = news.concat(await this.findScopedNews(params.account.userId, params.query.target));
		} else {
			news = news.concat(await this.findSchoolNews(params.account));
			news = news.concat(await this.findScopedNews(params.account.userId));
		}
		if (params.query) {
			news = sort(news, params.query.$sort);
		}
		// paginate by default, but let $paginate=false through
		news = paginate(news, { $paginate: true, ...params.query });
		return Promise.resolve(news);
	}

	/**
	 * POST /news/
	 * Creates a news item
	 * @param {Object} data @see NewsModel
	 * @param {Object} params
	 * @returns {News} the created news object
	 * @throws {Forbidden} if not authorized
	 * @memberof NewsService
	 */
	async create(data, params) {
		await this.authorize(data, params.account.userId, 'NEWS_CREATE');
		return this.app.service('newsModel').create(data);
	}

	/**
	 * DELETE /news/{id}
	 * Deletes a single news item.
	 * @param {BsonId|String} id The news item's Id
	 * @param {Object} params Note that params.query won't work here
	 * @returns {News} the deleted news item
	 * @throws {Forbidden} if not authorized
	 * @throws {NotFound} if the id does not belong to a news object
	 * @memberof NewsService
	 */
	async remove(id, params) {
		const news = await this.app.service('newsModel').get(id);
		this.checkExistence(news, id);
		await this.authorize(news, params.account.userId, 'NEWS_CREATE');
		await this.app.service('newsModel').remove(id);
		return news;
	}

	/**
	 * PUT /news/{id}
	 * Replaces a single news item
	 * @param {BsonId|String} id The news item's Id
	 * @param {News} data updated news item
	 * @param {Object} params Feathers request params (note that using params.query won't work here)
	 * @returns {News} updated news item
	 * @throws {Forbidden} if not authorized
	 * @throws {NotFound} if the id does not belong to a news object
	 * @memberof NewsService
	 */
	async update(id, data, params) {
		const news = await this.app.service('newsModel').get(id);
		this.checkExistence(news, id);
		await this.authorize(news, params.account.userId, 'NEWS_EDIT');
		const updatedNews = await this.app.service('newsModel').update(id, data);
		await NewsService.createHistoryEntry(news);
		return updatedNews;
	}

	/**
	 * PATCH /news/{id}
	 * Patches a single news item
	 * @param {BsonId|String} id The news item's Id
	 * @param {Object} data object containing updated news item attributes
	 * @param {Object} params Feathers request params (note that using params.query won't work here)
	 * @returns {News} patched news item
	 * @throws {Forbidden} if not authorized
	 * @throws {NotFound} if the id does not belong to a news object
	 * @memberof NewsService
	 */
	async patch(id, data, params) {
		const news = await this.app.service('newsModel').get(id);
		this.checkExistence(news, id);
		await this.authorize(news, params.account.userId, 'NEWS_EDIT');
		const patchedNews = await this.app.service('newsModel').patch(id, data);
		await NewsService.createHistoryEntry(news);
		return patchedNews;
	}
}

module.exports = function news() {
	const app = this;

	// use /news to access a user's news
	app.use('/news', new NewsService());
	app.service('news').hooks(hooks);

	// use /newsModel to directly access the model from other services
	// (external requests are blocked)
	app.use('/newsModel', service({
		Model: newsModel,
		lean: true,
		paginate: {
			default: 25,
		},
	}));
	app.service('/newsModel').hooks(newsModelHooks);
};
<|endoftext|>


<commit_before>import { createStore } from 'redux'
import reducer from './counter-app/reducer'

export default createStore(reducer)
<commit_msg>Make sure Redux Devtools is finding the store (#894)<commit_after>import { createStore } from 'redux'
import reducer from './counter-app/reducer'

export default createStore(
  reducer,
  typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
<|endoftext|>


<commit_before>define([
	'goo/loaders/handlers/ConfigHandler',
	'goo/renderer/Material',
	'goo/renderer/Util',
	'goo/renderer/shaders/ShaderLib',
	'goo/util/rsvp',
	'goo/util/PromiseUtil',
	'goo/util/ObjectUtil'
],
/** @lends */
function(
	ConfigHandler,
	Material,
	Util,
	ShaderLib,
	RSVP,
	PromiseUtil,
	_
) {
	"use strict";

	/**
	 * @class Handler for loading materials into engine
	 * @extends ConfigHandler
	 * @param {World} world
	 * @param {Function} getConfig
	 * @param {Function} updateObject
	 * @private
	 */
	function MaterialHandler() {
		ConfigHandler.apply(this, arguments);
	}

	MaterialHandler.prototype = Object.create(ConfigHandler.prototype);
	MaterialHandler.prototype.constructor = MaterialHandler;
	ConfigHandler._registerClass('material', MaterialHandler);

	MaterialHandler.ENGINE_SHADER_PREFIX = "GOO_ENGINE_SHADERS/";

	/**
	 * Preparing material config by populating it with defaults.
	 * @param {object} config
	 * @private
	 */
	MaterialHandler.prototype._prepare = function(config) {
		if (!config.blendState) {
			config.blendState = {};
		}
		_.defaults(config.blendState, {
			blending: 'NoBlending',
			blendEquation: 'AddEquation',
			blendSrc: 'SrcAlphaFactor',
			blendDst: 'OneMinusSrcAlphaFactor'
		});

		if (!config.cullState) {
			config.cullState = {};
		}
		_.defaults(config.cullState, {
			enabled: true,
			cullFace: 'Back',
			frontFace: 'CCW'
		});

		if (!config.depthState) {
			config.depthState = {};
		}
		_.defaults(config.depthState, {
			enabled: true,
			write: true
		});

		if (config.renderQueue === null || config.renderQueue === undefined) {
			config.renderQueue = -1;
		}
		if (config.dualTransparency === null || config.dualTransparency === undefined) {
			config.dualTransparency = false;
		}
		config.wireframe = false;
		config.flat = false;
	};

	/**
	 * Creates a (somewhat) empty material.
	 * @returns {Material}
	 * @private
	 */
	MaterialHandler.prototype._create = function() {
		return new Material();
	};

	/**
	 * Adds/updates/removes a a material
	 * @param {string} ref
	 * @param {object|null} config
	 * @param {object} options
	 * @returns {RSVP.Promise} Resolves with the updated material or null if removed
	 */
	MaterialHandler.prototype._update = function(ref, config, options) {
		var that = this;
		return ConfigHandler.prototype._update.call(this, ref, config, options).then(function(material) {
			if (!material) { return; }
			var promises = [];
			// Material settings
			_.extend(material.blendState, config.blendState);
			_.extend(material.cullState, config.cullState);
			_.extend(material.depthState, config.depthState);

			material.id = config.id;
			material.name = config.name;
			material.wireframe = config.wireframe;
			material.flat = config.flat;
			material.dualTransparency = config.dualTransparency;
			if (config.renderQueue === -1) {
				material.renderQueue = null;
			} else {
				material.renderQueue = config.renderQueue;
			}
			material.uniforms = {};
			for (var name in config.uniforms) {
				if (config.uniforms[name].enabled === undefined) {
					material.uniforms[name] = _.clone(config.uniforms[name]);
				} else if (config.uniforms[name].enabled) {
					material.uniforms[name] = _.clone(config.uniforms[name].value);
				}
			}

			// Shader
			if (config.wireframe) {
				material.shader = Material.createShader(ShaderLib.simple);
			} else {
				var shaderRef = config.shaderRef;
				if (!shaderRef) {
					material.shader = Material.createShader(ShaderLib.texturedLit, 'DefaultShader');
				}
				else if (shaderRef.indexOf(MaterialHandler.ENGINE_SHADER_PREFIX) === 0) {
					var shaderName = shaderRef.slice(MaterialHandler.ENGINE_SHADER_PREFIX.length);
					material.shader = Material.createShader(ShaderLib[shaderName]);
				} else {
					var p = that._load(shaderRef, options).then(function(shader) {
						material.shader = shader;
					}).then(null, function(err) {
						throw new Error('Error loading shader: ' + err);
					});
					promises.push(p);
				}
			}

			// Textures
			function addTexture(type, ref, options) {
				return that._load(ref, options).then(function(texture) {
					if (texture.image) {
						material.setTexture(type, texture);
					} else {
						material.removeTexture(type);
					}
				}).then(null, function(err) {
					throw new Error('Error loading texture: ' + ref + ' - ' + err);
				});
			}
			var textureRef;
			for (var type in config.texturesMapping) {
				textureRef = config.texturesMapping[type];
				if(!textureRef || !textureRef.textureRef || textureRef.enabled === false) {
					material.removeTexture(type);
				} else {
					promises.push(addTexture(type, textureRef.textureRef, options));
				}
			}
			for (var type in material._textureMaps) {
				if (!config.texturesMapping[type]) {
					material.removeTexture(type);
				}
			}
			return RSVP.all(promises).then(function() {
				return material;
			});
		});
	};

	return MaterialHandler;
});
<commit_msg>Bugfix on #920<commit_after>define([
	'goo/loaders/handlers/ConfigHandler',
	'goo/renderer/Material',
	'goo/renderer/Util',
	'goo/renderer/shaders/ShaderLib',
	'goo/util/rsvp',
	'goo/util/PromiseUtil',
	'goo/util/ObjectUtil'
],
/** @lends */
function(
	ConfigHandler,
	Material,
	Util,
	ShaderLib,
	RSVP,
	PromiseUtil,
	_
) {
	"use strict";

	/**
	 * @class Handler for loading materials into engine
	 * @extends ConfigHandler
	 * @param {World} world
	 * @param {Function} getConfig
	 * @param {Function} updateObject
	 * @private
	 */
	function MaterialHandler() {
		ConfigHandler.apply(this, arguments);
	}

	MaterialHandler.prototype = Object.create(ConfigHandler.prototype);
	MaterialHandler.prototype.constructor = MaterialHandler;
	ConfigHandler._registerClass('material', MaterialHandler);

	MaterialHandler.ENGINE_SHADER_PREFIX = "GOO_ENGINE_SHADERS/";

	/**
	 * Preparing material config by populating it with defaults.
	 * @param {object} config
	 * @private
	 */
	MaterialHandler.prototype._prepare = function(config) {
		if (!config.blendState) {
			config.blendState = {};
		}
		_.defaults(config.blendState, {
			blending: 'NoBlending',
			blendEquation: 'AddEquation',
			blendSrc: 'SrcAlphaFactor',
			blendDst: 'OneMinusSrcAlphaFactor'
		});

		if (!config.cullState) {
			config.cullState = {};
		}
		_.defaults(config.cullState, {
			enabled: true,
			cullFace: 'Back',
			frontFace: 'CCW'
		});

		if (!config.depthState) {
			config.depthState = {};
		}
		_.defaults(config.depthState, {
			enabled: true,
			write: true
		});

		if (config.renderQueue === null || config.renderQueue === undefined) {
			config.renderQueue = -1;
		}
		if (config.dualTransparency === null || config.dualTransparency === undefined) {
			config.dualTransparency = false;
		}
		config.wireframe = false;
		config.flat = false;
	};

	/**
	 * Creates a (somewhat) empty material.
	 * @returns {Material}
	 * @private
	 */
	MaterialHandler.prototype._create = function() {
		return new Material();
	};

	/**
	 * Adds/updates/removes a a material
	 * @param {string} ref
	 * @param {object|null} config
	 * @param {object} options
	 * @returns {RSVP.Promise} Resolves with the updated material or null if removed
	 */
	MaterialHandler.prototype._update = function(ref, config, options) {
		var that = this;
		return ConfigHandler.prototype._update.call(this, ref, config, options).then(function(material) {
			if (!material) { return; }
			var promises = [];
			// Material settings
			_.extend(material.blendState, config.blendState);
			_.extend(material.cullState, config.cullState);
			_.extend(material.depthState, config.depthState);

			material.id = config.id;
			material.name = config.name;
			material.wireframe = config.wireframe;
			material.flat = config.flat;
			material.dualTransparency = config.dualTransparency;
			if (config.renderQueue === -1) {
				material.renderQueue = null;
			} else {
				material.renderQueue = config.renderQueue;
			}
			material.uniforms = {};
			for (var name in config.uniforms) {
				if (config.uniforms[name].enabled === undefined) {
					material.uniforms[name] = _.clone(config.uniforms[name]);
				} else if (config.uniforms[name].enabled) {
					material.uniforms[name] = _.clone(config.uniforms[name].value);
				}
			}

			// Shader
			if (config.wireframe) {
				material.shader = Material.createShader(ShaderLib.simple);
			} else {
				var shaderRef = config.shaderRef;
				if (!shaderRef) {
					material.shader = Material.createShader(ShaderLib.texturedLit, 'DefaultShader');
				}
				else if (shaderRef.indexOf(MaterialHandler.ENGINE_SHADER_PREFIX) === 0) {
					var shaderName = shaderRef.slice(MaterialHandler.ENGINE_SHADER_PREFIX.length);
					material.shader = Material.createShader(ShaderLib[shaderName]);
				} else {
					var p = that._load(shaderRef, options).then(function(shader) {
						material.shader = shader;
					}).then(null, function(err) {
						throw new Error('Error loading shader: ' + err);
					});
					promises.push(p);
				}
			}

			// Textures
			function addTexture(type, ref, options) {
				return that._load(ref, options).then(function(texture) {
					material.setTexture(type, texture);
				}).then(null, function(err) {
					throw new Error('Error loading texture: ' + ref + ' - ' + err);
				});
			}
			var textureRef;
			for (var type in config.texturesMapping) {
				textureRef = config.texturesMapping[type];
				if(!textureRef || !textureRef.textureRef || textureRef.enabled === false) {
					material.removeTexture(type);
				} else {
					promises.push(addTexture(type, textureRef.textureRef, options));
				}
			}
			for (var type in material._textureMaps) {
				if (!config.texturesMapping[type]) {
					material.removeTexture(type);
				}
			}
			return RSVP.all(promises).then(function() {
				return material;
			});
		});
	};

	return MaterialHandler;
});
<|endoftext|>


<commit_before>const expect = require('chai').expect
const getRejectionReasonId = require('../../../../app/services/data/get-rejection-reason-id')
const getRejectionReasons = require('../../../../app/services/data/get-rejection-reasons')
var rejectionReasons

describe('services/data/get-rejection-reason-id', function () {

  before(function () {
    return getRejectionReasons()
      .then(function (reasons){
        rejectionReasons = reasons
      })
  })

  it('should retrieve the rejection reason id for each given rejection reason', function (done) {
    rejectionReasons.forEach(function (reason){
      getRejectionReasonId(reason).then(function (id) {
        expect(id).to.eq(reason.id)
        done()
      })
    })
  })
})
<commit_msg>APVS0134: Fixing coding standards<commit_after>const expect = require('chai').expect
const getRejectionReasonId = require('../../../../app/services/data/get-rejection-reason-id')
const getRejectionReasons = require('../../../../app/services/data/get-rejection-reasons')
var rejectionReasons

describe('services/data/get-rejection-reason-id', function () {
  before(function () {
    return getRejectionReasons()
      .then(function (reasons) {
        rejectionReasons = reasons
      })
  })

  it('should retrieve the rejection reason id for each given rejection reason', function (done) {
    rejectionReasons.forEach(function (reason) {
      getRejectionReasonId(reason).then(function (id) {
        expect(id).to.eq(reason.id)
        done()
      })
    })
  })
})
<|endoftext|>


<commit_before>import { mount } from '@vue/test-utils'
import Textarea from '@/components/Textarea'
import { wait } from '../../../utils'

describe('components/Textarea', () => {
  it('should handle value prop with `null` value.', done => {
    let wrapper = mount(
      Textarea,
      {
        propsData: {
          value: null
        }
      },
      {
        attachToDocument: true,
        sync: false
      }
    )

    wrapper.vm.$on('input', val => {
      expect(val).to.equal('')

      wrapper.destroy()
      done()
    })

    wrapper.find('textarea').trigger('input')
  })

  it('should transparently pass-through attrs to the <textarea> element.', () => {
    let wrapper = mount(
      Textarea,
      {
        attrs: {
          autofocus: ''
        }
      },
      {
        sync: false
      }
    )

    expect(wrapper.find('textarea').element.autofocus).to.equal(true)
    wrapper.destroy()
  })

  it('should handle `resizable` correctly', () => {
    let wrapper = mount(
      {
        components: {
          'veui-textarea': Textarea
        },
        template: '<veui-textarea resizable/>'
      },
      {
        attachToDocument: true,
        sync: false
      }
    )

    let { element } = wrapper.find('textarea')
    expect(getComputedStyle(element).resize).to.equal('vertical')
    wrapper.destroy()
  })

  it('should render maxlength limit correctly', () => {
    let wrapper = mount({
      components: {
        'veui-textarea': Textarea
      },
      template: `
      <veui-textarea value="foo" maxlength="5"/>
    `
    })

    expect(wrapper.find('textarea').attributes('maxlength')).to.equal(undefined)
    expect(wrapper.find('.veui-textarea-count').text()).to.equal('3/5')
  })

  it('should render maxlength limit with strict prop correctly', () => {
    let wrapper = mount({
      components: {
        'veui-textarea': Textarea
      },
      template: `
      <veui-textarea value="foo" maxlength="5" strict/>
    `
    })

    expect(wrapper.find('textarea').attributes('maxlength')).to.equal('5')
    expect(wrapper.find('.veui-textarea-count').text()).to.equal('3/5')
  })

  it('should prevent content overlapping with counter', async () => {
    let wrapper = mount(
      {
        components: {
          'veui-textarea': Textarea
        },
        data () {
          return {
            val: '\nWWWWWWWWWWWWWWWWW'
          }
        },
        template: `
      <veui-textarea :value="val" line-number rows="2" maxlength="5"/>
    `
      },
      {
        attachToDocument: true,
        sync: false
      }
    )

    expect(wrapper.find('.veui-textarea-count-overlap').exists()).to.equal(
      false
    )

    wrapper.vm.val += 'W'
    await wait(0)

    expect(wrapper.find('.veui-textarea-count-overlap').exists()).to.equal(true)

    wrapper.destroy()
  })

  it('should make `value` prop fully controlled', async () => {
    let wrapper = mount(Textarea, {
      propsData: {
        value: 'foo'
      }
    })

    let input = wrapper.find('textarea')
    input.element.value = 'bar'
    input.trigger('input')
    await wrapper.vm.$nextTick()
    expect(input.element.value).to.equal('foo')
  })

  it('should pass event object on input', done => {
    let wrapper = mount(Textarea, {
      propsData: {
        value: 'foo'
      }
    })
    wrapper.vm.$on('input', (_, e) => {
      expect(!!e).to.equal(true)
      done()
    })

    let input = wrapper.find('textarea')
    input.element.value = 'bar'
    input.trigger('input')
  })

  it('should handle getLength prop correctly.', () => {
    let wrapper = mount({
      components: {
        'veui-textarea': Textarea
      },
      methods: {
        getLength () {
          return 4
        }
      },
      template: `
        <veui-textarea value="中文" :get-length="getLength" maxlength="5"/>
      `
    })

    expect(wrapper.find('.veui-textarea-count').text()).to.equal('4/5')
  })
})
<commit_msg>test: add more tests for textarea<commit_after>import { mount } from '@vue/test-utils'
import Textarea from '@/components/Textarea'
import { wait } from '../../../utils'

describe('components/Textarea', () => {
  it('should handle value prop with `null` value', done => {
    let wrapper = mount(
      Textarea,
      {
        propsData: {
          value: null
        }
      },
      {
        attachToDocument: true,
        sync: false
      }
    )

    wrapper.vm.$on('input', val => {
      expect(val).to.equal('')

      wrapper.destroy()
      done()
    })

    wrapper.find('textarea').trigger('input')
  })

  it('should transparently pass-through attrs to the <textarea> element', () => {
    let wrapper = mount(
      Textarea,
      {
        attrs: {
          autofocus: ''
        }
      },
      {
        sync: false
      }
    )

    expect(wrapper.find('textarea').element.autofocus).to.equal(true)
    wrapper.destroy()
  })

  it('should handle `resizable` correctly', () => {
    let wrapper = mount(
      {
        components: {
          'veui-textarea': Textarea
        },
        template: '<veui-textarea resizable/>'
      },
      {
        attachToDocument: true,
        sync: false
      }
    )

    let { element } = wrapper.find('textarea')
    expect(getComputedStyle(element).resize).to.equal('vertical')
    wrapper.destroy()
  })

  it('should render `maxlength` limit correctly', () => {
    let wrapper = mount({
      components: {
        'veui-textarea': Textarea
      },
      template: `
      <veui-textarea value="foo" maxlength="5"/>
    `
    })

    expect(wrapper.find('textarea').attributes('maxlength')).to.equal(undefined)
    expect(wrapper.find('.veui-textarea-count').text()).to.equal('3/5')
  })

  it('should render maxlength limit with strict prop correctly', () => {
    let wrapper = mount({
      components: {
        'veui-textarea': Textarea
      },
      template: `
      <veui-textarea value="foo" maxlength="5" strict/>
    `
    })

    expect(wrapper.find('textarea').attributes('maxlength')).to.equal('5')
    expect(wrapper.find('.veui-textarea-count').text()).to.equal('3/5')
  })

  it('should prevent content overlapping with counter', async () => {
    let wrapper = mount(
      {
        components: {
          'veui-textarea': Textarea
        },
        data () {
          return {
            val: '\nWWWWWWWWWWWWWWWWW'
          }
        },
        template: `
      <veui-textarea :value="val" line-number rows="2" maxlength="5"/>
    `
      },
      {
        attachToDocument: true,
        sync: false
      }
    )

    expect(wrapper.find('.veui-textarea-count-overlap').exists()).to.equal(
      false
    )

    wrapper.vm.val += 'W'
    await wait(0)

    expect(wrapper.find('.veui-textarea-count-overlap').exists()).to.equal(true)

    wrapper.destroy()
  })

  it('should make `value` prop fully controlled', async () => {
    let wrapper = mount(Textarea, {
      propsData: {
        value: 'foo'
      }
    })

    let input = wrapper.find('textarea')
    input.element.value = 'bar'
    input.trigger('input')
    await wrapper.vm.$nextTick()
    expect(input.element.value).to.equal('foo')
  })

  it('should pass event object on input', done => {
    let wrapper = mount(Textarea, {
      propsData: {
        value: 'foo'
      }
    })
    wrapper.vm.$on('input', (_, e) => {
      expect(!!e).to.equal(true)
      done()
    })

    let input = wrapper.find('textarea')
    input.element.value = 'bar'
    input.trigger('input')
  })

  it('should handle `getLength` prop correctly', () => {
    let wrapper = mount({
      components: {
        'veui-textarea': Textarea
      },
      methods: {
        getLength () {
          return 4
        }
      },
      template: `
        <veui-textarea value="中文" :get-length="getLength" maxlength="5"/>
      `
    })

    expect(wrapper.find('.veui-textarea-count').text()).to.equal('4/5')
  })

  it('should handle `autoresize` prop correctly', async () => {
    let wrapper = mount(
      {
        components: {
          'veui-textarea': Textarea
        },
        template: `
        <veui-textarea autoresize v-model="text"/>
      `,
        data () {
          return {
            text: '\n'.repeat(9)
          }
        }
      },
      {
        attachToDocument: true,
        sync: false
      }
    )

    let { vm, element } = wrapper

    await wait(0)
    let initialHeight = element.offsetHeight

    async function testRows (rows) {
      vm.text = '\n'.repeat(rows - 1)
      await wait(0)
      expect(Math.ceil(element.offsetHeight / initialHeight)).to.equal(
        rows / 10
      )
    }

    await testRows(100)
    await testRows(50)
    await testRows(120)
    await testRows(80)

    wrapper.destroy()
  })

  it('should be focusable', async () => {
    let wrapper = mount(
      {
        components: {
          'veui-textarea': Textarea
        },
        template: '<veui-textarea/>'
      },
      {
        attachToDocument: true,
        sync: false
      }
    )

    let { vm } = wrapper.find(Textarea)
    vm.focus()

    await wait(0)
    expect(document.activeElement).to.equal(wrapper.find('textarea').element)
    expect(wrapper.classes()).to.contain('veui-focus')

    wrapper.destroy()
  })
})
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import { withCurrentUser } from 'meteor/vulcan:core';
import { withRouter } from 'react-router';
import Users from 'meteor/vulcan:users';

export default function withAccess (options) {

  const { groups, redirect } = options;

  // we return a function that takes a component and itself returns a component
  return WrappedComponent => {
    class AccessComponent extends PureComponent {

      // if there are any groups defined check if user belongs, else just check if user exists
      canAccess = currentUser => {
        return groups ? currentUser && Users.isMemberOf(currentUser, groups) : currentUser;
      }

      // redirect on constructor if user cannot access
      constructor(props) {
        super(props);
        if(!this.canAccess(props.currentUser)) {
          props.router.push(redirect);
        }
      }

      render() {
        return this.canAccess(this.props.currentUser) ? <WrappedComponent {...this.props}/> : null;
      }
    }

    AccessComponent.displayName = `withAccess(${WrappedComponent.displayName})`;

    return withRouter(withCurrentUser(AccessComponent));
  }
}<commit_msg>check for redirect before trying to redirect<commit_after>import React, { PureComponent } from 'react';
import { withCurrentUser } from 'meteor/vulcan:core';
import { withRouter } from 'react-router';
import Users from 'meteor/vulcan:users';

export default function withAccess (options) {

  const { groups, redirect } = options;

  // we return a function that takes a component and itself returns a component
  return WrappedComponent => {
    class AccessComponent extends PureComponent {

      // if there are any groups defined check if user belongs, else just check if user exists
      canAccess = currentUser => {
        return groups ? currentUser && Users.isMemberOf(currentUser, groups) : currentUser;
      }

      // redirect on constructor if user cannot access
      constructor(props) {
        super(props);
        if(!this.canAccess(props.currentUser) && typeof redirect === 'string') {
          props.router.push(redirect);
        }
      }

      render() {
        return this.canAccess(this.props.currentUser) ? <WrappedComponent {...this.props}/> : null;
      }
    }

    AccessComponent.displayName = `withAccess(${WrappedComponent.displayName})`;

    return withRouter(withCurrentUser(AccessComponent));
  }
}
<|endoftext|>


<commit_before>/**
 * @depends {nrs.js}
 */
var NRS = (function(NRS, $, undefined) {
	NRS.confirmedFormWarning = false;

	NRS.forms = {};

	$(".modal form input").keydown(function(e) {
		if (e.which == "13") {
			e.preventDefault();
			if (NRS.settings["submit_on_enter"] && e.target.type != "textarea") {
				$(this).submit();
			} else {
				return false;
			}
		}
	});

	$(".modal button.btn-primary:not([data-dismiss=modal]):not([data-ignore=true])").click(function() {
		NRS.submitForm($(this).closest(".modal"), $(this));
	});

	NRS.submitForm = function($modal, $btn) {
		if (!$btn) {
			$btn = $modal.find("button.btn-primary:not([data-dismiss=modal])");
		}

		var $modal = $btn.closest(".modal");

		$modal.modal("lock");
		$modal.find("button").prop("disabled", true);
		$btn.button("loading");

		if ($btn.data("form")) {
			var $form = $modal.find("form#" + $btn.data("form"));
			if (!$form.length) {
				$form = $modal.find("form:first");
			}
		} else {
			var $form = $modal.find("form:first");
		}

		var requestType = $form.find("input[name=request_type]").val();
		var requestTypeKey = requestType.replace(/([A-Z])/g, function($1) {
			return "_" + $1.toLowerCase();
		});

		var successMessage = $.t("success_" + requestTypeKey);
		var errorMessage = $.t("error_" + requestTypeKey);

		var data = null;

		var formFunction = NRS["forms"][requestType];
		var formErrorFunction = NRS["forms"][requestType + "Error"];

		if (typeof formErrorFunction != "function") {
			formErrorFunction = false;
		}

		var originalRequestType = requestType;

		if (NRS.downloadingBlockchain) {
			$form.find(".error_message").html($.t("error_blockchain_downloading")).show();
			if (formErrorFunction) {
				formErrorFunction();
			}
			NRS.unlockForm($modal, $btn);
			return;
		} else if (NRS.state.isScanning) {
			$form.find(".error_message").html($.t("error_form_blockchain_rescanning")).show();
			if (formErrorFunction) {
				formErrorFunction();
			}
			NRS.unlockForm($modal, $btn);
			return;
		}

		var invalidElement = false;

		//TODO
		$form.find(":input").each(function() {
			if ($(this).is(":invalid")) {
				var error = "";
				var name = String($(this).attr("name")).capitalize();
				var value = $(this).val();

				if ($(this).hasAttr("max")) {
					var max = $(this).attr("max");

					if (value > max) {
						error = $.t("error_max_value", {
							"field": NRS.getTranslatedFieldName(name).toLowerCase(),
							"max": max
						}).capitalize();
					}
				}

				if ($(this).hasAttr("min")) {
					var min = $(this).attr("min");

					if (value < min) {
						error = $.t("error_min_value", {
							"field": NRS.getTranslatedFieldName(name).toLowerCase(),
							"min": min
						}).capitalize();
					}
				}

				if (!error) {
					error = $.t("error_invalid_field", {
						"field": NRS.getTranslatedFieldName(name).toLowerCase()
					}).capitalize();
				}

				$form.find(".error_message").html(error).show();

				if (formErrorFunction) {
					formErrorFunction();
				}

				NRS.unlockForm($modal, $btn);
				invalidElement = true;
				return false;
			}
		});

		if (invalidElement) {
			return;
		}

		if (typeof formFunction == "function") {
			var output = formFunction($modal);

			if (!output) {
				return;
			} else if (output.error) {
				$form.find(".error_message").html(output.error.escapeHTML()).show();
				if (formErrorFunction) {
					formErrorFunction();
				}
				NRS.unlockForm($modal, $btn);
				return;
			} else {
				if (output.requestType) {
					requestType = output.requestType;
				}
				if (output.data) {
					data = output.data;
				}
				if (output.successMessage) {
					successMessage = output.successMessage;
				}
				if (output.errorMessage) {
					errorMessage = output.errorMessage;
				}
				if (output.stop) {
					NRS.unlockForm($modal, $btn, true);
					return;
				}
			}
		}

		if (!data) {
			data = NRS.getFormData($form);
		}

		if (data.deadline) {
			data.deadline = String(data.deadline * 60); //hours to minutes
		}

		if ("secretPhrase" in data && !data.secretPhrase.length && !NRS.rememberPassword) {
			$form.find(".error_message").html($.t("error_passphrase_required")).show();
			if (formErrorFunction) {
				formErrorFunction(false, data);
			}
			NRS.unlockForm($modal, $btn);
			return;
		}

		if (data.recipient) {
			data.recipient = $.trim(data.recipient);
			if (/^\d+$/.test(data.recipient)) {
				$form.find(".error_message").html($.t("error_numeric_ids_not_allowed")).show();
				if (formErrorFunction) {
					formErrorFunction(false, data);
				}
				NRS.unlockForm($modal, $btn);
				return;
			} else if (!/^NXT\-[A-Z0-9]+\-[A-Z0-9]+\-[A-Z0-9]+\-[A-Z0-9]+/i.test(data.recipient)) {
				var convertedAccountId = $modal.find("input[name=converted_account_id]").val();
				if (!convertedAccountId || (!/^\d+$/.test(convertedAccountId) && !/^NXT\-[A-Z0-9]+\-[A-Z0-9]+\-[A-Z0-9]+\-[A-Z0-9]+/i.test(convertedAccountId))) {
					$form.find(".error_message").html($.t("error_account_id")).show();
					if (formErrorFunction) {
						formErrorFunction(false, data);
					}
					NRS.unlockForm($modal, $btn);
					return;
				} else {
					data.recipient = convertedAccountId;
					data["_extra"] = {
						"convertedAccount": true
					};
				}
			}
		}

		if (!NRS.showedFormWarning) {
			if ("amountNXT" in data && NRS.settings["amount_warning"] && NRS.settings["amount_warning"] != "0") {
				if (new BigInteger(NRS.convertToNQT(data.amountNXT)).compareTo(new BigInteger(NRS.settings["amount_warning"])) > 0) {
					NRS.showedFormWarning = true;
					$form.find(".error_message").html($.t("error_max_amount_warning", {
						"nxt": NRS.formatAmount(NRS.settings["amount_warning"])
					})).show();
					if (formErrorFunction) {
						formErrorFunction(false, data);
					}
					NRS.unlockForm($modal, $btn);
					return;
				}
			}

			if ("feeNXT" in data && NRS.settings["fee_warning"] && NRS.settings["fee_warning"] != "0") {
				if (new BigInteger(NRS.convertToNQT(data.feeNXT)).compareTo(new BigInteger(NRS.settings["fee_warning"])) > 0) {
					NRS.showedFormWarning = true;
					$form.find(".error_message").html($.t("error_max_fee_warning", {
						"nxt": NRS.formatAmount(NRS.settings["fee_warning"])
					})).show();
					if (formErrorFunction) {
						formErrorFunction(false, data);
					}
					NRS.unlockForm($modal, $btn);
					return;
				}
			}
		}

		if (data.doNotBroadcast) {
			data.broadcast = "false";
			delete data.doNotBroadcast;
		}

		NRS.sendRequest(requestType, data, function(response) {
			//todo check again.. response.error
			if (response.fullHash && (!response.error || response.error != "Double spending transaction")) {
				NRS.unlockForm($modal, $btn);

				if (!$modal.hasClass("modal-no-hide")) {
					$modal.modal("hide");
				}

				if (successMessage) {
					$.growl(successMessage.escapeHTML(), {
						type: "success"
					});
				}

				var formCompleteFunction = NRS["forms"][originalRequestType + "Complete"];

				if (requestType != "parseTransaction") {
					if (typeof formCompleteFunction == "function") {
						data.requestType = requestType;

						if (response.transaction) {
							NRS.addUnconfirmedTransaction(response.transaction, function(alreadyProcessed) {
								response.alreadyProcessed = alreadyProcessed;
								formCompleteFunction(response, data);
							});
						} else {
							response.alreadyProcessed = false;
							formCompleteFunction(response, data);
						}
					} else {
						NRS.addUnconfirmedTransaction(response.transaction);
					}
				} else {
					if (typeof formCompleteFunction == "function") {
						data.requestType = requestType;
						formCompleteFunction(response, data);
					}
				}

				if (NRS.accountInfo && !NRS.accountInfo.publicKey) {
					$("#dashboard_message").hide();
				}
			} else if (response.errorCode) {
				$form.find(".error_message").html(response.errorDescription.escapeHTML()).show();

				if (formErrorFunction) {
					formErrorFunction(response, data);
				}

				NRS.unlockForm($modal, $btn);
			} else {
				var sentToFunction = false;

				if (!errorMessage) {
					var formCompleteFunction = NRS["forms"][originalRequestType + "Complete"];

					if (typeof formCompleteFunction == 'function') {
						sentToFunction = true;
						data.requestType = requestType;

						NRS.unlockForm($modal, $btn);

						if (!$modal.hasClass("modal-no-hide")) {
							$modal.modal("hide");
						}
						formCompleteFunction(response, data);
					} else {
						errorMessage = $.t("error_unknown");
					}
				}

				if (!sentToFunction) {
					NRS.unlockForm($modal, $btn, true);

					$.growl(errorMessage.escapeHTML(), {
						type: 'danger'
					});
				}
			}
		});
	}

	NRS.unlockForm = function($modal, $btn, hide) {
		$modal.find("button").prop("disabled", false);
		if ($btn) {
			$btn.button("reset");
		}
		$modal.modal("unlock");
		if (hide) {
			$modal.modal("hide");
		}
	}

	return NRS;
}(NRS || {}, jQuery));<commit_msg>Don't show default success message when already shown in complete function.<commit_after>/**
 * @depends {nrs.js}
 */
var NRS = (function(NRS, $, undefined) {
	NRS.confirmedFormWarning = false;

	NRS.forms = {};

	$(".modal form input").keydown(function(e) {
		if (e.which == "13") {
			e.preventDefault();
			if (NRS.settings["submit_on_enter"] && e.target.type != "textarea") {
				$(this).submit();
			} else {
				return false;
			}
		}
	});

	$(".modal button.btn-primary:not([data-dismiss=modal]):not([data-ignore=true])").click(function() {
		NRS.submitForm($(this).closest(".modal"), $(this));
	});

	NRS.getSuccessMessage = function(requestType) {
		var ignore = ["asset_exchange_change_group_name", "asset_exchange_group", "add_contact", "update_contact", "delete_contact",
			"send_message", "decrypt_messages", "start_forging", "stop_forging", "generate_token", "send_money", "set_alias", "add_asset_bookmark"
		];

		if (ignore.indexOf(requestType) != -1) {
			return "";
		} else {
			return $.t("success_" + requestType);
		}
	}

	NRS.getErrorMessage = function(requestType) {
		return $.t("error_" + requestType);
	}

	NRS.submitForm = function($modal, $btn) {
		if (!$btn) {
			$btn = $modal.find("button.btn-primary:not([data-dismiss=modal])");
		}

		var $modal = $btn.closest(".modal");

		$modal.modal("lock");
		$modal.find("button").prop("disabled", true);
		$btn.button("loading");

		if ($btn.data("form")) {
			var $form = $modal.find("form#" + $btn.data("form"));
			if (!$form.length) {
				$form = $modal.find("form:first");
			}
		} else {
			var $form = $modal.find("form:first");
		}

		var requestType = $form.find("input[name=request_type]").val();
		var requestTypeKey = requestType.replace(/([A-Z])/g, function($1) {
			return "_" + $1.toLowerCase();
		});

		var successMessage = NRS.getSuccessMessage(requestTypeKey);
		var errorMessage = NRS.getErrorMessage(requestTypeKey);

		var data = null;

		var formFunction = NRS["forms"][requestType];
		var formErrorFunction = NRS["forms"][requestType + "Error"];

		if (typeof formErrorFunction != "function") {
			formErrorFunction = false;
		}

		var originalRequestType = requestType;

		if (NRS.downloadingBlockchain) {
			$form.find(".error_message").html($.t("error_blockchain_downloading")).show();
			if (formErrorFunction) {
				formErrorFunction();
			}
			NRS.unlockForm($modal, $btn);
			return;
		} else if (NRS.state.isScanning) {
			$form.find(".error_message").html($.t("error_form_blockchain_rescanning")).show();
			if (formErrorFunction) {
				formErrorFunction();
			}
			NRS.unlockForm($modal, $btn);
			return;
		}

		var invalidElement = false;

		//TODO
		$form.find(":input").each(function() {
			if ($(this).is(":invalid")) {
				var error = "";
				var name = String($(this).attr("name")).capitalize();
				var value = $(this).val();

				if ($(this).hasAttr("max")) {
					var max = $(this).attr("max");

					if (value > max) {
						error = $.t("error_max_value", {
							"field": NRS.getTranslatedFieldName(name).toLowerCase(),
							"max": max
						}).capitalize();
					}
				}

				if ($(this).hasAttr("min")) {
					var min = $(this).attr("min");

					if (value < min) {
						error = $.t("error_min_value", {
							"field": NRS.getTranslatedFieldName(name).toLowerCase(),
							"min": min
						}).capitalize();
					}
				}

				if (!error) {
					error = $.t("error_invalid_field", {
						"field": NRS.getTranslatedFieldName(name).toLowerCase()
					}).capitalize();
				}

				$form.find(".error_message").html(error).show();

				if (formErrorFunction) {
					formErrorFunction();
				}

				NRS.unlockForm($modal, $btn);
				invalidElement = true;
				return false;
			}
		});

		if (invalidElement) {
			return;
		}

		if (typeof formFunction == "function") {
			var output = formFunction($modal);

			if (!output) {
				return;
			} else if (output.error) {
				$form.find(".error_message").html(output.error.escapeHTML()).show();
				if (formErrorFunction) {
					formErrorFunction();
				}
				NRS.unlockForm($modal, $btn);
				return;
			} else {
				if (output.requestType) {
					requestType = output.requestType;
				}
				if (output.data) {
					data = output.data;
				}
				if (output.successMessage) {
					successMessage = output.successMessage;
				}
				if (output.errorMessage) {
					errorMessage = output.errorMessage;
				}
				if (output.stop) {
					NRS.unlockForm($modal, $btn, true);
					return;
				}
			}
		}

		if (!data) {
			data = NRS.getFormData($form);
		}

		if (data.deadline) {
			data.deadline = String(data.deadline * 60); //hours to minutes
		}

		if ("secretPhrase" in data && !data.secretPhrase.length && !NRS.rememberPassword) {
			$form.find(".error_message").html($.t("error_passphrase_required")).show();
			if (formErrorFunction) {
				formErrorFunction(false, data);
			}
			NRS.unlockForm($modal, $btn);
			return;
		}

		if (data.recipient) {
			data.recipient = $.trim(data.recipient);
			if (/^\d+$/.test(data.recipient)) {
				$form.find(".error_message").html($.t("error_numeric_ids_not_allowed")).show();
				if (formErrorFunction) {
					formErrorFunction(false, data);
				}
				NRS.unlockForm($modal, $btn);
				return;
			} else if (!/^NXT\-[A-Z0-9]+\-[A-Z0-9]+\-[A-Z0-9]+\-[A-Z0-9]+/i.test(data.recipient)) {
				var convertedAccountId = $modal.find("input[name=converted_account_id]").val();
				if (!convertedAccountId || (!/^\d+$/.test(convertedAccountId) && !/^NXT\-[A-Z0-9]+\-[A-Z0-9]+\-[A-Z0-9]+\-[A-Z0-9]+/i.test(convertedAccountId))) {
					$form.find(".error_message").html($.t("error_account_id")).show();
					if (formErrorFunction) {
						formErrorFunction(false, data);
					}
					NRS.unlockForm($modal, $btn);
					return;
				} else {
					data.recipient = convertedAccountId;
					data["_extra"] = {
						"convertedAccount": true
					};
				}
			}
		}

		if (!NRS.showedFormWarning) {
			if ("amountNXT" in data && NRS.settings["amount_warning"] && NRS.settings["amount_warning"] != "0") {
				if (new BigInteger(NRS.convertToNQT(data.amountNXT)).compareTo(new BigInteger(NRS.settings["amount_warning"])) > 0) {
					NRS.showedFormWarning = true;
					$form.find(".error_message").html($.t("error_max_amount_warning", {
						"nxt": NRS.formatAmount(NRS.settings["amount_warning"])
					})).show();
					if (formErrorFunction) {
						formErrorFunction(false, data);
					}
					NRS.unlockForm($modal, $btn);
					return;
				}
			}

			if ("feeNXT" in data && NRS.settings["fee_warning"] && NRS.settings["fee_warning"] != "0") {
				if (new BigInteger(NRS.convertToNQT(data.feeNXT)).compareTo(new BigInteger(NRS.settings["fee_warning"])) > 0) {
					NRS.showedFormWarning = true;
					$form.find(".error_message").html($.t("error_max_fee_warning", {
						"nxt": NRS.formatAmount(NRS.settings["fee_warning"])
					})).show();
					if (formErrorFunction) {
						formErrorFunction(false, data);
					}
					NRS.unlockForm($modal, $btn);
					return;
				}
			}
		}

		if (data.doNotBroadcast) {
			data.broadcast = "false";
			delete data.doNotBroadcast;
		}

		NRS.sendRequest(requestType, data, function(response) {
			//todo check again.. response.error
			if (response.fullHash && (!response.error || response.error != "Double spending transaction")) {
				NRS.unlockForm($modal, $btn);

				if (!$modal.hasClass("modal-no-hide")) {
					$modal.modal("hide");
				}

				if (successMessage) {
					$.growl(successMessage.escapeHTML(), {
						type: "success"
					});
				}

				var formCompleteFunction = NRS["forms"][originalRequestType + "Complete"];

				if (requestType != "parseTransaction") {
					if (typeof formCompleteFunction == "function") {
						data.requestType = requestType;

						if (response.transaction) {
							NRS.addUnconfirmedTransaction(response.transaction, function(alreadyProcessed) {
								response.alreadyProcessed = alreadyProcessed;
								formCompleteFunction(response, data);
							});
						} else {
							response.alreadyProcessed = false;
							formCompleteFunction(response, data);
						}
					} else {
						NRS.addUnconfirmedTransaction(response.transaction);
					}
				} else {
					if (typeof formCompleteFunction == "function") {
						data.requestType = requestType;
						formCompleteFunction(response, data);
					}
				}

				if (NRS.accountInfo && !NRS.accountInfo.publicKey) {
					$("#dashboard_message").hide();
				}
			} else if (response.errorCode) {
				$form.find(".error_message").html(response.errorDescription.escapeHTML()).show();

				if (formErrorFunction) {
					formErrorFunction(response, data);
				}

				NRS.unlockForm($modal, $btn);
			} else {
				var sentToFunction = false;

				if (!errorMessage) {
					var formCompleteFunction = NRS["forms"][originalRequestType + "Complete"];

					if (typeof formCompleteFunction == 'function') {
						sentToFunction = true;
						data.requestType = requestType;

						NRS.unlockForm($modal, $btn);

						if (!$modal.hasClass("modal-no-hide")) {
							$modal.modal("hide");
						}
						formCompleteFunction(response, data);
					} else {
						errorMessage = $.t("error_unknown");
					}
				}

				if (!sentToFunction) {
					NRS.unlockForm($modal, $btn, true);

					$.growl(errorMessage.escapeHTML(), {
						type: 'danger'
					});
				}
			}
		});
	}

	NRS.unlockForm = function($modal, $btn, hide) {
		$modal.find("button").prop("disabled", false);
		if ($btn) {
			$btn.button("reset");
		}
		$modal.modal("unlock");
		if (hide) {
			$modal.modal("hide");
		}
	}

	return NRS;
}(NRS || {}, jQuery));<|endoftext|>


<commit_before>// Template.manageObjects

var managableTypes = [
  {
    collectionSlug: "data-sets",
    humanName: "Data Sets",
    singularName: "data set",
    collectionName: "DataSets",
    introTemplate: "introDataSets",
    createTemplate: "createDataSet",
    showTemplate: "showDataSet",
  },
  {
    collectionSlug: "sample-groups",
    humanName: "Sample Groups",
    singularName: "sample group",
    collectionName: "SampleGroups",
    introTemplate: "introSampleGroups",
    createTemplate: "createSampleGroup",
    showTemplate: "showSampleGroup",
    permissionDeniedTemplate: "waitAndThenPermissionDenied",
  },
  {
    collectionSlug: "gene-sets",
    humanName: "Gene Sets",
    singularName: "gene set",
    collectionName: "GeneSets",
    introTemplate: "introGeneSets",
    createTemplate: "createGeneSet",
    showTemplate: "showGeneSet",
  },
  {
    collectionSlug: "gene-set-groups",
    humanName: "Gene Set Groups",
    singularName: "gene set group",
    collectionName: "GeneSetGroups",
    introTemplate: "introGeneSetGroups",
    createTemplate: "createGeneSetGroup",
    showTemplate: "showGeneSetGroup",
  },
  {
    collectionSlug: "studies",
    humanName: "Studies",
    singularName: "study",
    collectionName: "Studies",
    introTemplate: "introStudies",
    createTemplate: "createStudy",
    showTemplate: "showStudy",
  },
  {
    collectionSlug: "clinical-forms",
    humanName: "Clinical Forms",
    singularName: "clinical form",
    collectionName: "Forms",
    introTemplate: "introForms",
    createTemplate: "createForm",
    showTemplate: "showForm",
  },
];

Template.manageObjects.helpers({
  managableTypes: managableTypes,
  tabActive() {
    return this.collectionSlug === FlowRouter.getParam("collectionSlug");
  },
  selectedType() {
    let selected = FlowRouter.getParam("collectionSlug");

    return _.findWhere(managableTypes, { collectionSlug: selected });
  },
});

// Template.dataTypeInfoIcon

Template.dataTypeInfoIcon.onRendered(function () {
  let instance = this;

  instance.$(".help.circle.icon").popup({
    position: "bottom left",
    hoverable: true,
  });
});

// Template.manageObjectsGrid

Template.manageObjectsGrid.onCreated(function () {
  let instance = this;

  instance.currentlyManaging = new ReactiveVar();

  // subscribe to the names of the available data, and
  // set instance.currentlyManaging
  instance.autorun(() => {
    let slug = FlowRouter.getParam("collectionSlug");
    let currObj = _.findWhere(managableTypes, { collectionSlug: slug });

    instance.currentlyManaging.set(currObj);
    instance.subscribe("allOfCollectionOnlyMetadata", currObj.collectionName);
  });

  // if the user selected something before, select that one
  var lastSlug;

  instance.autorun((computation) => {
    let selected = FlowRouter.getParam("selected");
    let collectionSlug = FlowRouter.getParam("collectionSlug");

    if (lastSlug !== collectionSlug && !selected) {
      let fromSession = Session.get("manageObjects-" + collectionSlug);

      // Only execute after a bit because of a race condition between
      // this firing and the URL actually getting set.
      // (FlowRouter.getParam("selected") works fine but the URL is wrong)
      Meteor.defer(() => {
        FlowRouter.setParams({
          selected: fromSession
        });
      });
    } else {
      Session.set("manageObjects-" + collectionSlug, selected);
    }

    lastSlug = collectionSlug;
  });
});

Template.manageObjectsGrid.onRendered(function () {
  let instance = this;

  // NOTE: a deferred versino of refreshSticky is passed down all
  // the way to the create/showTemplate level

  // keep track of whether it's been stickied yet
  // start this out as false because it hasn't been stickied yet
  let stickied = false;

  // Make the manage object detail sticky so it moves down
  // the page as the user scrolls down.
  function refreshSticky() {
    console.log("refreshSticky");
    // reactively watch the count of the items on the left so it'll update
    // when it changes, but don't do anything with the return value
    let hi = getObjects(instance).count();

    // Only activate the sticky when the height of the master list is
    // greater than a pageful. Don't have a sticky when creating a new object
    // because it's too annoying to keep refreshing the sticky.
    // (Creation UIs tend to get bigger as the user fills in information.)
    if (instance.$("#manage-obj-master").height() > $(window).height() &&
        FlowRouter.getParam("selected") !== "create") {
      if (stickied) {
        // refresh the sticky because it's been set up already
        instance.$("#manage-obj-detail").sticky("refresh");
      } else {
        // set up the sticky for the first time
        instance.$("#manage-obj-detail").sticky({
          context: "#manage-obj-context",
          offset: 55,
        });
      }

      stickied = true;
    } else {
      // destroy the sticky
      instance.$("#manage-obj-detail").sticky("destroy");

      stickied = false;
    }
  }

  // call it only after Blaze has rerendered
  deferredRefreshSticky = () => {

  };

  // call refreshSticky when a reactive variable changes
  instance.autorun(() => {
    refreshSticky();
  });

  // only call it 300 seconds after the thing has stopped resizing
  instance.debouncedRefreshSticky = _.debounce(function() {
    Meteor.defer(refreshSticky);
  }, 300);

  // call refreshSticky when the window is resized
  $(window).resize(instance.debouncedRefreshSticky);
});

function getObjects (instance, query = {}) {
  // get all the objects for this data type
  let slug = FlowRouter.getParam("collectionSlug");
  let managing = instance.currentlyManaging.get();

  return MedBook.collections[managing.collectionName].find(query, {
    // need to sort by _id also to break ties: if two things have the same
    // name, clicking on one can cause the list to reorder itself
    sort: { name: 1, version: 1, _id: 1 },
  });
}

Template.manageObjectsGrid.helpers({
  getObjects() { return getObjects(Template.instance()); },
  managingObject() {
    return this._id === FlowRouter.getParam("selected");
  },
  showVersion() {
    if (this.version) {
      let instance = Template.instance();

      // only show the version if the version isn't 1 or there's a duplicate
      return this.version !== 1 ||
          getObjects(instance, { name: this.name }).count() > 1;
    }
  },
  managableTypeAndRefreshSticky() {
    added = this;

    added.refreshSticky = Template.instance().debouncedRefreshSticky;

    return added;
  },
});

// Template.manageObject

Template.manageObject.onCreated(function () {
  let instance = this;

  // subscribe to the selected object
  instance.autorun(() => {
    let { collectionName } = instance.data;
    let selectedId = FlowRouter.getParam("selected");

    if (selectedId) {
      instance.subscribe("objectFromCollection", collectionName, selectedId);
    }
  });
});

Template.manageObject.helpers({
  getObjects() {
    return getObjects(Template.instance().parent());
  },
  getObject() {
    let slug = FlowRouter.getParam("collectionSlug");
    let managing = _.findWhere(managableTypes, { collectionSlug: slug });
    let selectedId = FlowRouter.getParam("selected");

    return MedBook.collections[managing.collectionName].findOne(selectedId);
  },
  onDelete() {
    return () => {
      FlowRouter.setParams({ selected: null });
    };
  },
  addRefreshSticky(obj) {
    obj.refreshSticky = this.refreshSticky;

    return obj;
  },
});
<commit_msg>removed console.log<commit_after>// Template.manageObjects

var managableTypes = [
  {
    collectionSlug: "data-sets",
    humanName: "Data Sets",
    singularName: "data set",
    collectionName: "DataSets",
    introTemplate: "introDataSets",
    createTemplate: "createDataSet",
    showTemplate: "showDataSet",
  },
  {
    collectionSlug: "sample-groups",
    humanName: "Sample Groups",
    singularName: "sample group",
    collectionName: "SampleGroups",
    introTemplate: "introSampleGroups",
    createTemplate: "createSampleGroup",
    showTemplate: "showSampleGroup",
    permissionDeniedTemplate: "waitAndThenPermissionDenied",
  },
  {
    collectionSlug: "gene-sets",
    humanName: "Gene Sets",
    singularName: "gene set",
    collectionName: "GeneSets",
    introTemplate: "introGeneSets",
    createTemplate: "createGeneSet",
    showTemplate: "showGeneSet",
  },
  {
    collectionSlug: "gene-set-groups",
    humanName: "Gene Set Groups",
    singularName: "gene set group",
    collectionName: "GeneSetGroups",
    introTemplate: "introGeneSetGroups",
    createTemplate: "createGeneSetGroup",
    showTemplate: "showGeneSetGroup",
  },
  {
    collectionSlug: "studies",
    humanName: "Studies",
    singularName: "study",
    collectionName: "Studies",
    introTemplate: "introStudies",
    createTemplate: "createStudy",
    showTemplate: "showStudy",
  },
  {
    collectionSlug: "clinical-forms",
    humanName: "Clinical Forms",
    singularName: "clinical form",
    collectionName: "Forms",
    introTemplate: "introForms",
    createTemplate: "createForm",
    showTemplate: "showForm",
  },
];

Template.manageObjects.helpers({
  managableTypes: managableTypes,
  tabActive() {
    return this.collectionSlug === FlowRouter.getParam("collectionSlug");
  },
  selectedType() {
    let selected = FlowRouter.getParam("collectionSlug");

    return _.findWhere(managableTypes, { collectionSlug: selected });
  },
});

// Template.dataTypeInfoIcon

Template.dataTypeInfoIcon.onRendered(function () {
  let instance = this;

  instance.$(".help.circle.icon").popup({
    position: "bottom left",
    hoverable: true,
  });
});

// Template.manageObjectsGrid

Template.manageObjectsGrid.onCreated(function () {
  let instance = this;

  instance.currentlyManaging = new ReactiveVar();

  // subscribe to the names of the available data, and
  // set instance.currentlyManaging
  instance.autorun(() => {
    let slug = FlowRouter.getParam("collectionSlug");
    let currObj = _.findWhere(managableTypes, { collectionSlug: slug });

    instance.currentlyManaging.set(currObj);
    instance.subscribe("allOfCollectionOnlyMetadata", currObj.collectionName);
  });

  // if the user selected something before, select that one
  var lastSlug;

  instance.autorun((computation) => {
    let selected = FlowRouter.getParam("selected");
    let collectionSlug = FlowRouter.getParam("collectionSlug");

    if (lastSlug !== collectionSlug && !selected) {
      let fromSession = Session.get("manageObjects-" + collectionSlug);

      // Only execute after a bit because of a race condition between
      // this firing and the URL actually getting set.
      // (FlowRouter.getParam("selected") works fine but the URL is wrong)
      Meteor.defer(() => {
        FlowRouter.setParams({
          selected: fromSession
        });
      });
    } else {
      Session.set("manageObjects-" + collectionSlug, selected);
    }

    lastSlug = collectionSlug;
  });
});

Template.manageObjectsGrid.onRendered(function () {
  let instance = this;

  // NOTE: a deferred versino of refreshSticky is passed down all
  // the way to the create/showTemplate level

  // keep track of whether it's been stickied yet
  // start this out as false because it hasn't been stickied yet
  let stickied = false;

  // Make the manage object detail sticky so it moves down
  // the page as the user scrolls down.
  function refreshSticky() {
    // reactively watch the count of the items on the left so it'll update
    // when it changes, but don't do anything with the return value
    let hi = getObjects(instance).count();

    // Only activate the sticky when the height of the master list is
    // greater than a pageful. Don't have a sticky when creating a new object
    // because it's too annoying to keep refreshing the sticky.
    // (Creation UIs tend to get bigger as the user fills in information.)
    if (instance.$("#manage-obj-master").height() > $(window).height() &&
        FlowRouter.getParam("selected") !== "create") {
      if (stickied) {
        // refresh the sticky because it's been set up already
        instance.$("#manage-obj-detail").sticky("refresh");
      } else {
        // set up the sticky for the first time
        instance.$("#manage-obj-detail").sticky({
          context: "#manage-obj-context",
          offset: 55,
        });
      }

      stickied = true;
    } else {
      // destroy the sticky
      instance.$("#manage-obj-detail").sticky("destroy");

      stickied = false;
    }
  }

  // call it only after Blaze has rerendered
  deferredRefreshSticky = () => {

  };

  // call refreshSticky when a reactive variable changes
  instance.autorun(() => {
    refreshSticky();
  });

  // only call it 300 seconds after the thing has stopped resizing
  instance.debouncedRefreshSticky = _.debounce(function() {
    Meteor.defer(refreshSticky);
  }, 300);

  // call refreshSticky when the window is resized
  $(window).resize(instance.debouncedRefreshSticky);
});

function getObjects (instance, query = {}) {
  // get all the objects for this data type
  let slug = FlowRouter.getParam("collectionSlug");
  let managing = instance.currentlyManaging.get();

  return MedBook.collections[managing.collectionName].find(query, {
    // need to sort by _id also to break ties: if two things have the same
    // name, clicking on one can cause the list to reorder itself
    sort: { name: 1, version: 1, _id: 1 },
  });
}

Template.manageObjectsGrid.helpers({
  getObjects() { return getObjects(Template.instance()); },
  managingObject() {
    return this._id === FlowRouter.getParam("selected");
  },
  showVersion() {
    if (this.version) {
      let instance = Template.instance();

      // only show the version if the version isn't 1 or there's a duplicate
      return this.version !== 1 ||
          getObjects(instance, { name: this.name }).count() > 1;
    }
  },
  managableTypeAndRefreshSticky() {
    added = this;

    added.refreshSticky = Template.instance().debouncedRefreshSticky;

    return added;
  },
});

// Template.manageObject

Template.manageObject.onCreated(function () {
  let instance = this;

  // subscribe to the selected object
  instance.autorun(() => {
    let { collectionName } = instance.data;
    let selectedId = FlowRouter.getParam("selected");

    if (selectedId) {
      instance.subscribe("objectFromCollection", collectionName, selectedId);
    }
  });
});

Template.manageObject.helpers({
  getObjects() {
    return getObjects(Template.instance().parent());
  },
  getObject() {
    let slug = FlowRouter.getParam("collectionSlug");
    let managing = _.findWhere(managableTypes, { collectionSlug: slug });
    let selectedId = FlowRouter.getParam("selected");

    return MedBook.collections[managing.collectionName].findOne(selectedId);
  },
  onDelete() {
    return () => {
      FlowRouter.setParams({ selected: null });
    };
  },
  addRefreshSticky(obj) {
    obj.refreshSticky = this.refreshSticky;

    return obj;
  },
});
<|endoftext|>


<commit_before>console.log("adding drag drop event listeners");

function DataTransfer() {
  this.data = {};
};
DataTransfer.prototype.setData = function(type, value){
  this.data[type] = value;
  console.log('value of type: ' + type + ' set');
};
DataTransfer.prototype.getData = function(type){
  return this.data[type];
};

var drag_and_drop = {
  dragging: false,
  nodes: [],
  dataTransfer: new DataTransfer()
};
findElementNodes = function(node){
  var body = document.body;
  var nodes = [];
  while(node != document) {
    nodes.push(node);
    try {
      node = node.parentNode;
    } catch (e) {
      console.log('error type: ' + e.name);
      node = document;
    }// finally {
//      node = body;
  //  }
  }
  return nodes.reverse();
};
findDraggableNodes = function(node){
  var draggable = 'dragger';
  var body = document.body;
  var draggable_nodes = [];
  while(node != body) {
    if(node.hasAttribute(draggable)){
      var draggable_value = node.attributes[draggable].value;
      if(draggable_value == true
        || draggable_value == 'true'
        || draggable_value == ""){
          draggable_nodes.push(node);
      }
    }
    node = node.parentNode;
    }
    return draggable_nodes;
};
copyStyle = function(original,copy){
  var originalStyle = window.getComputedStyle(original);
  var bodyStyle = window.getComputedStyle(document.body);
  var key;
  for(var l = originalStyle.length,i = 0; i < l ; i++){
    key = originalStyle[i];
    if(originalStyle[key] != bodyStyle[key] || key == 'outline'){
      // only copy styles not inherited from the body
      copy.style[key] = originalStyle[key];
    }
  };
}
cloneWithStyle = function(nodeOrig){
  var copyRoot = document.createElement('div');
  var treeWalker = document.createTreeWalker(
    nodeOrig, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null, false);
  var node = treeWalker;
  var currentNode,newNode;
  var copyNode = copyRoot;
  var appendType = 'child';
  var explode = 5000;
  while(explode > 0){
    explode--;
    currentNode = treeWalker.currentNode;
    if(currentNode.nodeType == 1){
      newNode = document.createElement(currentNode.nodeName);
      copyStyle(currentNode,newNode);
    }else if(currentNode.nodeType == 3){
      newNode = document.createTextNode(currentNode.nodeValue);
    }
    if(appendType === 'child'){
      copyNode.appendChild(newNode);
    } else if(appendType === 'sibling'){
      copyNode.parentNode.appendChild(newNode);
    }
    copyNode = newNode;
    if(node.firstChild()){
      appendType = 'child';
    } else {
      while(!node.nextSibling()){
        if(node.currentNode == node.root){
          return copyRoot;
        }
        node.parentNode();
        copyNode = copyNode.parentNode;
      }
      appendType = 'sibling';
    }
  }
}
touchStartCallback = function(e){
  var target,clientX,clientY;
  if(e.type == 'mousedown'){
    console.log("mousedown");
    target = e.target;
    clientX = e.clientX;
    clientY = e.clientY;
  } else if(e.type == 'touchstart'){
    if(e.touches.length > 1){
      return;
    } else {
      target = e.touches[0].target;
      clientX = e.touches[0].clientX;
      clientY = e.touches[0].clientY;
    }
  }
  var dnodes = findDraggableNodes(target);
  var dcount = dnodes.length;
//  console.log('draggable_nodes count: ' + dcount);
  if(dcount){
    e.preventDefault();
    drag_and_drop.dragging = true;
    drag_and_drop.dragNode = dnodes[0];
    drag_and_drop.nodes = findElementNodes(target);
    var dcopy = cloneWithStyle(dnodes[0]);
    var rect = dnodes[0].getBoundingClientRect();
    var offsetY = clientY - rect.top;
    var offsetX = clientX - rect.left;
    drag_and_drop['rect'] = rect;
    drag_and_drop['offset'] = { X: offsetX, Y: offsetY };
    document.body.appendChild(dcopy);
    dcopy.style.position = "absolute";
    dcopy.style['z-index'] = 5000;
    dcopy.style.top = '0px';
    dcopy.style.left = '0px';
    dcopy.style.overflow = 'hidden';
    var X = clientX - offsetX;
    var Y = clientY - offsetY;
    dcopy.style['-webkit-transform'] = 'translate(' + X + 'px, ' + Y + 'px)';
    dcopy.style['pointer-events'] = 'none';
    drag_and_drop.dcopy = dcopy;
    var copyOrigRect = dcopy.getBoundingClientRect();
    drag_and_drop['copyOrigRect'] = copyOrigRect;

    var dragStartEvent = document.createEvent("Event");
    dragStartEvent.initEvent("dragstart", true, true);
    dragStartEvent.dataTransfer = drag_and_drop.dataTransfer;
    var dragStartCanceled = !dnodes[0].dispatchEvent(dragStartEvent);
  }
}
touchEndCallback = function(e){
  console.log("mouseup");
  drag_and_drop.dragging = false;
  document.body.removeChild(drag_and_drop.dcopy);
  var dragEndEvent = document.createEvent("Event");
  dragEndEvent.initEvent("dragend", true, true);
  dragEndEvent.screenX = drag_and_drop.moveData.screenX;
  dragEndEvent.screenY = drag_and_drop.moveData.screenY;
  drag_and_drop.dragNode.dispatchEvent(dragEndEvent);
}
touchMoveCallback = function(e){
  if(drag_and_drop.dragging){
    console.log('dragging');
    var moveData = {}
    if(e.type == 'mousemove'){
      //console.log('mousemove');
      moveData.clientX = e.clientX;
      moveData.clientY = e.clientY;
      moveData.screenX = e.screenX;
      moveData.screenY = e.screenY;
    } else if(e.type == 'touchmove'){
      if(e.touches.length > 1){
        return;
      } else {
        //console.log('touchmove');
        moveData.clientX = e.touches[0].clientX;
        moveData.clientY = e.touches[0].clientY;
        moveData.screenX = e.touches[0].screenX;
        moveData.screenY = e.touches[0].screenY;
      }
    }
    drag_and_drop.moveData = moveData;
    var element = document.elementFromPoint(moveData.clientX,moveData.clientY);
    if(element !== null){
      var dragOverEvent = document.createEvent("Event");
      dragOverEvent.initEvent("dragover", true, true);
      dragOverEvent.dataTransfer = {};
      var dragOverCanceled = !element.dispatchEvent(dragOverEvent);
    }
    var oldNodes = drag_and_drop.nodes;
    var newNodes;
    if(element === null){
      newNodes = [];
    } else {
      newNodes = findElementNodes(element);
    }
    var elCount = (oldNodes.length < newNodes.length) ?
      oldNodes.length : newNodes.length;
    var i = 0;
    for( ; i < elCount && oldNodes[i] === newNodes[i] ; i++){}
    for(var on = oldNodes.length -1 ; on >= i ; on--){
      var dragLeaveEvent = document.createEvent("Event");
      dragLeaveEvent.initEvent("dragleave", true, true);
      oldNodes[on].dispatchEvent(dragLeaveEvent);
    }
    for(var nn = newNodes.length ; i < nn ; i++){
      var dragEnterEvent = document.createEvent("Event");
      dragEnterEvent.initEvent("dragenter", true, true);
      newNodes[i].dispatchEvent(dragEnterEvent);
    }
    drag_and_drop.nodes = newNodes;
    var dcopy = drag_and_drop.dcopy;
    var offset = drag_and_drop.offset;
    var copyOrigWidth = drag_and_drop.copyOrigRect.width;
    var X = moveData.clientX - offset.X;
    var Y = moveData.clientY - offset.Y;
    // if dragged item extends to right of screen trim it to fit
    var overflow = (X + copyOrigWidth) - window.innerWidth;
    if(overflow > 0){
      dcopy.style.width = (copyOrigWidth - overflow) + 'px';
    } else {
      dcopy.style.width = copyOrigWidth + 'px';
    }
    dcopy.style['-webkit-transform'] = 'translate(' + X + 'px, ' + Y + 'px)';
  }
}
document.addEventListener('mousedown', touchStartCallback, true);
document.addEventListener('touchstart', touchStartCallback, true);
document.addEventListener('mouseup', touchEndCallback, true);
document.addEventListener('touchend', touchEndCallback, true);
document.addEventListener('mousemove', touchMoveCallback, true);
document.addEventListener('touchmove', touchMoveCallback, true);

<commit_msg>added drop event. Now useful, but not complete.<commit_after>console.log("adding drag drop event listeners");

function DataTransfer() {
  this.data = {};
};
DataTransfer.prototype.setData = function(type, value){
  this.data[type] = value;
  console.log('value of type: ' + type + ' set');
};
DataTransfer.prototype.getData = function(type){
  return this.data[type];
};

var drag_and_drop = {
  dragging: false,
  nodes: [],
  dataTransfer: new DataTransfer()
};
findElementNodes = function(node){
  var body = document.body;
  var nodes = [];
  while(node != document) {
    nodes.push(node);
    try {
      node = node.parentNode;
    } catch (e) {
      console.log('error type: ' + e.name);
      node = document;
    }// finally {
//      node = body;
  //  }
  }
  return nodes.reverse();
};
findDraggableNodes = function(node){
  var draggable = 'dragger';
  var body = document.body;
  var draggable_nodes = [];
  while(node != body) {
    if(node.hasAttribute(draggable)){
      var draggable_value = node.attributes[draggable].value;
      if(draggable_value == true
        || draggable_value == 'true'
        || draggable_value == ""){
          draggable_nodes.push(node);
      }
    }
    node = node.parentNode;
    }
    return draggable_nodes;
};
copyStyle = function(original,copy){
  var originalStyle = window.getComputedStyle(original);
  var bodyStyle = window.getComputedStyle(document.body);
  var key;
  for(var l = originalStyle.length,i = 0; i < l ; i++){
    key = originalStyle[i];
    if(originalStyle[key] != bodyStyle[key] || key == 'outline'){
      // only copy styles not inherited from the body
      copy.style[key] = originalStyle[key];
    }
  };
}
cloneWithStyle = function(nodeOrig){
  var copyRoot = document.createElement('div');
  var treeWalker = document.createTreeWalker(
    nodeOrig, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, null, false);
  var node = treeWalker;
  var currentNode,newNode;
  var copyNode = copyRoot;
  var appendType = 'child';
  var explode = 5000;
  while(explode > 0){
    explode--;
    currentNode = treeWalker.currentNode;
    if(currentNode.nodeType == 1){
      newNode = document.createElement(currentNode.nodeName);
      copyStyle(currentNode,newNode);
    }else if(currentNode.nodeType == 3){
      newNode = document.createTextNode(currentNode.nodeValue);
    }
    if(appendType === 'child'){
      copyNode.appendChild(newNode);
    } else if(appendType === 'sibling'){
      copyNode.parentNode.appendChild(newNode);
    }
    copyNode = newNode;
    if(node.firstChild()){
      appendType = 'child';
    } else {
      while(!node.nextSibling()){
        if(node.currentNode == node.root){
          return copyRoot;
        }
        node.parentNode();
        copyNode = copyNode.parentNode;
      }
      appendType = 'sibling';
    }
  }
}
touchStartCallback = function(e){
  var target,clientX,clientY;
  if(e.type == 'mousedown'){
    console.log("mousedown");
    target = e.target;
    clientX = e.clientX;
    clientY = e.clientY;
  } else if(e.type == 'touchstart'){
    if(e.touches.length > 1){
      return;
    } else {
      target = e.touches[0].target;
      clientX = e.touches[0].clientX;
      clientY = e.touches[0].clientY;
    }
  }
  var dnodes = findDraggableNodes(target);
  var dcount = dnodes.length;
//  console.log('draggable_nodes count: ' + dcount);
  if(dcount){
    e.preventDefault();
    drag_and_drop.dragging = true;
    drag_and_drop.dragNode = dnodes[0];
    drag_and_drop.nodes = findElementNodes(target);
    var dcopy = cloneWithStyle(dnodes[0]);
    var rect = dnodes[0].getBoundingClientRect();
    var offsetY = clientY - rect.top;
    var offsetX = clientX - rect.left;
    drag_and_drop['rect'] = rect;
    drag_and_drop['offset'] = { X: offsetX, Y: offsetY };
    document.body.appendChild(dcopy);
    dcopy.style.position = "absolute";
    dcopy.style['z-index'] = 5000;
    dcopy.style.top = '0px';
    dcopy.style.left = '0px';
    dcopy.style.overflow = 'hidden';
    var X = clientX - offsetX;
    var Y = clientY - offsetY;
    dcopy.style['-webkit-transform'] = 'translate(' + X + 'px, ' + Y + 'px)';
    dcopy.style['pointer-events'] = 'none';
    drag_and_drop.dcopy = dcopy;
    var copyOrigRect = dcopy.getBoundingClientRect();
    drag_and_drop['copyOrigRect'] = copyOrigRect;

    var dragStartEvent = document.createEvent("Event");
    dragStartEvent.initEvent("dragstart", true, true);
    dragStartEvent.dataTransfer = drag_and_drop.dataTransfer;
    var dragStartCanceled = !dnodes[0].dispatchEvent(dragStartEvent);
  }
}
touchEndCallback = function(e){
  console.log("mouseup or touchend");
  drag_and_drop.dragging = false;
  document.body.removeChild(drag_and_drop.dcopy);
  var dropElement = drag_and_drop.dropElement;
  if(dropElement !== null){
    var dropEvent = document.createEvent("Event");
    dropEvent.initEvent("drop", true, true);
    dropEvent.dataTransfer = drag_and_drop.dataTransfer;
    dropEvent.clientX = drag_and_drop.moveData.clientX;
    dropEvent.clientY = drag_and_drop.moveData.clientY;
    dropElement.dispatchEvent(dropEvent);
  } else {
    console.log('invalid drop target');
  }
  var oldNodes = drag_and_drop.nodes;
  for(var on = oldNodes.length -1 ; on >= 0 ; on--){
    var dragLeaveEvent = document.createEvent("Event");
    dragLeaveEvent.initEvent("dragleave", true, true);
    oldNodes[on].dispatchEvent(dragLeaveEvent);
  }
  var dragEndEvent = document.createEvent("Event");
  dragEndEvent.initEvent("dragend", true, true);
  dragEndEvent.screenX = drag_and_drop.moveData.screenX;
  dragEndEvent.screenY = drag_and_drop.moveData.screenY;
  drag_and_drop.dragNode.dispatchEvent(dragEndEvent);
}
touchMoveCallback = function(e){
  if(drag_and_drop.dragging){
    console.log('dragging');
    var moveData = {}
    if(e.type == 'mousemove'){
      //console.log('mousemove');
      moveData.clientX = e.clientX;
      moveData.clientY = e.clientY;
      moveData.screenX = e.screenX;
      moveData.screenY = e.screenY;
    } else if(e.type == 'touchmove'){
      if(e.touches.length > 1){
        return;
      } else {
        //console.log('touchmove');
        moveData.clientX = e.touches[0].clientX;
        moveData.clientY = e.touches[0].clientY;
        moveData.screenX = e.touches[0].screenX;
        moveData.screenY = e.touches[0].screenY;
      }
    }
    drag_and_drop.moveData = moveData;
    var element = document.elementFromPoint(moveData.clientX,moveData.clientY);
    drag_and_drop.dropElement = element;
    if(element !== null){
      var dragOverEvent = document.createEvent("Event");
      dragOverEvent.initEvent("dragover", true, true);
      dragOverEvent.dataTransfer = drag_and_drop.dataTransfer;
      var dragOverCanceled = !element.dispatchEvent(dragOverEvent);
    }
    var oldNodes = drag_and_drop.nodes;
    var newNodes;
    if(element === null){
      newNodes = [];
    } else {
      newNodes = findElementNodes(element);
    }
    var elCount = (oldNodes.length < newNodes.length) ?
      oldNodes.length : newNodes.length;
    var i = 0;
    for( ; i < elCount && oldNodes[i] === newNodes[i] ; i++){}
    for(var on = oldNodes.length -1 ; on >= i ; on--){
      var dragLeaveEvent = document.createEvent("Event");
      dragLeaveEvent.initEvent("dragleave", true, true);
      oldNodes[on].dispatchEvent(dragLeaveEvent);
    }
    for(var nn = newNodes.length ; i < nn ; i++){
      var dragEnterEvent = document.createEvent("Event");
      dragEnterEvent.initEvent("dragenter", true, true);
      newNodes[i].dispatchEvent(dragEnterEvent);
    }
    drag_and_drop.nodes = newNodes;
    var dcopy = drag_and_drop.dcopy;
    var offset = drag_and_drop.offset;
    var copyOrigWidth = drag_and_drop.copyOrigRect.width;
    var X = moveData.clientX - offset.X;
    var Y = moveData.clientY - offset.Y;
    // if dragged item extends to right of screen trim it to fit
    var overflow = (X + copyOrigWidth) - window.innerWidth;
    if(overflow > 0){
      dcopy.style.width = (copyOrigWidth - overflow) + 'px';
    } else {
      dcopy.style.width = copyOrigWidth + 'px';
    }
    dcopy.style['-webkit-transform'] = 'translate(' + X + 'px, ' + Y + 'px)';
  }
}
document.addEventListener('mousedown', touchStartCallback, true);
document.addEventListener('touchstart', touchStartCallback, true);
document.addEventListener('mouseup', touchEndCallback, true);
document.addEventListener('touchend', touchEndCallback, true);
document.addEventListener('mousemove', touchMoveCallback, true);
document.addEventListener('touchmove', touchMoveCallback, true);

<|endoftext|>


<commit_before>'use strict';

const playerBar = '.player_ct.mini';
const durationSelector = `${playerBar} .time_all`;
const currentTimeSelector = `${playerBar} .time_current`;

Connector.playerSelector = `#app ${playerBar}`;

Connector.artistSelector = `${playerBar} .track_info .artist`;

Connector.trackSelector = `${playerBar} .track_info .title`;

Connector.pauseButtonSelector = '.control_area .icon-player.btn-player-pause';

Connector.trackArtSelector = `${playerBar} .playbar_ct .thumb`;

Connector.getCurrentTime = () => getSeconds(currentTimeSelector);

Connector.getDuration = () => getSeconds(durationSelector);

function getSeconds(selector) {
	const timeStr = $(selector)[0].innerText;
	return Util.stringToSeconds(timeStr);
}
<commit_msg>Rewrite music-flo connector(#2837)<commit_after>'use strict';

Connector.playerSelector = '.player_ct.mini';
Connector.artistSelector = '.track_area .track_info .artist';
Connector.trackSelector = '.track_area .track_info .title';
Connector.pauseButtonSelector = '.control_area .icon-player.btn-player-pause';
Connector.trackArtSelector = '.playbar_ct .thumb';
Connector.durationSelector = '.time_all > span';
Connector.currentTimeSelector = '.time_current > span';
<|endoftext|>


<commit_before>// Place your library's code here
//
// If you add additional files, be sure to
// load them in order in ./wrapper.js
//

import _ from 'underscore';
import moment from 'moment';

var ConsecutiveSegments = {

  // Segment an array of events by scale
  group(segments, scale='weeks') {
    if (_.isEmpty(segments)) { return []; }

    var currentGroup = 0, groups = [], currentMoment, prevMoment;
    _.chain(segments)
      .map((events, timestamp) => {
        return { timestamp, events };
      })
      .each((s, index, segments) => {

        // Check to see if the current group is the same
        // as the previous group by computing the difference
        // in their timestamps. > 1 means that they aren't
        // consecutive.
        if (index !== 0) {
          currentMoment = moment.unix(s.timestamp).utc();
          prevMoment = moment.unix(segments[index - 1].timestamp).utc();
          if (currentMoment.diff(prevMoment, scale) > 1) {
            currentGroup++;
          }
        }

        // Ensure that the group exists, then push to it
        if (!groups[currentGroup]) {
          groups[currentGroup] = [];
        }
        groups[currentGroup].push(_.clone(s));
      });

    return groups;
  }
};

export default ConsecutiveSegments;
<commit_msg>Refactor to use _.reduce.<commit_after>// Place your library's code here
//
// If you add additional files, be sure to
// load them in order in ./wrapper.js
//

import _ from 'underscore';
import moment from 'moment';

var ConsecutiveSegments = {

  // Segment an array of events by scale
  group(segments, scale='weeks') {
    if (_.isEmpty(segments)) { return []; }

    let currentGroup = 0, currentMoment, prevMoment;
    return _.chain(segments)
      .map((events, timestamp) => {
        return { timestamp, events };
      })
      .reduce((memo, s, index, segments) => {

        // Check to see if the current group is the same
        // as the previous group by computing the difference
        // in their timestamps. They aren't consecutive when
        // the difference is > 1
        if (index !== 0) {
          currentMoment = moment.unix(s.timestamp).utc();
          prevMoment = moment.unix(segments[index - 1].timestamp).utc();
          if (currentMoment.diff(prevMoment, scale) > 1) {
            currentGroup++;
          }
        }

        // Ensure that the group exists, then push to it
        if (!memo[currentGroup]) {
          memo[currentGroup] = [];
        }
        memo[currentGroup].push(_.clone(s));
        return memo;
      }, [])
      .value();
  }
};

export default ConsecutiveSegments;
<|endoftext|>