<commit_before>
export class Router {

	/**
	 * foward()
	 * @type {History}
	 */
	forward = null;

	/**
	 * back()
	 * @type {History}
	 */
	back = null;

	/**
	 * _pushState()
	 * @type {History}
	 */
	pushState = null;

	/**
	 * _replaceState()
	 * @type {History}
	 */
	replaceState = null;

	/**
	 * forceUrlchange
	 * @type {Boolean}
	 */
	forceUrlchange = false;

	/**
	 * scope
	 * @type {Object}
	 */
	scope = {
		action: null,
		urlchange: null,
	};

	/**
	 * event
	 * @type {Object}
	 */
	event = {
		action: null,
		urlchange: null,
	};

	/**
	 * mode
	 * @type {Object}
	 */
	mode = {
		stealth: null,
	};

	/**
	 * helper
	 * @type {Object}
	 */
	helper = {
		createURL: null,
		encodeURI: null,
		location: null,
		Promise: null,
		XRegExp: null,
	};

	/**
	 * promise
	 * @type {Object}
	 */
	promise = {}

	/**
	 * _runRoute
	 * @type {Boolean}
	 */
	_runRoute = true;

	/**
	 * _lastFragment
	 * @type {String}
	 */
	_lastFragment = null;

	/**
	 * routes
	 * @type {Object}
	 */
	_routes = {
		static: {},
		dynamic: {},
		cache: {},
	};

	/**
	 * constructor
	 * @param  {Object} config
	 * @return {Router}
	 */
	constructor(config = {}){

		Object.assign(this, config);
		this._init();

	}

	/**
	 * on
	 * @param  {string} event
	 * @param  {function} handler
	 * @return {Promise}
	 */
	on(type = '', handler = null){

		if(!type){
			throw 'Please pass at least type e.g urlchange, Foo, Bar, ...';
		}

		let [ name, route ] = type.split(' ');
		let promise = this.addRouteListener(name, route, handler);

		return promise;

	}

	/**
	* off
	* @param  {string} event
	* @return {undefined}
	*/
	off(event) {

		this.scope.off(event);

	}

	/**
	 * trigger
	 * @param  {String} event
	 * @param  {Object} options
	 * @return {Undefined}
	 */
	trigger(event = '', options = null){

		this.scope.trigger(event, options);

	}

	/**
	 * off
	 * @return {Undefined}
	 */
	destroy() {

		this._removeInternalCoreEvents();
		this._removeRegisteredEvents();

	}

	/**
	 * addRouteListener
	 * @param {string} name
	 * @param {string} route
	 * @param {Promise} promise
	 */
	addRouteListener(name, route, handler = null){

		/**
		 * _addRoute is required for:
		 * 1. if route matched then we can take name for triggering event
		 * 2. if router should destroy, see .destroy()
		 */
		if(route && name) {
			this._addRoute(route, name);
		}

		// create promise if handler not exists
		let promise = null;
		if(name && !handler){
			promise = this._addPromise(name);
		}

		this.scope.on(name, event => {

			let params = event.detail || {};

			if(handler){
				handler(params);
			}
			// resolve promise
			this._promiseHandler(name, params);
		});

		return promise;
	}

	/**
	 * _addRoute
	 * @param {string} route
	 * @param {string} name
	 */
	_addRoute(route, name){

		if(this._routes.dynamic[route] || this._routes.static[route]) {
			throw `route: "${route} already exists!"`;
		}

		if(this._isDynamicURL(route)) {
			let regex = this._convertRouteToXRegexExp(route);
			this._routes.dynamic[route] = this._createRouteObject(name, route, regex);
		} else {
			this._routes.static[route]  = this._createRouteObject(name, route);
		}

	}

	_createRouteObject(name, route, regex = null){

		return {
			name: name,
			route: route,
			regex: regex,
			params: null,
			fragment: null,
			cache: false,
		};

	}

	/**
	 * _addPromise
	 * @param {string} name
	 * @return {Promise} promise
	 */
	_addPromise(name) {

		if(!this.promise[name]){
			this.promise[name] = [];
		}

		let promise = this.Promise(resolve => {
			this.promise[name].push(resolve);
		});

		return promise;

	}

	/**
	 * _getRoutes
	 * @return {Object}
	 */
	_getRoutes(type = 'static'){

		return this._routes[type];

	}

	/**
	 * encodeURI
	 * @param  {String} uri
	 * @return {String}
	 */
	encodeURI(uri){

		return this.helper.encodeURI(uri);

	}

	/**
	 * createURL
	 * @param  {String} href
	 * @return {URL} url
	 */
	createURL(href){

		let url = new this.helper.createURL(href);
		url.fragment = url.href.replace(url.origin, '');
		return url;

	}

	/**
	 * Promise
	 * @param  {Function} handler
	 * @return {Promise}
	 */
	Promise(handler=null) {

		if(!handler){
			throw `Please pass a handler function handler(){}!`;
		}

		return new this.helper.Promise(handler);

	}

	/**
	 * XRegExp
	 */
	get XRegExp() {

		return this.helper.XRegExp;

	}

	/**
	 * _init
	 * @return {Undefined}
	 */
	_init(){

		this._bindInternalCoreEvents();

	}

	/**
	 * _bindInternalCoreEvents
	 * @return {Undefined}
	 */
	_bindInternalCoreEvents(){

		this.scope.on(this.event.action, ::this._onAction);
		this.scope.on(this.event.urlchange, ::this._onUrlchange);

	}

	/**
	 * _removeInternalCoreEvents
	 * @return {undefined}
	 */
	_removeInternalCoreEvents(){

		this.scope.off(this.event.action);
		this.scope.off(this.event.urlchange);

	}

	/**
	 * _removeRegisteredEvents
	 * @return {undefined}
	 */
	_removeRegisteredEvents(){

		let allRoutes = Object.assign({}, this._routes.static, this._routes.dynamic);
		for(let route of Object.keys(allRoutes)){

			let { name } = allRoutes[route];
			this.scope.off(name);

		}

	}

	/**
	 * _onAction
	 * @param  {Event} event
	 * @return {undefined}
	 */
	_onAction(event){

		this._preventDefault(event);
		if(!this._runRoute) {
			return;
		}
		this._stopPropagation(event);

		this._applyActionEvent(event);

	}

	/**
	 * _onActionController
	 * @param  {Element} target
	 * @param  {String} type
	 * @return {Undefined}
	 */
	_applyActionEvent( event ){

		let href =  this._getCurrentHref(event);
		let urlObject = this.createURL(href);
		let urlFragment = urlObject.fragment;

		if(this._urlFragmentChanged(urlFragment)) {
			if(this._isDefinedEventAction(event.type)){
				this.pushState(null, null, this.encodeURI(urlFragment));
			}
			this.scope.trigger(this.event.urlchange, urlObject);
		}
		this._setURLFragment(urlFragment);

	}

	/**
	 * _isDefinedEventAction
	 * @param  {string} event_type
	 * @return {Boolean} _isDefinedEventAction
	 */
	_isDefinedEventAction(event_type){

		let isDefinedEventAction = event_type === this._getDefinedEventAction();
		return isDefinedEventAction;

	}

	/**
	 * _getCurrentHref
	 * @param  {Event} event
	 * @return {string}
	 */
	_getCurrentHref(event) {

		// extract defined_event_action e.g. "click" from "click a"
		let defined_event_action = this._getDefinedEventAction();
		// check if event is triggered by "click" or triggered by back/forward button and return href
		let href = event.type === defined_event_action ? event.target.href : this.helper.location.href;
		return href;

	}

	/**
	 * _getDefinedEventAction
	 * @return {string} defined_event_action
	 */
	_getDefinedEventAction(){

		let [ defined_event_action ] = this.event.action.split(' ');
		return defined_event_action;

	}

	/**
	 * _urlFragmentChanged
	 * @return {boolean}
	 */
	_urlFragmentChanged(currentFragment){

		let urlFragmentChanged = ( currentFragment !== this._lastFragment );
		return urlFragmentChanged;

	}

	/**
	 * _setFragment
	 * @param {string} fragment
	 * @param {undefined}
	 */
	_setURLFragment(fragment) {

		this._lastFragment = fragment;

	}

	/**
	 * _onUrlchange
	 * @param  {Event} event
	 * @return {Undefined}
	 */
	_onUrlchange(event){

		if(!(event.detail instanceof this.helper.createURL)){
			return;
		}

		let { fragment } = event.detail;
		let matchedURL = this._matchURL(fragment);
		if(matchedURL){
			let { name } = matchedURL;
			matchedURL.URL = event.detail;
			this.scope.trigger(name, matchedURL);
		}

	}

	/**
	 * _matchURL description
	 * @param  {string} fragment
	 * @return {object}
	 */
	_matchURL(fragment, cache = true){

		let matchedURLObject = null;

		// load from cache
		matchedURLObject = this._getRouteCache(fragment);
		if(matchedURLObject && cache){
			return matchedURLObject;
		}

		// match static url
		matchedURLObject = this._matchStaticURL(fragment);
		if(matchedURLObject) {
			return matchedURLObject;
		}

		// match dynamic url
		matchedURLObject = this._matchDynamicURL(fragment);
		if(matchedURLObject) {
			this._addRouteCache(fragment, matchedURLObject);
			return matchedURLObject;
		}

		return null;

	}

	/**
	 * _matchStaticURL
	 * @param  {string} fragment
	 * @return {object}
	 */
	_matchStaticURL(fragment){

		let matchedURLObject = this._getRoutes('static')[fragment] || null;
		if(matchedURLObject) {

			// resolve reference
			matchedURLObject = JSON.parse(JSON.stringify(matchedURLObject));

			// build matchedURLObject
			matchedURLObject = Object.assign({}, matchedURLObject, { fragment });
		}

		return matchedURLObject;
	}

	/**
	 * _matchDynamicURL
	 * @param  {string} fragment
	 * @return {object}
	 */
	_matchDynamicURL(fragment){

		for(let route of Object.keys(this._routes.dynamic)){

			// parepare datas for matching
			let routeObject = this._routes.dynamic[route];
			// match regex
			let compiledRegex = this.XRegExp(routeObject.regex);
			let matchedRegex = this.XRegExp.exec(fragment, compiledRegex);

			// return if not matched
			if(matchedRegex === null){
				continue;
			}

			// convert matched to object-object
			let params = {};
			delete matchedRegex.index;
			delete matchedRegex.input;
			for(let param in matchedRegex){
				let value = matchedRegex[param];
				if(this._isNumeric(value)){
					value = parseFloat(value);
				}
				params[param] = value;
			}

			// resolve reference
			routeObject = JSON.parse(JSON.stringify(routeObject));

			// build matchedURLObject
			let matchedURLObject = Object.assign(routeObject, { params }, { fragment });
			return matchedURLObject;

		}

		return null;

	}

	/**
	 * _addRouteCache
	 * @param {string} fragment
	 * @param {object} cacheObject
	 */
	_addRouteCache(fragment, cacheObject){

		cacheObject = JSON.parse(JSON.stringify(cacheObject));
		cacheObject.cache = true;
		this._routes.cache[fragment] = cacheObject;

	}

	/**
	 * _getRouteCache
	 * @param  {string} fragment
	 * @return {object} cacheRouteObject
	 */
	_getRouteCache(fragment) {

		return this._routes.cache[fragment] || null;

	}

	/**
	 * _promiseHandler
	 * @param  {string} type
	 * @param  {Evemt} event
	 * @return {undefined}
	 */
	_promiseHandler(type, event){

		if(!this.promise[type]){
			return;
		}

		let resolve;
		// promise can resolved only once therefore shift()
		while((resolve = this.promise[type].shift()) !== undefined){
			resolve(event);
		}

	}

	/**
	 * _convertRouteToXRegexExp
	 * @param  {string} url
	 * @return {XRegExp-String} url
	 */
	_convertRouteToXRegexExp(url = ''){

		let variableURLRegex = this.XRegExp('{{(?<variableURL>[a-z]+)}}', 'g');
		url = url.replace(/\//g, '\\/');
		url = this.XRegExp.replace(url, variableURLRegex, '(?<${variableURL}>.*?)');

		return url;

	}

	/**
	 * _isDynamicURL
	 * @param  {string} url
	 * @return {Boolean} _isDynamicURL;
	 */
	_isDynamicURL(url = '') {

		let isDynamicURL = /{{[a-z]+}}/.test(url);
		return isDynamicURL;

	}

	/**
	 * _preventDefault
	 * @param  {Event} event
	 * @return {undefined}
	 */
	_preventDefault(event){

		if(event instanceof Event){
			event.preventDefault();
		}

	}

	/**
	 * _stopPropagation
	 * @param  {Event} event
	 * @return {undefined}
	 */
	_stopPropagation(event){

		if(this.mode.stealth && event instanceof Event){
			event.stopPropagation();
		}

	}

	/**
	 * _isNumeric
	 * @param  {string|number} value
	 * @return {Boolean}
	 */
	_isNumeric(value) {

		return /^[+-]?\d+(\.\d+)?$/.test(value);

	}

	/**
	 * whoami
	 * @param  {String} path
	 * @return {object}
	 */
	whoami(url){

		let matchedUrlObject =  this._matchURL(url, false);
		return matchedUrlObject;

	}

	/**
	 * constructURL
	 * @param  {String} route
	 * @param  {Object} params
	 * @return {String}
	 */
	constructURL(route, params = {}){

	}

	/**
	 * redirect
	 * @return {undefined}
	 */
	redirect(){

	}

	/**
	 * start
	 * @return {undefined}
	 */
	start(){

		this._runRoute = true;

	}

	/**
	 * stop
	 * @return {Undefined}
	 */
	stop(){

		this._runRoute = false;

	}
}
<commit_msg>src/libs/router.js: remove part of resolve reference<commit_after>
export class Router {

	/**
	 * foward()
	 * @type {History}
	 */
	forward = null;

	/**
	 * back()
	 * @type {History}
	 */
	back = null;

	/**
	 * _pushState()
	 * @type {History}
	 */
	pushState = null;

	/**
	 * _replaceState()
	 * @type {History}
	 */
	replaceState = null;

	/**
	 * forceUrlchange
	 * @type {Boolean}
	 */
	forceUrlchange = false;

	/**
	 * scope
	 * @type {Object}
	 */
	scope = {
		action: null,
		urlchange: null,
	};

	/**
	 * event
	 * @type {Object}
	 */
	event = {
		action: null,
		urlchange: null,
	};

	/**
	 * mode
	 * @type {Object}
	 */
	mode = {
		stealth: null,
	};

	/**
	 * helper
	 * @type {Object}
	 */
	helper = {
		createURL: null,
		encodeURI: null,
		location: null,
		Promise: null,
		XRegExp: null,
	};

	/**
	 * promise
	 * @type {Object}
	 */
	promise = {}

	/**
	 * _runRoute
	 * @type {Boolean}
	 */
	_runRoute = true;

	/**
	 * _lastFragment
	 * @type {String}
	 */
	_lastFragment = null;

	/**
	 * routes
	 * @type {Object}
	 */
	_routes = {
		static: {},
		dynamic: {},
		cache: {},
	};

	/**
	 * constructor
	 * @param  {Object} config
	 * @return {Router}
	 */
	constructor(config = {}){

		Object.assign(this, config);
		this._init();

	}

	/**
	 * on
	 * @param  {string} event
	 * @param  {function} handler
	 * @return {Promise}
	 */
	on(type = '', handler = null){

		if(!type){
			throw 'Please pass at least type e.g urlchange, Foo, Bar, ...';
		}

		let [ name, route ] = type.split(' ');
		let promise = this.addRouteListener(name, route, handler);

		return promise;

	}

	/**
	* off
	* @param  {string} event
	* @return {undefined}
	*/
	off(event) {

		this.scope.off(event);

	}

	/**
	 * trigger
	 * @param  {String} event
	 * @param  {Object} options
	 * @return {Undefined}
	 */
	trigger(event = '', options = null){

		this.scope.trigger(event, options);

	}

	/**
	 * off
	 * @return {Undefined}
	 */
	destroy() {

		this._removeInternalCoreEvents();
		this._removeRegisteredEvents();

	}

	/**
	 * addRouteListener
	 * @param {string} name
	 * @param {string} route
	 * @param {Promise} promise
	 */
	addRouteListener(name, route, handler = null){

		/**
		 * _addRoute is required for:
		 * 1. if route matched then we can take name for triggering event
		 * 2. if router should destroy, see .destroy()
		 */
		if(route && name) {
			this._addRoute(route, name);
		}

		// create promise if handler not exists
		let promise = null;
		if(name && !handler){
			promise = this._addPromise(name);
		}

		this.scope.on(name, event => {

			let params = event.detail || {};

			if(handler){
				handler(params);
			}
			// resolve promise
			this._promiseHandler(name, params);
		});

		return promise;
	}

	/**
	 * _addRoute
	 * @param {string} route
	 * @param {string} name
	 */
	_addRoute(route, name){

		if(this._routes.dynamic[route] || this._routes.static[route]) {
			throw `route: "${route} already exists!"`;
		}

		if(this._isDynamicURL(route)) {
			let regex = this._convertRouteToXRegexExp(route);
			this._routes.dynamic[route] = this._createRouteObject(name, route, regex);
		} else {
			this._routes.static[route]  = this._createRouteObject(name, route);
		}

	}

	_createRouteObject(name, route, regex = null){

		return {
			name: name,
			route: route,
			regex: regex,
			params: null,
			fragment: null,
			cache: false,
		};

	}

	/**
	 * _addPromise
	 * @param {string} name
	 * @return {Promise} promise
	 */
	_addPromise(name) {

		if(!this.promise[name]){
			this.promise[name] = [];
		}

		let promise = this.Promise(resolve => {
			this.promise[name].push(resolve);
		});

		return promise;

	}

	/**
	 * _getRoutes
	 * @return {Object}
	 */
	_getRoutes(type = 'static'){

		return this._routes[type];

	}

	/**
	 * encodeURI
	 * @param  {String} uri
	 * @return {String}
	 */
	encodeURI(uri){

		return this.helper.encodeURI(uri);

	}

	/**
	 * createURL
	 * @param  {String} href
	 * @return {URL} url
	 */
	createURL(href){

		let url = new this.helper.createURL(href);
		url.fragment = url.href.replace(url.origin, '');
		return url;

	}

	/**
	 * Promise
	 * @param  {Function} handler
	 * @return {Promise}
	 */
	Promise(handler=null) {

		if(!handler){
			throw `Please pass a handler function handler(){}!`;
		}

		return new this.helper.Promise(handler);

	}

	/**
	 * XRegExp
	 */
	get XRegExp() {

		return this.helper.XRegExp;

	}

	/**
	 * _init
	 * @return {Undefined}
	 */
	_init(){

		this._bindInternalCoreEvents();

	}

	/**
	 * _bindInternalCoreEvents
	 * @return {Undefined}
	 */
	_bindInternalCoreEvents(){

		this.scope.on(this.event.action, ::this._onAction);
		this.scope.on(this.event.urlchange, ::this._onUrlchange);

	}

	/**
	 * _removeInternalCoreEvents
	 * @return {undefined}
	 */
	_removeInternalCoreEvents(){

		this.scope.off(this.event.action);
		this.scope.off(this.event.urlchange);

	}

	/**
	 * _removeRegisteredEvents
	 * @return {undefined}
	 */
	_removeRegisteredEvents(){

		let allRoutes = Object.assign({}, this._routes.static, this._routes.dynamic);
		for(let route of Object.keys(allRoutes)){

			let { name } = allRoutes[route];
			this.scope.off(name);

		}

	}

	/**
	 * _onAction
	 * @param  {Event} event
	 * @return {undefined}
	 */
	_onAction(event){

		this._preventDefault(event);
		if(!this._runRoute) {
			return;
		}
		this._stopPropagation(event);

		this._applyActionEvent(event);

	}

	/**
	 * _onActionController
	 * @param  {Element} target
	 * @param  {String} type
	 * @return {Undefined}
	 */
	_applyActionEvent( event ){

		let href =  this._getCurrentHref(event);
		let urlObject = this.createURL(href);
		let urlFragment = urlObject.fragment;

		if(this._urlFragmentChanged(urlFragment)) {
			if(this._isDefinedEventAction(event.type)){
				this.pushState(null, null, this.encodeURI(urlFragment));
			}
			this.scope.trigger(this.event.urlchange, urlObject);
		}
		this._setURLFragment(urlFragment);

	}

	/**
	 * _isDefinedEventAction
	 * @param  {string} event_type
	 * @return {Boolean} _isDefinedEventAction
	 */
	_isDefinedEventAction(event_type){

		let isDefinedEventAction = event_type === this._getDefinedEventAction();
		return isDefinedEventAction;

	}

	/**
	 * _getCurrentHref
	 * @param  {Event} event
	 * @return {string}
	 */
	_getCurrentHref(event) {

		// extract defined_event_action e.g. "click" from "click a"
		let defined_event_action = this._getDefinedEventAction();
		// check if event is triggered by "click" or triggered by back/forward button and return href
		let href = event.type === defined_event_action ? event.target.href : this.helper.location.href;
		return href;

	}

	/**
	 * _getDefinedEventAction
	 * @return {string} defined_event_action
	 */
	_getDefinedEventAction(){

		let [ defined_event_action ] = this.event.action.split(' ');
		return defined_event_action;

	}

	/**
	 * _urlFragmentChanged
	 * @return {boolean}
	 */
	_urlFragmentChanged(currentFragment){

		let urlFragmentChanged = ( currentFragment !== this._lastFragment );
		return urlFragmentChanged;

	}

	/**
	 * _setFragment
	 * @param {string} fragment
	 * @param {undefined}
	 */
	_setURLFragment(fragment) {

		this._lastFragment = fragment;

	}

	/**
	 * _onUrlchange
	 * @param  {Event} event
	 * @return {Undefined}
	 */
	_onUrlchange(event){

		if(!(event.detail instanceof this.helper.createURL)){
			return;
		}

		let { fragment } = event.detail;
		let matchedURL = this._matchURL(fragment);
		if(matchedURL){
			let { name } = matchedURL;
			matchedURL.URL = event.detail;
			this.scope.trigger(name, matchedURL);
		}

	}

	/**
	 * _matchURL description
	 * @param  {string} fragment
	 * @return {object}
	 */
	_matchURL(fragment, cache = true){

		let matchedURLObject = null;

		// load from cache
		matchedURLObject = this._getRouteCache(fragment);
		if(matchedURLObject && cache){
			return matchedURLObject;
		}

		// match static url
		matchedURLObject = this._matchStaticURL(fragment);
		if(matchedURLObject) {
			return matchedURLObject;
		}

		// match dynamic url
		matchedURLObject = this._matchDynamicURL(fragment);
		if(matchedURLObject) {
			this._addRouteCache(fragment, matchedURLObject);
			return matchedURLObject;
		}

		return null;

	}

	/**
	 * _matchStaticURL
	 * @param  {string} fragment
	 * @return {object}
	 */
	_matchStaticURL(fragment){

		let matchedURLObject = this._getRoutes('static')[fragment] || null;
		if(matchedURLObject) {

			// build matchedURLObject
			matchedURLObject = Object.assign({}, matchedURLObject, { fragment });
		}

		return matchedURLObject;
	}

	/**
	 * _matchDynamicURL
	 * @param  {string} fragment
	 * @return {object}
	 */
	_matchDynamicURL(fragment){

		for(let route of Object.keys(this._routes.dynamic)){

			// parepare datas for matching
			let routeObject = this._routes.dynamic[route];
			// match regex
			let compiledRegex = this.XRegExp(routeObject.regex);
			let matchedRegex = this.XRegExp.exec(fragment, compiledRegex);

			// return if not matched
			if(matchedRegex === null){
				continue;
			}

			// convert matched to object-object
			let params = {};
			delete matchedRegex.index;
			delete matchedRegex.input;
			for(let param in matchedRegex){
				let value = matchedRegex[param];
				if(this._isNumeric(value)){
					value = parseFloat(value);
				}
				params[param] = value;
			}

			// build matchedURLObject
			let matchedURLObject = Object.assign({}, routeObject, { params, fragment });
			return matchedURLObject;

		}

		return null;

	}

	/**
	 * _addRouteCache
	 * @param {string} fragment
	 * @param {object} cacheObject
	 */
	_addRouteCache(fragment, cacheObject){

		cacheObject = JSON.parse(JSON.stringify(cacheObject));
		cacheObject.cache = true;
		this._routes.cache[fragment] = cacheObject;

	}

	/**
	 * _getRouteCache
	 * @param  {string} fragment
	 * @return {object} cacheRouteObject
	 */
	_getRouteCache(fragment) {

		return this._routes.cache[fragment] || null;

	}

	/**
	 * _promiseHandler
	 * @param  {string} type
	 * @param  {Evemt} event
	 * @return {undefined}
	 */
	_promiseHandler(type, event){

		if(!this.promise[type]){
			return;
		}

		let resolve;
		// promise can resolved only once therefore shift()
		while((resolve = this.promise[type].shift()) !== undefined){
			resolve(event);
		}

	}

	/**
	 * _convertRouteToXRegexExp
	 * @param  {string} url
	 * @return {XRegExp-String} url
	 */
	_convertRouteToXRegexExp(url = ''){

		let variableURLRegex = this.XRegExp('{{(?<variableURL>[a-z]+)}}', 'g');
		url = url.replace(/\//g, '\\/');
		url = this.XRegExp.replace(url, variableURLRegex, '(?<${variableURL}>.*?)');

		return url;

	}

	/**
	 * _isDynamicURL
	 * @param  {string} url
	 * @return {Boolean} _isDynamicURL;
	 */
	_isDynamicURL(url = '') {

		let isDynamicURL = /{{[a-z]+}}/.test(url);
		return isDynamicURL;

	}

	/**
	 * _preventDefault
	 * @param  {Event} event
	 * @return {undefined}
	 */
	_preventDefault(event){

		if(event instanceof Event){
			event.preventDefault();
		}

	}

	/**
	 * _stopPropagation
	 * @param  {Event} event
	 * @return {undefined}
	 */
	_stopPropagation(event){

		if(this.mode.stealth && event instanceof Event){
			event.stopPropagation();
		}

	}

	/**
	 * _isNumeric
	 * @param  {string|number} value
	 * @return {Boolean}
	 */
	_isNumeric(value) {

		return /^[+-]?\d+(\.\d+)?$/.test(value);

	}

	/**
	 * whoami
	 * @param  {String} path
	 * @return {object}
	 */
	whoami(url){

		let matchedUrlObject =  this._matchURL(url, false);
		return matchedUrlObject;

	}

	/**
	 * constructURL
	 * @param  {String} route
	 * @param  {Object} params
	 * @return {String}
	 */
	constructURL(route, params = {}){

	}

	/**
	 * redirect
	 * @return {undefined}
	 */
	redirect(){

	}

	/**
	 * start
	 * @return {undefined}
	 */
	start(){

		this._runRoute = true;

	}

	/**
	 * stop
	 * @return {Undefined}
	 */
	stop(){

		this._runRoute = false;

	}
}
<|endoftext|>


<commit_before>var util = require('util');
var config = require('./config');
var fs = require('fs');
var path = require('path');

module.exports = function() {
  // check for log directory
  var logsDir = './logs/';
  fs.exists(logsDir, function(exists) {
    if ( !exists ) {
      fs.mkdir(logsDir, function(err) {
        if ( !!err ) {
          console.log("Could not create log directory.");
          throw err;
        }
      });
    }
  });

  console.log = function() {

    var string = "";
    for ( var i = 0; i < arguments.length; i++ ) {
      var argument = arguments[i];
      if ( typeof argument == "object" ) {
        string += util.inspect(argument);
      } else {
        string += argument;
      }
      string += " ";
    }
    string = string.trim();

    util.log(string);

    // log to file, if on production
    if ( process.env.NODE_ENV === 'production' ) {
      var d = new Date();

      date = d.getFullYear() + "." + ( d.getMonth() + 1 ) + "." + d.getDate();
      var currentLogName = util.format(config.logname, date);
      currentLogName = path.join(logsDir, currentLogName);
      time = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();

      string += '\n';
      string = time + ': ' + string;
      fs.appendFile(currentLogName, string, function(err) {
        if ( !!err ) {
          throw err;
        }
      });
    }

  };
};
<commit_msg>Writing nice times into log now.<commit_after>var util = require('util');
var config = require('./config');
var fs = require('fs');
var path = require('path');

module.exports = function() {
  // check for log directory
  var logsDir = './logs/';
  fs.exists(logsDir, function(exists) {
    if ( !exists ) {
      fs.mkdir(logsDir, function(err) {
        if ( !!err ) {
          console.log("Could not create log directory.");
          throw err;
        }
      });
    }
  });

  function zerofy(num) {
    if ( num < 10 ) {
      return "0" + num;
    } else {
      return num;
    }
  }

  console.log = function() {

    var string = "";
    for ( var i = 0; i < arguments.length; i++ ) {
      var argument = arguments[i];
      if ( typeof argument == "object" ) {
        string += util.inspect(argument);
      } else {
        string += argument;
      }
      string += " ";
    }
    string = string.trim();

    util.log(string);

    // log to file, if on production
    if ( process.env.NODE_ENV === 'production' ) {
      var d = new Date();

      date = d.getFullYear() + "." + ( d.getMonth() + 1 ) + "." + d.getDate();
      var currentLogName = util.format(config.logname, date);
      currentLogName = path.join(logsDir, currentLogName);
      time = zerofy(d.getHours()) + ":" + zerofy(d.getMinutes()) + ":" + zerofy(d.getSeconds());

      string += '\n';
      string = time + ': ' + string;
      fs.appendFile(currentLogName, string, function(err) {
        if ( !!err ) {
          throw err;
        }
      });
    }

  };
};
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 David Heidrich, BowlingX <me@bowlingx.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
'use strict';

require('styles/main.scss');

import {Client, EVENT_MESSAGE} from 'Client';
import Marklib from 'marklib';
import Util from 'flexcss/src/main/util/Util';

const ATTR_COMMENTP = 'data-commentp';

const TIMEOUT = 50;

document.addEventListener('DOMContentLoaded', () => {

    const node = document.querySelector(`[${ATTR_COMMENTP}]`);
    if (node) {
        const channel = node.getAttribute(ATTR_COMMENTP);
        Client.connect(channel).then((client) => {
            let timeout;

            // append actions
            const appContainer = document.createElement('div');
            appContainer.setAttribute('data-commentp-app', Util.guid());
            appContainer.innerHTML = require('templates/selection-action.html');
            document.body.appendChild(appContainer);

            const actionContainer = appContainer.querySelector('[data-commentp-action]');

            const event = 'onmouseup' in document ? 'mouseup' : 'selectionchange';

            const clickEvent = 'ontouchend' in document ? 'touchend' : 'click';
            document.addEventListener(clickEvent, (e) => {
                const selection = document.getSelection();
                if (selection.rangeCount === 0 || selection.toString().trim() === '') {
                    if (!Util.isPartOfNode(e.target, actionContainer)) {
                        actionContainer.classList.remove('open');
                    }
                }
            });

            document.addEventListener(event, () => {
                clearTimeout(timeout);
                timeout = setTimeout(() => {
                    const selection = document.getSelection();
                    if (selection.rangeCount > 0) {
                        const range = selection.getRangeAt(0),
                            isPartOfNode = Util.isPartOfNode(range.commonAncestorContainer, node);
                        if (isPartOfNode) {
                            var clientRect = range.getBoundingClientRect();
                            if (clientRect.width > 0) {
                                /*const marking = new Marklib.Rendering(document, 'marking', node);
                                 if (range.startContainer.nodeType === Node.TEXT_NODE
                                 && range.endContainer.nodeType === Node.TEXT_NODE) {
                                 const result = marking.renderWithRange(range);
                                 client.action('mark', result);
                                 selection.removeAllRanges();
                                 }*/

                                actionContainer.classList.add('open');
                                Util.setupPositionNearby(range, actionContainer, document.body, true, true);
                            } else {
                                actionContainer.classList.remove('open');
                            }
                        }
                    } else {
                        actionContainer.classList.remove('open');
                    }
                }, TIMEOUT);
            });

            client.on(EVENT_MESSAGE, (msg) => {
                // FIXME: Create a queue :)
                const renderer = new Marklib.Rendering(document, 'marking-remote', node);
                renderer.renderWithResult(msg);
            });
        });
    }

});

export default Client;
<commit_msg>remove checks<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 David Heidrich, BowlingX <me@bowlingx.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
'use strict';

require('styles/main.scss');

import {Client, EVENT_MESSAGE} from 'Client';
import Marklib from 'marklib';
import Util from 'flexcss/src/main/util/Util';

const ATTR_COMMENTP = 'data-commentp';

const TIMEOUT = 50;

document.addEventListener('DOMContentLoaded', () => {

    const node = document.querySelector(`[${ATTR_COMMENTP}]`);
    if (node) {
        const channel = node.getAttribute(ATTR_COMMENTP);
        Client.connect(channel).then((client) => {
            let timeout;

            // append actions
            const appContainer = document.createElement('div');
            appContainer.setAttribute('data-commentp-app', Util.guid());
            appContainer.innerHTML = require('templates/selection-action.html');
            document.body.appendChild(appContainer);

            const actionContainer = appContainer.querySelector('[data-commentp-action]');

            const event = 'onmouseup' in document ? 'mouseup' : 'selectionchange';

            const clickEvent = 'ontouchend' in document ? 'touchend' : 'click';
            document.addEventListener(clickEvent, (e) => {
                if (!Util.isPartOfNode(e.target, actionContainer)) {
                    actionContainer.classList.remove('open');
                }
            });

            document.addEventListener(event, () => {
                clearTimeout(timeout);
                timeout = setTimeout(() => {
                    const selection = document.getSelection();
                    if (selection.rangeCount > 0) {
                        const range = selection.getRangeAt(0),
                            isPartOfNode = Util.isPartOfNode(range.commonAncestorContainer, node);
                        if (isPartOfNode) {
                            var clientRect = range.getBoundingClientRect();
                            if (clientRect.width > 0) {
                                /*const marking = new Marklib.Rendering(document, 'marking', node);
                                 if (range.startContainer.nodeType === Node.TEXT_NODE
                                 && range.endContainer.nodeType === Node.TEXT_NODE) {
                                 const result = marking.renderWithRange(range);
                                 client.action('mark', result);
                                 selection.removeAllRanges();
                                 }*/

                                actionContainer.classList.add('open');
                                Util.setupPositionNearby(range, actionContainer, document.body, true, true);
                            } else {
                                actionContainer.classList.remove('open');
                            }
                        }
                    } else {
                        actionContainer.classList.remove('open');
                    }
                }, TIMEOUT);
            });

            client.on(EVENT_MESSAGE, (msg) => {
                // FIXME: Create a queue :)
                const renderer = new Marklib.Rendering(document, 'marking-remote', node);
                renderer.renderWithResult(msg);
            });
        });
    }

});

export default Client;
<|endoftext|>


<commit_before>const fs = require('fs')

const Cards = require('../../data/cards')
const Sets = require('../../data/sets')
const {code, cards, size} = require('../../data/custom')

if (Sets[code]) {
  console.log('already processed, exiting')
  process.exit()
}

const COLORS = {
  W: 'white',
  U: 'blue',
  B: 'black',
  R: 'red',
  G: 'green'
}

const set = Sets[code] = {
  common: [],
  uncommon: [],
  rare: [],
  mythic: [],
  size: size || 10
}

cards.forEach(rawCard => {
  const rarity = rawCard.rarity.split(' ')[0].toLowerCase()
  if (rarity === 'basic')
    return

  const name = rawCard.name.toLowerCase()
  set[rarity].push(name)

  const sets = {[code]: { rarity, url: rawCard.url }}
  if (Cards[name])
    return Cards[name].sets[code] = sets[code]

  const {colors} = rawCard
  const color
    = colors.length === 1 ? COLORS[colors[0]]
    : !colors.length ? 'colorless'
    : 'multicolor'

  Cards[name] = {
    cmc: rawCard.cmc,
    color,
    name,
    type: rawCard.type.split(' ')[0],
    sets
  }
})

fs.writeFileSync('data/cards.json', JSON.stringify(Cards, null, 2))
fs.writeFileSync('data/sets.json', JSON.stringify(Sets, null, 2))
<commit_msg>fix card names<commit_after>const fs = require('fs')

const Cards = require('../../data/cards')
const Sets = require('../../data/sets')
const {code, cards, size} = require('../../data/custom')

if (Sets[code]) {
  console.log('already processed, exiting')
  process.exit()
}

const COLORS = {
  W: 'white',
  U: 'blue',
  B: 'black',
  R: 'red',
  G: 'green'
}

const set = Sets[code] = {
  common: [],
  uncommon: [],
  rare: [],
  mythic: [],
  size: size || 10
}

cards.forEach(rawCard => {
  const rarity = rawCard.rarity.split(' ')[0].toLowerCase()
  if (rarity === 'basic')
    return

  const {name} = rawCard
  const lc = name.toLowerCase()
  set[rarity].push(lc)

  const sets = {[code]: { rarity, url: rawCard.url }}
  if (Cards[lc])
    return Cards[lc].sets[code] = sets[code]

  const {colors} = rawCard
  const color
    = colors.length === 1 ? COLORS[colors[0]]
    : !colors.length ? 'colorless'
    : 'multicolor'

  Cards[lc] = {
    cmc: rawCard.cmc,
    color,
    name,
    type: rawCard.type.split(' ')[0],
    sets
  }
})

fs.writeFileSync('data/cards.json', JSON.stringify(Cards, null, 2))
fs.writeFileSync('data/sets.json', JSON.stringify(Sets, null, 2))
<|endoftext|>


<commit_before>import Coordinate from '../geo/Coordinate';
import Point from '../geo/Point';
import Map from './Map';
import { isFunction } from '../core/util';

Map.include(/** @lends Map.prototype */ {

    /**
     * Pan to the given coordinate
     * @param {Coordinate} coordinate - coordinate to pan to
     * @param {Object} [options=null] - pan options
     * @param {Boolean} [options.animation=null] - whether pan with animation
     * @param {Boolean} [options.duration=600] - pan animation duration
     * @return {Map} this
     */
    panTo: function (coordinate, options = {}, step) {
        if (!coordinate) {
            return this;
        }
        if (isFunction(options)) {
            step = options;
            options = {};
        }
        coordinate = new Coordinate(coordinate);
        if (typeof (options['animation']) === 'undefined' || options['animation']) {
            return this._panAnimation(coordinate, options['duration'], step);
        } else {
            this.setCenter(coordinate);
            return this;
        }
    },

    /**
     * Pan the map by the give point
     * @param  {Point} point - distance to pan, in pixel
     * @param {Object} [options=null] - pan options
     * @param {Boolean} [options.animation=null] - whether pan with animation
     * @param {Boolean} [options.duration=600] - pan animation duration
     * @return {Map} this
     */
    panBy: function (offset, options = {}, step) {
        if (!offset) {
            return this;
        }
        if (isFunction(options)) {
            step = options;
            options = {};
        }
        offset = new Point(offset).multi(-1);
        this.onMoveStart();
        if (typeof (options['animation']) === 'undefined' || options['animation']) {
            const target = this.locateByPoint(this.getCenter(), offset.x, offset.y);
            this._panAnimation(target, options['duration'], step);
        } else {
            this._offsetCenterByPixel(offset);
            this.onMoveEnd(this._parseEventFromCoord(this.getCenter()));
        }
        return this;
    },

    _panAnimation: function (target, t, cb) {
        return this.animateTo({
            'center' : target
        }, {
            'duration' : t || this.options['panAnimationDuration'],
        }, cb);
    }
});
<commit_msg>fix map.panBy direction, close #879 (#880)<commit_after>import Coordinate from '../geo/Coordinate';
import Point from '../geo/Point';
import Map from './Map';
import { isFunction } from '../core/util';

Map.include(/** @lends Map.prototype */ {

    /**
     * Pan to the given coordinate
     * @param {Coordinate} coordinate - coordinate to pan to
     * @param {Object} [options=null] - pan options
     * @param {Boolean} [options.animation=null] - whether pan with animation
     * @param {Boolean} [options.duration=600] - pan animation duration
     * @return {Map} this
     */
    panTo: function (coordinate, options = {}, step) {
        if (!coordinate) {
            return this;
        }
        if (isFunction(options)) {
            step = options;
            options = {};
        }
        coordinate = new Coordinate(coordinate);
        if (typeof (options['animation']) === 'undefined' || options['animation']) {
            return this._panAnimation(coordinate, options['duration'], step);
        } else {
            this.setCenter(coordinate);
            return this;
        }
    },

    /**
     * Pan the map by the give point
     * @param  {Point} point - distance to pan, in pixel
     * @param {Object} [options=null] - pan options
     * @param {Boolean} [options.animation=null] - whether pan with animation
     * @param {Boolean} [options.duration=600] - pan animation duration
     * @return {Map} this
     */
    panBy: function (offset, options = {}, step) {
        if (!offset) {
            return this;
        }
        if (isFunction(options)) {
            step = options;
            options = {};
        }
        offset = new Point(offset);
        this.onMoveStart();
        if (typeof (options['animation']) === 'undefined' || options['animation']) {
            offset = offset.multi(-1);
            const target = this.locateByPoint(this.getCenter(), offset.x, offset.y);
            this._panAnimation(target, options['duration'], step);
        } else {
            this._offsetCenterByPixel(offset);
            this.onMoveEnd(this._parseEventFromCoord(this.getCenter()));
        }
        return this;
    },

    _panAnimation: function (target, t, cb) {
        return this.animateTo({
            'center': target
        }, {
            'duration': t || this.options['panAnimationDuration'],
        }, cb);
    }
});
<|endoftext|>


<commit_before>const {
	ADD_LIFX_NETWORK_LIGHT,
	REMOVE_LIFX_NETWORK_LIGHT,
} = require('$redux/lifxNetwork/actions')

module.exports = [
	ADD_LIFX_NETWORK_LIGHT,
	REMOVE_LIFX_NETWORK_LIGHT,
]
<commit_msg>Blacklisted ADD_GROUP<commit_after>const {
	ADD_GROUP,
} = require('$redux/groups/actions')

const {
	ADD_LIFX_NETWORK_LIGHT,
	REMOVE_LIFX_NETWORK_LIGHT,
} = require('$redux/lifxNetwork/actions')

module.exports = [
	ADD_GROUP,
	ADD_LIFX_NETWORK_LIGHT,
	REMOVE_LIFX_NETWORK_LIGHT,
]
<|endoftext|>


<commit_before>// ==UserScript==
// @id             iitc-plugin-region-score-lead@hansolo669
// @name           IITC plugin: region score lead
// @category       Tweaks
// @version        0.0.1
// @namespace      https://github.com/hansolo669/iitc-tweaks
// @updateURL      http://www.reallyawesomedomain.com/iitc-tweaks/region-score-lead.meta.js
// @downloadURL    http://www.reallyawesomedomain.com/iitc-tweaks/region-score-lead.user.js
// @description    Small modification to the region scores to show the current mu lead.
// @include        https://www.ingress.com/intel*
// @include        http://www.ingress.com/intel*
// @match          https://www.ingress.com/intel*
// @match          http://www.ingress.com/intel*
// @include        https://www.ingress.com/mission/*
// @include        http://www.ingress.com/mission/*
// @match          https://www.ingress.com/mission/*
// @match          http://www.ingress.com/mission/*
// @grant          none
// ==/UserScript==

function wrapper(plugin_info) {
// ensure plugin framework is there, even if iitc is not yet loaded
if(typeof window.plugin !== 'function') window.plugin = function() {};

// PLUGIN START ////////////////////////////////////////////////////////
var setup = function() {
// we stick all this stuff in the setup function to ensure it loads *after* IITC has booted
  window.regionScoreboard = function() {
    // TODO: rather than just load the region scores for the center of the map, display a list of regions in the current view
    // and let the user select one (with automatic selection when just one region, and limited to close enough zooms so list size is reasonable)
    var latLng = map.getCenter();

    var latE6 = Math.round(latLng.lat*1E6);
    var lngE6 = Math.round(latLng.lng*1E6);

    var dlg = dialog({title:'Region scores',html:'Loading regional scores...',width:450,minHeight:320});

    window.postAjax('getRegionScoreDetails', {latE6:latE6,lngE6:lngE6}, function(res){regionScoreboardSuccess(res,dlg);}, function(){regionScoreboardFailure(dlg);});
  }

  function regionScoreboardFailure(dlg) {
    dlg.html('Failed to load region scores - try again');
  }

  function regionScoreboardScoreHistoryChart(result, logscale) {
    // svg area 400x130. graph area 350x100, offset to 40,10

    if(!Math.log10)
      Math.log10 = function(x) { return Math.log(x) / Math.LN10; };

    var max = Math.max(result.gameScore[0],result.gameScore[1],10); //NOTE: ensure a min of 10 for the graph
    var items = []; //we'll copy the items to an array indexed by checkpoint number - easier to access!
    for (var i=0; i<result.scoreHistory.length; i++) {
      max = Math.max(max, result.scoreHistory[i][1], result.scoreHistory[i][2]); //note: index 0 is the checkpoint number here
      items[result.scoreHistory[i][0]] = [result.scoreHistory[i][1], result.scoreHistory[i][2]];
    }

    // scale up maximum a little, so graph isn't squashed right against upper edge
    max *= 1.09;

    // 0 cannot be displayed on a log scale, so we set the minimum to 0.001 and divide by lg(0.001)=-3
    var scale = logscale
      ? function(y) { return  10 - Math.log10(Math.max(0.001,y/max)) / 3 * 100; }
      : function(y) { return 110-y/max*100; };

    var teamPaths = [[],[]];
    var otherSvg = [];

    for (var i=0; i<items.length; i++) {
      var x=i*10+40;
      if (items[i] !== undefined) {
        // paths
        if (i>0 && items[i-1] !== undefined) {
          for (var t=0; t<2; t++) {
            teamPaths[t].push('M'+(x-10)+','+scale(items[i-1][t])+' L'+x+','+scale(items[i][t]));
          }
        }
        // markers
        otherSvg.push('<g title="test" class="checkpoint" data-cp="'+i+'" data-enl="'+items[i][0]+'" data-res="'+items[i][1]+'">');
        otherSvg.push('<rect x="'+(i*10+35)+'" y="10" width="10" height="100" fill="black" fill-opacity="0" />');
        for (var t=0; t<2; t++) {
          var col = t==0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
          otherSvg.push('<circle cx="'+x+'" cy="'+scale(items[i][t])+'" r="3" stroke-width="1" stroke="'+col+'" fill="'+col+'" fill-opacity="0.5" />');
        }
        otherSvg.push('</g>');
      }
    }

    var paths = '<path d="M40,110 L40,10 M40,110 L390,110" stroke="#fff" />';

    // graph tickmarks - horizontal
    var ticks = [];
    for (var i=5; i<=35; i+=5) {
      var x=i*10+40;
      ticks.push('M'+x+',10 L'+x+',110');
      otherSvg.push('<text x="'+x+'" y="125" font-size="12" font-family="Roboto, Helvetica, sans-serif" text-anchor="middle" fill="#fff">'+i+'</text>');
    }

    // vertical
    // first we calculate the power of 10 that is smaller than the max limit
    var vtickStep = Math.pow(10,Math.floor(Math.log10(max)));
    var vticks = [];
    if(logscale) {
      for(var i=0;i<4;i++) {
        vticks.push(vtickStep);
        vtickStep /= 10;
      }
    } else {
      // this could be between 1 and 10 grid lines - so we adjust to give nicer spacings
      if (vtickStep < (max/5)) {
        vtickStep *= 2;
      } else if (vtickStep > (max/2)) {
        vtickStep /= 2;
      }
      for (var i=vtickStep; i<=max; i+=vtickStep) {
        vticks.push(i);
      }
    }
    vticks.forEach(function(i) {
      var y = scale(i);

      ticks.push('M40,'+y+' L390,'+y);

      var istr = i>=1000000000 ? i/1000000000+'B' : i>=1000000 ? i/1000000+'M' : i>=1000 ? i/1000+'k' : i;
      otherSvg.push('<text x="35" y="'+y+'" font-size="12" font-family="Roboto, Helvetica, sans-serif" text-anchor="end" fill="#fff">'+istr+'</text>');
    });

    paths += '<path d="'+ticks.join(' ')+'" stroke="#fff" opacity="0.3" />;'

    for (var t=0; t<2; t++) {
      var col = t==0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
      if (teamPaths[t].length > 0) {
        paths += '<path d="'+teamPaths[t].join(' ')+'" stroke="'+col+'" />';
      }

      var y = scale(result.gameScore[t]);
      paths += '<path d="M40,'+y+' L390,'+y+'" stroke="'+col+'" stroke-dasharray="3,2" opacity="0.8" />';
    }

    var svg = '
<svg width="400" height="130">'
             +'<rect x="0" y="0" width="400" height="130" stroke="#FFCE00" fill="#08304E" />'
             +paths
             +otherSvg.join('')
             +'<foreignObject height="18" width="45" y="111" x="0" class="node">'
             +'<input type="checkbox" class="logscale" style="height:auto;padding:0;vertical-align:middle"'+(logscale?' checked':'')+'/>'
             +'log</foreignObject>'
             +'</svg>
';

    return svg;
  }

  function regionScoreboardScoreHistoryTable(result) {
    var history = result.scoreHistory;
    // the repeated inline styles are just temporary
    var table = '
 \

 \
      Checkpoint	 \
      Enlightened	 \
      Resistance	 \
      Lead

';
    var lead = 0;
    var rows = '';
    for(var i=history.length-1; i >= 0; i--) {
      lead += history[i][1] - history[i][2];
      var checkpoint_lead = lead < 0?'res: ' + digits(Math.abs(lead)):'enl: ' + digits(lead);
      rows = '
' + history[i][0]
          + '	' + digits(history[i][1])
          + '	' + digits(history[i][2])
          + '	<td class="' + (lead < 0?'res':'enl') + '" style="text-align: left;" >' + checkpoint_lead
          + '
' + rows;
    }
    return table += rows +'
';
  }

  function regionScoreboardSuccess(data,dlg,logscale) {
    if (data.result === undefined) {
      return regionScoreboardFailure(dlg);
    }

    var agentTable = '

#	Agent
';
    for (var i=0; i<data.result.topAgents.length; i++) {
      var agent = data.result.topAgents[i];
      agentTable += '
'+(i+1)+'	'+agent.nick+'
';
    }
    if (data.result.topAgents.length === 0) {
      agentTable += '
no top agents
';
    }
    agentTable += '
';

    var maxAverage = Math.max(data.result.gameScore[0], data.result.gameScore[1], 1);
    var teamRow = [];
    for (var t=0; t<2; t++) {
      var team = t===0 ? 'Enlightened' : 'Resistance';
      var teamClass = t===0 ? 'enl' : 'res';
      var teamCol = t===0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
      var barSize = Math.round(data.result.gameScore[t]/maxAverage*200);
      teamRow[t] = '
'+team+'	'+digits(data.result.gameScore[t])+'

';

    }

    var history = data.result.scoreHistory;
    // the lead is the sum of the difference of each checkpoint
    var lead = history.map(function(cp) { return cp[1] - cp[2] }).reduce(function(acc, diff) { return acc + diff });
    var leadinfo = '
';
    // res lead when we sum to a negative value
    if (lead < 0) {
      leadinfo += 'res lead: ' + digits(Math.abs(lead)) + 'mu
';
    } else {
      leadinfo += 'enl lead: ' + digits(lead) + 'mu
';
    }

    var first = PLAYER.team == 'RESISTANCE' ? 1 : 0;

    // we need some divs to make the accordion work properly
    dlg.html('
'
           +'Region scores for '+data.result.regionName+''
           +'

'+teamRow[first]+teamRow[1-first]+'
'
           +leadinfo // stick our info under the score bars
           +regionScoreboardScoreHistoryChart(data.result, logscale)+'
'
           +'Checkpoint overview'
           +'
'+regionScoreboardScoreHistoryTable(data.result)+'
'
           +'Top agents'
           +'
'+agentTable+'
'
           +'
');

    $('g.checkpoint', dlg).each(function(i, elem) {
      elem = $(elem);

      var tooltip = 'CP:\t'+elem.attr('data-cp')
        + '\nEnl:\t' + digits(elem.attr('data-enl'))
        + '\nRes:\t' + digits(elem.attr('data-res'));
      elem.tooltip({
        content: convertTextToTableMagic(tooltip),
        position: {my: "center bottom", at: "center top-10"}
      });
    });

    $('.cellscore', dlg).accordion({
      header: 'b',
      heightStyle: "fill",
    });

    $('input.logscale', dlg).change(function(){
      var input = $(this);
      regionScoreboardSuccess(data, dlg, input.prop('checked'));
    });
  }
}
// PLUGIN END //////////////////////////////////////////////////////////
setup.info = plugin_info; //add the script info data to the function as a property
if(!window.bootPlugins) window.bootPlugins = [];
window.bootPlugins.push(setup);
// if IITC has already booted, immediately run the 'setup' function
if(window.iitcLoaded && typeof setup === 'function') setup();
} // wrapper end
// inject code into site context
var script = document.createElement('script');
var info = {};
if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description };
script.appendChild(document.createTextNode('('+ wrapper +')('+JSON.stringify(info)+');'));
(document.body || document.head || document.documentElement).appendChild(script);
<commit_msg>version bump to 0.0.2<commit_after>// ==UserScript==
// @id             iitc-plugin-region-score-lead@hansolo669
// @name           IITC plugin: region score lead
// @category       Tweaks
// @version        0.0.2
// @namespace      https://github.com/hansolo669/iitc-tweaks
// @updateURL      http://www.reallyawesomedomain.com/iitc-tweaks/region-score-lead.meta.js
// @downloadURL    http://www.reallyawesomedomain.com/iitc-tweaks/region-score-lead.user.js
// @description    Small modification to the region scores to show the current mu lead.
// @include        https://www.ingress.com/intel*
// @include        http://www.ingress.com/intel*
// @match          https://www.ingress.com/intel*
// @match          http://www.ingress.com/intel*
// @include        https://www.ingress.com/mission/*
// @include        http://www.ingress.com/mission/*
// @match          https://www.ingress.com/mission/*
// @match          http://www.ingress.com/mission/*
// @grant          none
// ==/UserScript==

function wrapper(plugin_info) {
// ensure plugin framework is there, even if iitc is not yet loaded
if(typeof window.plugin !== 'function') window.plugin = function() {};

// PLUGIN START ////////////////////////////////////////////////////////
var setup = function() {
// we stick all this stuff in the setup function to ensure it loads *after* IITC has booted
  window.regionScoreboard = function() {
    // TODO: rather than just load the region scores for the center of the map, display a list of regions in the current view
    // and let the user select one (with automatic selection when just one region, and limited to close enough zooms so list size is reasonable)
    var latLng = map.getCenter();

    var latE6 = Math.round(latLng.lat*1E6);
    var lngE6 = Math.round(latLng.lng*1E6);

    var dlg = dialog({title:'Region scores',html:'Loading regional scores...',width:450,minHeight:320});

    window.postAjax('getRegionScoreDetails', {latE6:latE6,lngE6:lngE6}, function(res){regionScoreboardSuccess(res,dlg);}, function(){regionScoreboardFailure(dlg);});
  }

  function regionScoreboardFailure(dlg) {
    dlg.html('Failed to load region scores - try again');
  }

  function regionScoreboardScoreHistoryChart(result, logscale) {
    // svg area 400x130. graph area 350x100, offset to 40,10

    if(!Math.log10)
      Math.log10 = function(x) { return Math.log(x) / Math.LN10; };

    var max = Math.max(result.gameScore[0],result.gameScore[1],10); //NOTE: ensure a min of 10 for the graph
    var items = []; //we'll copy the items to an array indexed by checkpoint number - easier to access!
    for (var i=0; i<result.scoreHistory.length; i++) {
      max = Math.max(max, result.scoreHistory[i][1], result.scoreHistory[i][2]); //note: index 0 is the checkpoint number here
      items[result.scoreHistory[i][0]] = [result.scoreHistory[i][1], result.scoreHistory[i][2]];
    }

    // scale up maximum a little, so graph isn't squashed right against upper edge
    max *= 1.09;

    // 0 cannot be displayed on a log scale, so we set the minimum to 0.001 and divide by lg(0.001)=-3
    var scale = logscale
      ? function(y) { return  10 - Math.log10(Math.max(0.001,y/max)) / 3 * 100; }
      : function(y) { return 110-y/max*100; };

    var teamPaths = [[],[]];
    var otherSvg = [];

    for (var i=0; i<items.length; i++) {
      var x=i*10+40;
      if (items[i] !== undefined) {
        // paths
        if (i>0 && items[i-1] !== undefined) {
          for (var t=0; t<2; t++) {
            teamPaths[t].push('M'+(x-10)+','+scale(items[i-1][t])+' L'+x+','+scale(items[i][t]));
          }
        }
        // markers
        otherSvg.push('<g title="test" class="checkpoint" data-cp="'+i+'" data-enl="'+items[i][0]+'" data-res="'+items[i][1]+'">');
        otherSvg.push('<rect x="'+(i*10+35)+'" y="10" width="10" height="100" fill="black" fill-opacity="0" />');
        for (var t=0; t<2; t++) {
          var col = t==0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
          otherSvg.push('<circle cx="'+x+'" cy="'+scale(items[i][t])+'" r="3" stroke-width="1" stroke="'+col+'" fill="'+col+'" fill-opacity="0.5" />');
        }
        otherSvg.push('</g>');
      }
    }

    var paths = '<path d="M40,110 L40,10 M40,110 L390,110" stroke="#fff" />';

    // graph tickmarks - horizontal
    var ticks = [];
    for (var i=5; i<=35; i+=5) {
      var x=i*10+40;
      ticks.push('M'+x+',10 L'+x+',110');
      otherSvg.push('<text x="'+x+'" y="125" font-size="12" font-family="Roboto, Helvetica, sans-serif" text-anchor="middle" fill="#fff">'+i+'</text>');
    }

    // vertical
    // first we calculate the power of 10 that is smaller than the max limit
    var vtickStep = Math.pow(10,Math.floor(Math.log10(max)));
    var vticks = [];
    if(logscale) {
      for(var i=0;i<4;i++) {
        vticks.push(vtickStep);
        vtickStep /= 10;
      }
    } else {
      // this could be between 1 and 10 grid lines - so we adjust to give nicer spacings
      if (vtickStep < (max/5)) {
        vtickStep *= 2;
      } else if (vtickStep > (max/2)) {
        vtickStep /= 2;
      }
      for (var i=vtickStep; i<=max; i+=vtickStep) {
        vticks.push(i);
      }
    }
    vticks.forEach(function(i) {
      var y = scale(i);

      ticks.push('M40,'+y+' L390,'+y);

      var istr = i>=1000000000 ? i/1000000000+'B' : i>=1000000 ? i/1000000+'M' : i>=1000 ? i/1000+'k' : i;
      otherSvg.push('<text x="35" y="'+y+'" font-size="12" font-family="Roboto, Helvetica, sans-serif" text-anchor="end" fill="#fff">'+istr+'</text>');
    });

    paths += '<path d="'+ticks.join(' ')+'" stroke="#fff" opacity="0.3" />;'

    for (var t=0; t<2; t++) {
      var col = t==0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
      if (teamPaths[t].length > 0) {
        paths += '<path d="'+teamPaths[t].join(' ')+'" stroke="'+col+'" />';
      }

      var y = scale(result.gameScore[t]);
      paths += '<path d="M40,'+y+' L390,'+y+'" stroke="'+col+'" stroke-dasharray="3,2" opacity="0.8" />';
    }

    var svg = '
<svg width="400" height="130">'
             +'<rect x="0" y="0" width="400" height="130" stroke="#FFCE00" fill="#08304E" />'
             +paths
             +otherSvg.join('')
             +'<foreignObject height="18" width="45" y="111" x="0" class="node">'
             +'<input type="checkbox" class="logscale" style="height:auto;padding:0;vertical-align:middle"'+(logscale?' checked':'')+'/>'
             +'log</foreignObject>'
             +'</svg>
';

    return svg;
  }

  function regionScoreboardScoreHistoryTable(result) {
    var history = result.scoreHistory;
    // the repeated inline styles are just temporary
    var table = '
 \

 \
      Checkpoint	 \
      Enlightened	 \
      Resistance	 \
      Lead

';
    var lead = 0;
    var rows = '';
    for(var i=history.length-1; i >= 0; i--) {
      lead += history[i][1] - history[i][2];
      var checkpoint_lead = lead < 0?'res: ' + digits(Math.abs(lead)):'enl: ' + digits(lead);
      rows = '
' + history[i][0]
          + '	' + digits(history[i][1])
          + '	' + digits(history[i][2])
          + '	<td class="' + (lead < 0?'res':'enl') + '" style="text-align: left;" >' + checkpoint_lead
          + '
' + rows;
    }
    return table += rows +'
';
  }

  function regionScoreboardSuccess(data,dlg,logscale) {
    if (data.result === undefined) {
      return regionScoreboardFailure(dlg);
    }

    var agentTable = '

#	Agent
';
    for (var i=0; i<data.result.topAgents.length; i++) {
      var agent = data.result.topAgents[i];
      agentTable += '
'+(i+1)+'	'+agent.nick+'
';
    }
    if (data.result.topAgents.length === 0) {
      agentTable += '
no top agents
';
    }
    agentTable += '
';

    var maxAverage = Math.max(data.result.gameScore[0], data.result.gameScore[1], 1);
    var teamRow = [];
    for (var t=0; t<2; t++) {
      var team = t===0 ? 'Enlightened' : 'Resistance';
      var teamClass = t===0 ? 'enl' : 'res';
      var teamCol = t===0 ? COLORS[TEAM_ENL] : COLORS[TEAM_RES];
      var barSize = Math.round(data.result.gameScore[t]/maxAverage*200);
      teamRow[t] = '
'+team+'	'+digits(data.result.gameScore[t])+'

';

    }

    var history = data.result.scoreHistory;
    // the lead is the sum of the difference of each checkpoint
    var lead = history.map(function(cp) { return cp[1] - cp[2] }).reduce(function(acc, diff) { return acc + diff });
    var leadinfo = '
';
    // res lead when we sum to a negative value
    if (lead < 0) {
      leadinfo += 'res lead: ' + digits(Math.abs(lead)) + 'mu
';
    } else {
      leadinfo += 'enl lead: ' + digits(lead) + 'mu
';
    }

    var first = PLAYER.team == 'RESISTANCE' ? 1 : 0;

    // we need some divs to make the accordion work properly
    dlg.html('
'
           +'Region scores for '+data.result.regionName+''
           +'

'+teamRow[first]+teamRow[1-first]+'
'
           +leadinfo // stick our info under the score bars
           +regionScoreboardScoreHistoryChart(data.result, logscale)+'
'
           +'Checkpoint overview'
           +'
'+regionScoreboardScoreHistoryTable(data.result)+'
'
           +'Top agents'
           +'
'+agentTable+'
'
           +'
');

    $('g.checkpoint', dlg).each(function(i, elem) {
      elem = $(elem);

      var tooltip = 'CP:\t'+elem.attr('data-cp')
        + '\nEnl:\t' + digits(elem.attr('data-enl'))
        + '\nRes:\t' + digits(elem.attr('data-res'));
      elem.tooltip({
        content: convertTextToTableMagic(tooltip),
        position: {my: "center bottom", at: "center top-10"}
      });
    });

    $('.cellscore', dlg).accordion({
      header: 'b',
      heightStyle: "fill",
    });

    $('input.logscale', dlg).change(function(){
      var input = $(this);
      regionScoreboardSuccess(data, dlg, input.prop('checked'));
    });
  }
}
// PLUGIN END //////////////////////////////////////////////////////////
setup.info = plugin_info; //add the script info data to the function as a property
if(!window.bootPlugins) window.bootPlugins = [];
window.bootPlugins.push(setup);
// if IITC has already booted, immediately run the 'setup' function
if(window.iitcLoaded && typeof setup === 'function') setup();
} // wrapper end
// inject code into site context
var script = document.createElement('script');
var info = {};
if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description };
script.appendChild(document.createTextNode('('+ wrapper +')('+JSON.stringify(info)+');'));
(document.body || document.head || document.documentElement).appendChild(script);
<|endoftext|>


<commit_before>import React from 'react';
import { props, t, skinnable } from '../utils';
import { warn } from '../utils/log';
import cx from 'classnames';
import FlexView from '../flex/FlexView';
import find from 'lodash/collection/find';
import every from 'lodash/collection/every';
import isEqual from 'lodash/lang/isEqual';
import sortBy from 'lodash/collection/sortBy';

const Range = t.refinement(t.struct({
  startValue: t.Number,
  endValue: t.Number,
  fillingColor: t.maybe(t.String),
  labelColor: t.maybe(t.String)
}), r => r.startValue < r.endValue, 'Range');

const Ranges = t.refinement(t.list(Range), (rangeList) => {

  const isOverlapped = (range1, range2) => (
    Math.max(range1.startValue, range2.startValue) < Math.min(range1.endValue, range2.endValue)
  );

  const noOverlappingRanges = ranges => {
    return every(ranges, (range1, i) => {
      return every(ranges.slice(0, i).concat(ranges.slice(i + 1)), range2 => {
        return !isOverlapped(range1, range2);
      });
    });
  };

  return noOverlappingRanges(rangeList);
}, 'Ranges');

const isFullyFilled = (ranges, min, max) => {
  const comparableRanges = ranges.concat({ startValue: max, endValue: min });
  const sortedStartValueList = sortBy(comparableRanges, 'startValue').map(range => range.startValue);
  const sortedEndValueList = sortBy(comparableRanges, 'endValue').map(range => range.endValue);
  return isEqual(sortedStartValueList, sortedEndValueList);
};

const computePercentage = (value, min, max) => (
  Math.abs((value - min) * 100 / (max - min))
);

const labelFormatter = (value, min, max) => (
  `${computePercentage(value, min, max)}%`
);

/**
 * ### Renders a Meter
 */
@skinnable()
@props({
  /**
   * This is the value provided as input.
   */
  value: t.Number,
  /**
   * Minimum value.
   */
  min: t.maybe(t.Number),
  /**
   * Maximum value.
   */
  max: t.maybe(t.Number),
  /**
   * Function as input in which you can define a custom label format.
   */
  labelFormatter: t.maybe(t.Function),
  /**
   * Array of Object in which you can define startValue, endValue, labelColor, fillingColor.
   */
  ranges: t.maybe(Ranges),
  /**
   * String as color for label.
   */
  baseLabelColor: t.maybe(t.String),
  /**
   * String as color for bar.
   */
  baseFillingColor: t.maybe(t.String),
  id: t.maybe(t.String),
  className: t.maybe(t.String),
  style: t.maybe(t.Object)
})
export default class Meter extends React.Component {

  static defaultProps = {
    min: 0,
    max: 100,
    labelFormatter
  };

  componentDidMount() {
    this.logWarnings();
  }

  logWarnings = () => {
    const {
      ranges,
      max,
      min,
      baseFillingColor
    } = this.props;
    warn(() => {
      if (isFullyFilled(ranges, min, max) && baseFillingColor){
        return 'baseFillingColor not needed, ranges are fully filled';
      }
    });
    warn(() => {
      if (!(isFullyFilled(ranges, min, max) || baseFillingColor)){
        return 'ranges accept holes, need a baseFillingColor';
      }
    });
  };

  computeStyles = () => {
    const {
      value,
      min,
      max,
      ranges,
      baseFillingColor,
      baseLabelColor
    } = this.props;

    const range = find(ranges, ({ startValue, endValue })  => (
      value >= startValue && value <= endValue
    ));
    return {
      basisSize: `${computePercentage(value, min, max)}%`,
      fillingStyle: {
        backgroundColor: range ? range.fillingColor : baseFillingColor
      },
      labelStyle: {
        color: range ? range.labelColor : baseLabelColor
      }
    };
  }

  getLocals() {
    const {
      className,
      ...props
    } = this.props;

    const styles = this.computeStyles();

    return {
      ...props,
      className: cx('meter', className),
      fillingStyle: styles.fillingStyle,
      labelStyle: styles.labelStyle,
      basisSize: styles.basisSize
    };
  }

  template({ id, className, style, fillingStyle, labelStyle, basisSize, ...locals }) {
    return (
      <FlexView {...{ id, className, style }} grow>
        <FlexView className='bar' grow>
          <FlexView
            className='filling'
            basis={basisSize}
            style={fillingStyle}
          />
        </FlexView>
        <FlexView
          className='label'
          shrink={false}
          style={labelStyle}
        >
          {locals.labelFormatter(locals.value, locals.min, locals.max)}
        </FlexView>
      </FlexView>
    );
  }

}
<commit_msg>move logic outside of the template<commit_after>import React from 'react';
import { props, t, skinnable } from '../utils';
import { warn } from '../utils/log';
import cx from 'classnames';
import FlexView from '../flex/FlexView';
import find from 'lodash/collection/find';
import every from 'lodash/collection/every';
import isEqual from 'lodash/lang/isEqual';
import sortBy from 'lodash/collection/sortBy';

const Range = t.refinement(t.struct({
  startValue: t.Number,
  endValue: t.Number,
  fillingColor: t.maybe(t.String),
  labelColor: t.maybe(t.String)
}), r => r.startValue < r.endValue, 'Range');

const Ranges = t.refinement(t.list(Range), (rangeList) => {

  const isOverlapped = (range1, range2) => (
    Math.max(range1.startValue, range2.startValue) < Math.min(range1.endValue, range2.endValue)
  );

  const noOverlappingRanges = ranges => {
    return every(ranges, (range1, i) => {
      return every(ranges.slice(0, i).concat(ranges.slice(i + 1)), range2 => {
        return !isOverlapped(range1, range2);
      });
    });
  };

  return noOverlappingRanges(rangeList);
}, 'Ranges');

const isFullyFilled = (ranges, min, max) => {
  const comparableRanges = ranges.concat({ startValue: max, endValue: min });
  const sortedStartValueList = sortBy(comparableRanges, 'startValue').map(range => range.startValue);
  const sortedEndValueList = sortBy(comparableRanges, 'endValue').map(range => range.endValue);
  return isEqual(sortedStartValueList, sortedEndValueList);
};

const computePercentage = (value, min, max) => (
  Math.abs((value - min) * 100 / (max - min))
);

const labelFormatter = (value, min, max) => (
  `${computePercentage(value, min, max)}%`
);

/**
 * ### Renders a Meter
 */
@skinnable()
@props({
  /**
   * This is the value provided as input.
   */
  value: t.Number,
  /**
   * Minimum value.
   */
  min: t.maybe(t.Number),
  /**
   * Maximum value.
   */
  max: t.maybe(t.Number),
  /**
   * Function as input in which you can define a custom label format.
   */
  labelFormatter: t.maybe(t.Function),
  /**
   * Array of Object in which you can define startValue, endValue, labelColor, fillingColor.
   */
  ranges: t.maybe(Ranges),
  /**
   * String as color for label.
   */
  baseLabelColor: t.maybe(t.String),
  /**
   * String as color for bar.
   */
  baseFillingColor: t.maybe(t.String),
  id: t.maybe(t.String),
  className: t.maybe(t.String),
  style: t.maybe(t.Object)
})
export default class Meter extends React.Component {

  static defaultProps = {
    min: 0,
    max: 100,
    labelFormatter
  };

  componentDidMount() {
    this.logWarnings();
  }

  logWarnings = () => {
    const {
      ranges,
      max,
      min,
      baseFillingColor
    } = this.props;
    warn(() => {
      if (isFullyFilled(ranges, min, max) && baseFillingColor){
        return 'baseFillingColor not needed, ranges are fully filled';
      }
    });
    warn(() => {
      if (!(isFullyFilled(ranges, min, max) || baseFillingColor)){
        return 'ranges accept holes, need a baseFillingColor';
      }
    });
  };

  computeStyles = () => {
    const {
      value,
      min,
      max,
      ranges,
      baseFillingColor,
      baseLabelColor
    } = this.props;

    const range = find(ranges, ({ startValue, endValue })  => (
      value >= startValue && value <= endValue
    ));
    return {
      basisSize: `${computePercentage(value, min, max)}%`,
      fillingStyle: {
        backgroundColor: range ? range.fillingColor : baseFillingColor
      },
      labelStyle: {
        color: range ? range.labelColor : baseLabelColor
      }
    };
  }

  getLocals() {
    const {
      className,
      labelFormatter,
      ...props
    } = this.props;

    const styles = this.computeStyles();

    return {
      ...props,
      className: cx('meter', className),
      fillingStyle: styles.fillingStyle,
      labelStyle: styles.labelStyle,
      basisSize: styles.basisSize,
      formattedLabel: labelFormatter(props.value, props.min, props.max)
    };
  }

  template({ id, className, style, fillingStyle, labelStyle, basisSize, formattedLabel }) {
    return (
      <FlexView {...{ id, className, style }} grow>
        <FlexView className='bar' grow>
          <FlexView
            className='filling'
            basis={basisSize}
            style={fillingStyle}
          />
        </FlexView>
        <FlexView
          className='label'
          shrink={false}
          style={labelStyle}
        >
          {formattedLabel}
        </FlexView>
      </FlexView>
    );
  }

}
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */
import * as mongo from 'mongodb';
import parse from 'misskey-text';
import Post from '../../models/post';
import User from '../../models/user';
import Following from '../../models/following';
import DriveFile from '../../models/drive-file';
import serialize from '../../serializers/post';
//import extractHashtags from '../../core/extract-hashtags';
//import registerHashtags from '../../core/register-hashtags';
import createFile from '../../common/add-file-to-drive';
import notify from '../../common/notify';
import event from '../../event';
import es from '../../../db/elasticsearch';

/**
 * 最大文字数
 */
const maxTextLength = 300;

/**
 * 添付できるファイルの数
 */
const maxFileLength = 4;

/**
 * Create a post
 *
 * @param {Object} params
 * @param {Object} user
 * @param {Object} app
 * @return {Promise<object>}
 */
module.exports = (params, user, app) =>
	new Promise(async (res, rej) =>
{
	// Get 'text' parameter
	let text = params.text;
	if (text !== undefined && text !== null) {
		text = text.trim();
		if (text.length === 0) {
			text = null;
		} else if (text.length > maxTextLength) {
			return rej('too long text');
		}
	} else {
		text = null;
	}

	// Get 'images' parameter
	let images = params.images;
	let files = [];
	if (images !== undefined && images !== null) {
		images = images.split(',');

		if (images.length > maxFileLength) {
			return rej('too many images');
		}

		// Drop duplicates
		images = images.filter((x, i, self) => self.indexOf(x) === i);

		// Fetch files
		// forEach だと途中でエラーなどがあっても return できないので
		// 敢えて for を使っています。
		for (let i = 0; i < images.length; i++) {
			const image = images[i];

			const entity = await DriveFile.findOne({
				_id: new mongo.ObjectID(image),
				user: user._id
			}, {
				data: false
			});

			if (entity === null) {
				return rej('file not found');
			} else {
				files.push(entity);
			}
		}
	} else {
		files = null;
	}

	// Get 'repost' parameter
	let repost = params.repost;
	if (repost !== undefined && repost !== null) {
		// Get repostee
		repost = await Post.findOne({
			_id: new mongo.ObjectID(repost)
		});

		if (repost === null) {
			return rej('repostee is not found');
		} else if (repost.repost && !repost.text && !repost.images) {
			return rej('cannot repost from repost');
		}

		// Get recently post
		const latestPost = await Post.findOne({
			user: user._id
		}, {}, {
			sort: {
				_id: -1
			}
		});

		// 直近と同じRepost対象かつ引用じゃなかったらエラー
		if (latestPost &&
				latestPost.repost &&
				latestPost.repost.toString() === repost._id.toString() &&
				text === null && files === null) {
			return rej('二重Repostです(NEED TRANSLATE)');
		}

		// 直近がRepost対象かつ引用じゃなかったらエラー
		if (latestPost &&
				latestPost._id.toString() === repost._id.toString() &&
				text === null && files === null) {
			return rej('二重Repostです(NEED TRANSLATE)');
		}
	} else {
		repost = null;
	}

	// Get 'reply_to' parameter
	let replyTo = params.reply_to;
	if (replyTo !== undefined && replyTo !== null) {
		replyTo = await Post.findOne({
			_id: new mongo.ObjectID(replyTo)
		});

		if (replyTo === null) {
			return rej('reply to post is not found');
		}

		// 返信対象が引用でないRepostだったらエラー
		if (replyTo.repost && !replyTo.text && !replyTo.images) {
			return rej('cannot reply to repost');
		}
	} else {
		replyTo = null;
	}

	// テキストが無いかつ添付ファイルが無いかつRepostも無かったらエラー
	if (text === null && files === null && repost === null) {
		return rej('text, images or repost is required');
	}

	// 投稿を作成
	const inserted = await Post.insert({
		created_at: new Date(),
		images: images ? files.map(file => file._id) : undefined,
		reply_to: replyTo ? replyTo._id : undefined,
		repost: repost ? repost._id : undefined,
		text: text,
		user: user._id,
		app: app ? app._id : null
	});

	const post = inserted.ops[0];

	// Serialize
	const postObj = await serialize(post);

	// Reponse
	res(postObj);

	// 自分のストリーム
	event(user._id, 'post', postObj);

	const followers = await Following
		.find({ followee: user._id }, {
			follower: true,
			_id: false
		})
		.toArray();

	// 自分のフォロワーのストリーム
	followers.forEach(following => {
		event(following.follower, 'post', postObj);
	});

	// Increment my posts count
	User.updateOne({ _id: user._id }, {
		$inc: {
			posts_count: 1
		}
	});

	// Update replyee status
	if (replyTo) {
		if (replyTo.user.toString() !== user._id.toString()) {
			// Publish event
			event(replyTo.user, 'reply', postObj);

			// Create notification
			notify(replyTo.user, 'reply', {
				post: post._id
			});
		}

		Post.updateOne({ _id: replyTo._id }, {
			$inc: {
				replies_count: 1
			}
		});
	}

	if (repost) {
		// Publish event
		event(repost.user, 'repost', postObj);

		// Notify
		if (repost.user.toString() !== user._id.toString()) {
			notify(repost.user, 'repost', {
				post: post._id
			});
		}

		// 今までで同じ投稿をRepostしているか
		const existRepost = await Post.findOne({
			user: user._id,
			repost: repost._id,
			_id: {
				$ne: post._id
			}
		});

		if (existRepost === null) {
			// Update repostee status
			Post.updateOne({ _id: repost._id }, {
				$inc: {
					repost_count: 1
				}
			});
		}
	}

	// Register to search database
	if (text != null) {
		es.index({
			index: 'misskey',
			type: 'post',
			id: post._id.toString(),
			body: {
				text: post.text
			}
		});

		const tokens = parse(text);

		// Extract a mentions
		const mentions = tokens
			.filter(t => t.type == 'mention')
			.map(m => m.username)
			// Drop dupulicates
			.filter((v, i, s) => s.indexOf(v) == i);

		mentions.forEach(async (mention) => {
			// Fetch mentioned user
			const mentionedUser = await User
				.findOne({ username_lower: mention.toLowerCase() });

			// Notify
			if (mentionedUser._id.toString() !== user._id.toString()) {
				if (replyTo && replyTo.user.toString() == mentionedUser._id.toString()) return;
				if (repost && repost.user.toString() == mentionedUser._id.toString()) return;

				// Publish event
				event(mentionedUser._id, 'mention', postObj);

				// Create notification
				notify(mentionedUser._id, 'mention', {
					post: post._id
				});
			}
		});

		// ハッシュタグ抽出
		//const hashtags = extractHashtags(text);

		// ハッシュタグをデータベースに登録
		//registerHashtags(user, hashtags);
	}
});
<commit_msg>WIP #38<commit_after>'use strict';

/**
 * Module dependencies
 */
import * as mongo from 'mongodb';
import parse from 'misskey-text';
import Post from '../../models/post';
import User from '../../models/user';
import Following from '../../models/following';
import DriveFile from '../../models/drive-file';
import serialize from '../../serializers/post';
//import extractHashtags from '../../core/extract-hashtags';
//import registerHashtags from '../../core/register-hashtags';
import createFile from '../../common/add-file-to-drive';
import notify from '../../common/notify';
import event from '../../event';
import es from '../../../db/elasticsearch';

/**
 * 最大文字数
 */
const maxTextLength = 300;

/**
 * 添付できるファイルの数
 */
const maxFileLength = 4;

/**
 * Create a post
 *
 * @param {Object} params
 * @param {Object} user
 * @param {Object} app
 * @return {Promise<object>}
 */
module.exports = (params, user, app) =>
	new Promise(async (res, rej) =>
{
	// Get 'text' parameter
	let text = params.text;
	if (text !== undefined && text !== null) {
		text = text.trim();
		if (text.length === 0) {
			text = null;
		} else if (text.length > maxTextLength) {
			return rej('too long text');
		}
	} else {
		text = null;
	}

	// Get 'images' parameter
	let images = params.images;
	let files = [];
	if (images !== undefined && images !== null) {
		images = images.split(',');

		if (images.length > maxFileLength) {
			return rej('too many images');
		}

		// Drop duplicates
		images = images.filter((x, i, self) => self.indexOf(x) === i);

		// Fetch files
		// forEach だと途中でエラーなどがあっても return できないので
		// 敢えて for を使っています。
		for (let i = 0; i < images.length; i++) {
			const image = images[i];

			const entity = await DriveFile.findOne({
				_id: new mongo.ObjectID(image),
				user: user._id
			}, {
				data: false
			});

			if (entity === null) {
				return rej('file not found');
			} else {
				files.push(entity);
			}
		}
	} else {
		files = null;
	}

	// Get 'repost' parameter
	let repost = params.repost;
	if (repost !== undefined && repost !== null) {
		// Get repostee
		repost = await Post.findOne({
			_id: new mongo.ObjectID(repost)
		});

		if (repost === null) {
			return rej('repostee is not found');
		} else if (repost.repost && !repost.text && !repost.images) {
			return rej('cannot repost from repost');
		}

		// Get recently post
		const latestPost = await Post.findOne({
			user: user._id
		}, {}, {
			sort: {
				_id: -1
			}
		});

		// 直近と同じRepost対象かつ引用じゃなかったらエラー
		if (latestPost &&
				latestPost.repost &&
				latestPost.repost.toString() === repost._id.toString() &&
				text === null && files === null) {
			return rej('二重Repostです(NEED TRANSLATE)');
		}

		// 直近がRepost対象かつ引用じゃなかったらエラー
		if (latestPost &&
				latestPost._id.toString() === repost._id.toString() &&
				text === null && files === null) {
			return rej('二重Repostです(NEED TRANSLATE)');
		}
	} else {
		repost = null;
	}

	// Get 'reply_to' parameter
	let replyTo = params.reply_to;
	if (replyTo !== undefined && replyTo !== null) {
		replyTo = await Post.findOne({
			_id: new mongo.ObjectID(replyTo)
		});

		if (replyTo === null) {
			return rej('reply to post is not found');
		}

		// 返信対象が引用でないRepostだったらエラー
		if (replyTo.repost && !replyTo.text && !replyTo.images) {
			return rej('cannot reply to repost');
		}
	} else {
		replyTo = null;
	}

	// テキストが無いかつ添付ファイルが無いかつRepostも無かったらエラー
	if (text === null && files === null && repost === null) {
		return rej('text, images or repost is required');
	}

	// 投稿を作成
	const inserted = await Post.insert({
		created_at: new Date(),
		images: images ? files.map(file => file._id) : undefined,
		reply_to: replyTo ? replyTo._id : undefined,
		repost: repost ? repost._id : undefined,
		text: text,
		user: user._id,
		app: app ? app._id : null
	});

	const post = inserted.ops[0];

	// Serialize
	const postObj = await serialize(post);

	// Reponse
	res(postObj);

	// 自分のストリーム
	event(user._id, 'post', postObj);

	const followers = await Following
		.find({ followee: user._id }, {
			follower: true,
			_id: false
		})
		.toArray();

	// 自分のフォロワーのストリーム
	followers.forEach(following => {
		event(following.follower, 'post', postObj);
	});

	// Increment my posts count
	User.updateOne({ _id: user._id }, {
		$inc: {
			posts_count: 1
		}
	});

	// Update replyee status
	if (replyTo) {
		if (replyTo.user.toString() !== user._id.toString()) {
			// Publish event
			event(replyTo.user, 'reply', postObj);

			// Create notification
			notify(replyTo.user, 'reply', {
				post: post._id
			});
		}

		// Create mention
		Mention.insert({
			post: post._id,
			_post_user_id: user._id, // 非正規データ
			user: replyTo.user
		});

		Post.updateOne({ _id: replyTo._id }, {
			$inc: {
				replies_count: 1
			}
		});
	}

	if (repost) {
		// Publish event
		event(repost.user, 'repost', postObj);

		// Notify
		if (repost.user.toString() !== user._id.toString()) {
			notify(repost.user, 'repost', {
				post: post._id
			});
		}

		// Create mention
		Mention.insert({
			post: post._id,
			_post_user_id: user._id, // 非正規データ
			user: repost.user
		});

		// 今までで同じ投稿をRepostしているか
		const existRepost = await Post.findOne({
			user: user._id,
			repost: repost._id,
			_id: {
				$ne: post._id
			}
		});

		if (existRepost === null) {
			// Update repostee status
			Post.updateOne({ _id: repost._id }, {
				$inc: {
					repost_count: 1
				}
			});
		}
	}

	// Register to search database
	if (text != null) {
		es.index({
			index: 'misskey',
			type: 'post',
			id: post._id.toString(),
			body: {
				text: post.text
			}
		});

		const tokens = parse(text);

		// Extract a mentions
		const mentions = tokens
			.filter(t => t.type == 'mention')
			.map(m => m.username)
			// Drop dupulicates
			.filter((v, i, s) => s.indexOf(v) == i);

		mentions.forEach(async (mention) => {
			// Fetch mentioned user
			const mentionedUser = await User
				.findOne({ username_lower: mention.toLowerCase() });

			// Notify
			if (mentionedUser._id.toString() !== user._id.toString()) {
				if (replyTo && replyTo.user.toString() == mentionedUser._id.toString()) return;
				if (repost && repost.user.toString() == mentionedUser._id.toString()) return;

				// Publish event
				event(mentionedUser._id, 'mention', postObj);

				// Create mention
				Mention.insert({
					post: post._id,
					_post_user_id: user._id, // 非正規データ
					user: mentionedUser._id
				});

				// Create notification
				notify(mentionedUser._id, 'mention', {
					post: post._id
				});
			}
		});

		// ハッシュタグ抽出
		//const hashtags = extractHashtags(text);

		// ハッシュタグをデータベースに登録
		//registerHashtags(user, hashtags);
	}
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {connect} from 'react-redux';

import { map, uniq, find, differenceWith } from 'lodash';

import { Icon } from '../index';
import { clearSelection, highlightNodes, nodeUpdate, nodesSelect, deleteNodes, deselectNodes} from '../../modules/graph/index';
import { tableColumnAdd, tableColumnRemove } from '../../modules/data/index';
import { fieldLocator } from '../../helpers/index';

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

        this.state = {
            editNode: null,
            value: ""
        };
    }

    handleClearSelection() {
        const { dispatch } = this.props;
        dispatch(clearSelection());
    }

    handleCancelEditNode(node) {
        const { dispatch } = this.props;
        this.setState({editNode: null});
    }

    handleUpdateEditNode(node) {
        const { editNode, value } = this.state;
        const { dispatch } = this.props;

        dispatch(nodeUpdate(editNode.id, {name: value}));

        this.setState({editNode: null});
    }

    handleEditNode(node) {
        this.setState({editNode: node, value: node.name});
    }

    handleDeselectNode(node) {
        const { dispatch } = this.props;
        dispatch(deselectNodes([node]));
    }

    handleDeleteNode(node) {
        const { dispatch } = this.props;
        dispatch(deleteNodes([node]));
    }

    handleDeleteAllButSelectedNodes() {
        const { dispatch, node, nodes } = this.props;

        const delete_nodes = differenceWith(nodes, node, (n1, n2) => {
            return n1.id == n2.id;
        });

        dispatch(deleteNodes(delete_nodes));
    }

    handleSelectAllNodes() {
        const { dispatch, node, nodes, links } = this.props;

        dispatch(nodesSelect(nodes));
    }

    handleSelectRelatedNodes() {
        const { dispatch, node, nodes, links } = this.props;

        const related_nodes = [];

        let x = (n) => {
            related_nodes.push(n);

            for (let link of links) {
                if (link.source === n.id) {
                    // check if already visited
                    if (find(related_nodes, (o) => {
                        return (link.target === o.id);
                    })) {
                        continue;
                    }

                    const target_node = find(nodes, (n2) => {
                        return (link.target == n2.id);
                    });

                    x(target_node);
                }

                if (link.target === n.id) {
                    if (find(related_nodes, (o) => {
                        return (link.source === o.id);
                    })) {
                        continue;
                    }

                    const source_node = find(nodes, (n2) => {
                        return (link.source == n2.id);
                    });

                    x(source_node);
                }
            }
        };

        for (let n of node) {
            x(n);
        }

        dispatch(nodesSelect(related_nodes));
    }

    handleNodeLabelChange(event) {
        const { editNode, value } = this.state;
        this.setState({value: event.target.value});
    }

    handleDeleteAllNodes() {
        const { dispatch, node } = this.props;
        dispatch(deleteNodes(node));
    }

    renderSelected() {
        const { node } = this.props;
        const { editNode, value } = this.state;

        return (
            node ?
                map(node, (i_node) => {
                    if (editNode == i_node) {
                        return (

                                <form onSubmit={this.handleUpdateEditNode.bind(this)}>
                                <Icon className="glyphicon" name={ i_node.icon }></Icon>
                                <input type="text" value={value} onChange={ this.handleNodeLabelChange.bind(this) } />
                                <button type="submit" >update</button>
                                <button onClick={(n) => this.handleCancelEditNode(n) }>cancel</button>
                                </form>

                        );
                    } else {
                        return (

                                {i_node.name}
                                <Icon style={{'marginRight': '60px'}}  className="glyphicon" name={ i_node.icon }></Icon>
                                <Icon style={{'marginRight': '40px'}} onClick={(n) => this.handleEditNode(i_node)} name="ion-ios-remove-circle-outline"/>
                                <Icon style={{'marginRight': '20px'}} onClick={(n) => this.handleDeselectNode(i_node)} name="ion-ios-remove-circle-outline"/>
                                <Icon onClick={(n) => this.handleDeleteNode(i_node)} name="ion-ios-close-circle-outline"/>

                        );
                    }
                })
                : null
        );
    }

    render() {
        return (

                 this.handleClearSelection()}>
                    <Icon name="ion-ios-hand-outline"/> Clear selection

                 this.handleDeleteAllNodes()}>
                    <Icon name="ion-ios-hand-outline"/> Delete selected nodes

                 this.handleDeleteAllButSelectedNodes()}>
                    <Icon name="ion-ios-hand-outline"/> Delete but selected nodes

                 this.handleSelectRelatedNodes()}>
                    <Icon name="ion-ios-hand-outline"/> Select related nodes

                 this.handleSelectAllNodes()}>
                    <Icon name="ion-ios-hand-outline"/> Select all nodes

                    {this.renderSelected()}

        );
    }
}

function select(state) {
    return {
        node: state.entries.node,
        highlight_nodes: state.entries.highlight_nodes,
        nodes: state.entries.nodes,
        links: state.entries.links
    };
}

export default connect(select)(Nodes);
<commit_msg>using a modal for update node now<commit_after>import React, { Component } from 'react';
import {connect} from 'react-redux';

import { map, uniq, find, differenceWith } from 'lodash';

import { Icon } from '../index';
import { clearSelection, highlightNodes, nodeUpdate, nodesSelect, deleteNodes, deselectNodes} from '../../modules/graph/index';
import { tableColumnAdd, tableColumnRemove } from '../../modules/data/index';
import { fieldLocator } from '../../helpers/index';

import SkyLight from 'react-skylight';

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

        this.state = {
            editNode: null,
            value: ""
        };
    }

    handleClearSelection() {
        const { dispatch } = this.props;
        dispatch(clearSelection());
    }

    handleCancelEditNode(node) {
        const { dispatch } = this.props;
        this.setState({editNode: null});
    }

    handleUpdateEditNode(node) {
        const { editNode, value } = this.state;
        const { dispatch } = this.props;

        dispatch(nodeUpdate(editNode.id, {name: value}));

        this.setState({editNode: null});
    }

    handleEditNode(node) {
        this.setState({editNode: node, value: node.name});
        this.refs.customDialog.show();
    }

    handleDeselectNode(node) {
        const { dispatch } = this.props;
        dispatch(deselectNodes([node]));
    }

    handleDeleteNode(node) {
        const { dispatch } = this.props;
        dispatch(deleteNodes([node]));
    }

    handleDeleteAllButSelectedNodes() {
        const { dispatch, node, nodes } = this.props;

        const delete_nodes = differenceWith(nodes, node, (n1, n2) => {
            return n1.id == n2.id;
        });

        dispatch(deleteNodes(delete_nodes));
    }

    handleSelectAllNodes() {
        const { dispatch, node, nodes, links } = this.props;

        dispatch(nodesSelect(nodes));
    }

    handleSelectRelatedNodes() {
        const { dispatch, node, nodes, links } = this.props;

        const related_nodes = [];

        let x = (n) => {
            related_nodes.push(n);

            for (let link of links) {
                if (link.source === n.id) {
                    // check if already visited
                    if (find(related_nodes, (o) => {
                        return (link.target === o.id);
                    })) {
                        continue;
                    }

                    const target_node = find(nodes, (n2) => {
                        return (link.target == n2.id);
                    });

                    x(target_node);
                }

                if (link.target === n.id) {
                    if (find(related_nodes, (o) => {
                        return (link.source === o.id);
                    })) {
                        continue;
                    }

                    const source_node = find(nodes, (n2) => {
                        return (link.source == n2.id);
                    });

                    x(source_node);
                }
            }
        };

        for (let n of node) {
            x(n);
        }

        dispatch(nodesSelect(related_nodes));
    }

    handleNodeLabelChange(event) {
        const { editNode, value } = this.state;
        this.setState({value: event.target.value});
    }

    handleDeleteAllNodes() {
        const { dispatch, node } = this.props;
        dispatch(deleteNodes(node));
    }

    renderSelected() {
        const { node } = this.props;
        const { editNode, value } = this.state;

        return (
            node ?
                map(node, (i_node) => {
                        return (

                                {i_node.name}
                                <Icon style={{'marginRight': '60px'}}  className="glyphicon" name={ i_node.icon }></Icon>
                                <Icon style={{'marginRight': '40px'}} onClick={(n) => this.handleEditNode(i_node)} name="ion-ios-remove-circle-outline"/>
                                <Icon style={{'marginRight': '20px'}} onClick={(n) => this.handleDeselectNode(i_node)} name="ion-ios-remove-circle-outline"/>
                                <Icon onClick={(n) => this.handleDeleteNode(i_node)} name="ion-ios-close-circle-outline"/>

                        );
                })
                : null
        );
    }

    render() {
        const { editNode, value } = this.state;

        const updateNodeDialogStyles = {
            backgroundColor: '#fff',
            color: '#000',
            width: '400px',
            height: '400px',
	    marginTop: '-200px',
	    marginLeft: '-200px',
        };

	let edit_node = null;
	if (editNode) {
	    edit_node = <form>
		<Icon className="glyphicon" name={ editNode.icon }></Icon>

		    Name
		    <input type="text" className="form-control" value={value} onChange={ this.handleNodeLabelChange.bind(this) } placeholder='name' />

	    </form>;
	}

	return (

		 this.handleClearSelection()}>
		    <Icon name="ion-ios-hand-outline"/> Clear selection

		 this.handleDeleteAllNodes()}>
		    <Icon name="ion-ios-hand-outline"/> Delete selected nodes

		 this.handleDeleteAllButSelectedNodes()}>
		    <Icon name="ion-ios-hand-outline"/> Delete but selected nodes

		 this.handleSelectRelatedNodes()}>
		    <Icon name="ion-ios-hand-outline"/> Select related nodes

		 this.handleSelectAllNodes()}>
		    <Icon name="ion-ios-hand-outline"/> Select all nodes

		    {this.renderSelected()}

                <SkyLight dialogStyles={updateNodeDialogStyles} hideOnOverlayClicked ref="customDialog" title="Update node" afterClose={ this.handleUpdateEditNode.bind(this) }>
                    { edit_node }
                </SkyLight>

        );
    }
}

function select(state) {
    return {
        node: state.entries.node,
        highlight_nodes: state.entries.highlight_nodes,
        nodes: state.entries.nodes,
        links: state.entries.links
    };
}

export default connect(select)(Nodes);
<|endoftext|>