<commit_before>module.exports = function(server, sessionParser, store) {
	var	_ = require('lodash'),
		cookie = require('cookie'),
		cookieParser = require('cookie-parser'),
		WebSocket = require('ws'),
		Player = require('./models/PlayerModel').model;

	const logger = require('loggy');

	let wss = new WebSocket.Server({
		verifyClient: function(info, done) {
			logger.log('verifyClient() -> ', info.req.session, info.req.headers);

			if (info.req.headers.cookie || info.req.session) {
				done(info.req);
			}
		},
		server
	}),
	CLIENTS = [];

	wss.broadcast = function broadcast(data, sid, all = true) {
		logger.info('broadcast() -> ', data.id);
		wss.clients.forEach(function each(client) {
			if (client.readyState === WebSocket.OPEN) {
				if (all || client !== CLIENTS[sid]) {
					logger.info('send()..');
					client.send(JSON.stringify(data));
				}
			}
		});
	};

	wss.on('connection', function connection(ws, req) {
		let parseCookie = cookie.parse(req.headers.cookie)['connect.sid'],
			sid = cookieParser.signedCookie(parseCookie, '$eCuRiTy');

		logger.info('Connection accepted:', req.headers, req.session);
		logger.info(`Clients Connected: ${wss.clients.size}`);

		logger.info('sid -> ', sid);

		// Save sessionID against the array of
		// clients so we can reference later
		CLIENTS[sid] = ws;

		ws.send(JSON.stringify({ action: 'connected', type: 'global' }));

		// This is the most important callback for us, we'll handle
		// all messages from users here.
		ws.on('message', function(message) {
			var data = JSON.parse(message),
				query = {
					sessionId: sid
				};

			// Process WebSocket message
			logger.info('Message received: ', data);

			switch (data.action) {
				case 'whoami':
					logger.info('websocket:onmessage:whoami -> ', query, sid);

					Player.find(query)
						.select('+sessionId')
						.exec()
						.then(function(list) {
							let nuts = { action: 'whoami', type: 'players', nuts: list };

							logger.info(nuts);
							ws.send(JSON.stringify(nuts));
						})
						.catch(function(err) {
							logger.error(err);
						});
					break;

				default:
					wss.broadcast(data);
					break;
			}
		});

		ws.on('error', function(err) {
			logger.error(err);
		});

		ws.on('close', function(connection) {
			// close user connection
			logger.info('Connection Closed:', connection, wss.clients);
		});
	});

	global.wss = wss;

	return wss;
};
<commit_msg>Update mongoose query to return 'cardsInHand' for 'whoami'<commit_after>module.exports = function(server, sessionParser, store) {
	var	_ = require('lodash'),
		cookie = require('cookie'),
		cookieParser = require('cookie-parser'),
		WebSocket = require('ws'),
		Player = require('./models/PlayerModel').model;

	const logger = require('loggy');

	let wss = new WebSocket.Server({
		verifyClient: function(info, done) {
			logger.log('verifyClient() -> ', info.req.session, info.req.headers);

			if (info.req.headers.cookie || info.req.session) {
				done(info.req);
			}
		},
		server
	}),
	CLIENTS = [];

	wss.broadcast = function broadcast(data, sid, all = true) {
		logger.info('broadcast() -> ', data, sid, all);
		wss.clients.forEach(function each(client) {
			if (client.readyState === WebSocket.OPEN) {
				if (all || client !== CLIENTS[sid]) {
					logger.info('send()..');
					client.send(JSON.stringify(data));
				}
			}
		});
	};

	wss.on('connection', function connection(ws, req) {
		let parseCookie = cookie.parse(req.headers.cookie)['connect.sid'],
			sid = cookieParser.signedCookie(parseCookie, '$eCuRiTy');

		logger.info('Connection accepted:', req.headers, req.session);
		logger.info(`Clients Connected: ${wss.clients.size}`);

		logger.info('sid -> ', sid);

		// Save sessionID against the array of
		// clients so we can reference later
		CLIENTS[sid] = ws;

		ws.send(JSON.stringify({ action: 'connected', type: 'global' }));

		// This is the most important callback for us, we'll handle
		// all messages from users here.
		ws.on('message', function(message) {
			var data = JSON.parse(message),
				query = {
					sessionId: sid
				};

			// Process WebSocket message
			logger.info('Message received: ', data);

			switch (data.action) {
				case 'whoami':
					logger.info('websocket:onmessage:whoami -> ', query, sid);

					Player.find(query)
						.select('+sessionId +cardsInHand')
						.exec()
						.then(function(list) {
							let nuts = { action: 'whoami', type: 'players', nuts: list };

							logger.info(nuts);
							ws.send(JSON.stringify(nuts));
						})
						.catch(function(err) {
							logger.error(err);
						});
					break;

				default:
					wss.broadcast(data);
					break;
			}
		});

		ws.on('error', function(err) {
			logger.error(err);
		});

		ws.on('close', function(connection) {
			// close user connection
			logger.info('Connection Closed:', connection, wss.clients);
		});
	});

	global.wss = wss;

	return wss;
};
<|endoftext|>


<commit_before>import Basic from '../components/editors/Basic';
import Description from '../components/editors/Description';
import Locales from '../components/editors/Locales';

const editors = {};

export function registerEditor(name, editor) {
    editors[name] = editor;
}

export function getEditor(name) {
    if (! editors.hasOwnProperty(name)) {
        throw new Error(`Editor \`${name}\` doesn't exist`);
    }

    return editors[name];
}

registerEditor('basic', Basic);
registerEditor('locales', Locales);
registerEditor('description', Description);
<commit_msg>Update editors.js<commit_after>import Basic from '../components/editors/Basic';
import SizePicker from '../components/editors/SizePicker';
import ToggleLocales from '../components/editors/ToggleLocales';
import TranslatedDescription from '../components/editors/TranslatedDescription';

const editors = {};

export function registerEditor(name, editor) {
    editors[name] = editor;
}

export function getEditor(name) {
    if (! editors.hasOwnProperty(name)) {
        throw new Error(`Editor \`${name}\` doesn't exist`);
    }

    return editors[name];
}

registerEditor('basic', Basic);
registerEditor('sizePicker', SizePicker);
registerEditor('toggleLocales', ToggleLocales);
registerEditor('translatedDescrition', TranslatedDescription);
<|endoftext|>


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

  function afterGotSessionId (sessionId) {
    var X_NewRelic_ID = $.searchScripts(/xpid:"([^"]+)"/);

    var data = {
      adSessionId: sessionId,
    };

    var header = {
      Accept: 'application/json, text/javascript',
    };

    if (X_NewRelic_ID) {
      header['X-NewRelic-ID'] = X_NewRelic_ID;
    }

    var i = setInterval(function () {
      $.get('/shortest-url/end-adsession', data, header).then(function (text) {
        var r = _.parseJSON(text);
        if (r.status == "ok" && r.destinationUrl) {
          clearInterval(i);
          $.removeAllTimer();
          $.openLink(r.destinationUrl);
        }
      });
    }, 1000);
  }

  var hostRules = /^sh\.st|(dh10thbvu|u2ks|jnw0)\.com|digg\.to$/;

  $.register({
    rule: {
      host: hostRules,
      path: /^\/freeze\/.+/,
    },
    ready: function () {
      // Wait for the timer (server-side check)
      var o = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
          // If the button is now active
          if (mutation.target.getAttribute('class').match(/active/)) {
            o.disconnect();
            // Then we can redirect
            $.openLink(mutation.target.href);
          }
        });
      });

      o.observe($('#skip_button'), {
        attributes: true,
        attributeFilter: ['class'],
      });

    },
  });

  $.register({
    rule: {
      host: hostRules,
      path: /^\/[\d\w]+/,
    },
    ready: function () {
      $.removeNodes('iframe');

      var m = $.searchScripts(/sessionId: "([\d\w]+)",/);
      if (m) {
        afterGotSessionId(m[1]);
        return;
      }

      // script not loaded yet, wait until it appears
      var o = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
          var m = $.searchScripts(/sessionId: "([\d\w]+)",/);
          if (m) {
            o.disconnect();
            afterGotSessionId(m[1]);
          }
        });
      });
      o.observe(document.body, {
        childList: true,
      });
    },
  });

})();

// ex: ts=2 sts=2 sw=2 et
// sublime: tab_size 2; translate_tabs_to_spaces true; detect_indentation false; use_tab_stops true;
// kate: space-indent on; indent-width 2;
<commit_msg>sh.st<commit_after>(function () {
  'use strict';

  function afterGotSessionId (sessionId) {
    var X_NewRelic_ID = $.searchScripts(/xpid:"([^"]+)"/);

    var data = {
      adSessionId: sessionId,
    };

    var header = {
      Accept: 'application/json, text/javascript',
    };

    if (X_NewRelic_ID) {
      header['X-NewRelic-ID'] = X_NewRelic_ID;
    }

    var i = setInterval(function () {
      $.get('/shortest-url/end-adsession', data, header).then(function (text) {
        var r = _.parseJSON(text);
        if (r.status == "ok" && r.destinationUrl) {
          clearInterval(i);
          $.removeAllTimer();
          $.openLink(r.destinationUrl);
        }
      });
    }, 1000);
  }

  var hostRules = /^sh\.st|(dh10thbvu|u2ks|jnw0)\.com|digg\.to$/;

  $.register({
    rule: {
      host: hostRules,
      path: /https?:\/\//,
    },
    start: function () {
      var url = window.location.pathname + window.location.search + window.location.hash;
      url = url.match(/(https?:\/\/.*)$/);
      url = url[1];
      $.openLink(url);
    },
  });

  $.register({
    rule: {
      host: hostRules,
      path: /^\/freeze\/.+/,
    },
    ready: function () {
      // Wait for the timer (server-side check)
      var o = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
          // If the button is now active
          if (mutation.target.getAttribute('class').match(/active/)) {
            o.disconnect();
            // Then we can redirect
            $.openLink(mutation.target.href);
          }
        });
      });

      o.observe($('#skip_button'), {
        attributes: true,
        attributeFilter: ['class'],
      });

    },
  });

  $.register({
    rule: {
      host: hostRules,
      path: /^\/[\d\w]+/,
    },
    ready: function () {
      $.removeNodes('iframe');

      var m = $.searchScripts(/sessionId: "([\d\w]+)",/);
      if (m) {
        afterGotSessionId(m[1]);
        return;
      }

      // script not loaded yet, wait until it appears
      var o = new MutationObserver(function (mutations) {
        mutations.forEach(function (mutation) {
          var m = $.searchScripts(/sessionId: "([\d\w]+)",/);
          if (m) {
            o.disconnect();
            afterGotSessionId(m[1]);
          }
        });
      });
      o.observe(document.body, {
        childList: true,
      });
    },
  });

})();

// ex: ts=2 sts=2 sw=2 et
// sublime: tab_size 2; translate_tabs_to_spaces true; detect_indentation false; use_tab_stops true;
// kate: space-indent on; indent-width 2;
<|endoftext|>


<commit_before>"use strict";

let storage_sync_promise = new Promise((resolve) => {
	chrome.storage.sync.get(resolve);
});
let document_ready_promise = new Promise((resolve) => {
	if (/^(complete|loaded|interactive)$/.test(document.readyState)) {
		resolve();
	} else {
		document.addEventListener("DOMContentLoaded", resolve);
	}
});

let defaultsettings = {
	pin_header: true,
	synchronize_points: true,
	platform_icons: true,
	enter_button: true
};

Promise.all([storage_sync_promise, document_ready_promise]).then(([settings]) => {

settings = {...defaultsettings, ...settings};

function requestJSON(method, url, parameters) {
	return new Promise((resolve) => {
		const xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function() {
			if (xhr.readyState != 4 || xhr.status != 200) return;
			resolve(JSON.parse(xhr.responseText));
		};
		xhr.open(method, url);
		if (method === "POST") xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		xhr.send(parameters);
	});
}

// platform support cache
const Platforms = {
	get: (id) => new Promise((resolve) => {
		chrome.runtime.sendMessage({platformLookup: id}, resolve);
	})
};

// synchronize points across tabs
const pointsElement = document.querySelector(".nav__points");
let previous_points;
if (pointsElement) {
	previous_points = pointsElement.innerText;
}
function updatePoints(points, synchronize) {
	if (pointsElement && points != previous_points) {
		previous_points = points;
		pointsElement.innerText = points;
		if (synchronize !== false && settings.synchronize_points) {
			synchronizePoints(points);
		}
	}
}
function synchronizePoints(points) {
	chrome.runtime.sendMessage({points: points});
}
if (pointsElement && settings.synchronize_points) {
	synchronizePoints(pointsElement.innerText);
	chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
		if (message.points) {
			updatePoints(message.points, false);
		}
	});
	pointsElement.addEventListener("DOMSubtreeModified", (event) => {
		const points = event.target.innerText;
		if (points !== "" && points !== previous_points) {
			synchronizePoints(points);
			previous_points = points;
		}
	});
}

// Pin header bar to top
if (settings.pin_header) {
	document.body.classList.add("pin_header");
}

// clickable magnifying glass in search boxes
document.querySelectorAll(".sidebar__search-container .fa-search").forEach((element) => element.addEventListener("click", () => {
	const script = document.createElement("script");
	script.innerHTML = "$('.sidebar__search-input').trigger($.Event('keypress', {which: 13}));";
	(document.body || document.head).appendChild(script);
}));

// Giveaway browsing pages
if (window.location.pathname.match(/^\/(?:$|giveaways\/)/)) {
	// Add platform support icons
	if (settings.platform_icons) {
		const add_platform_icons = function(element, platforms = {}) {
			const next = element.nextSibling;
			if (platforms.windows) {
				const icon = document.createElement("i");
				icon.classList.add("giveaway__icon", "fa", "fa-windows");
				next.parentNode.insertBefore(icon, next);
			}
			if (platforms.mac) {
				const icon = document.createElement("i");
				icon.classList.add("giveaway__icon", "fa", "fa-apple");
				next.parentNode.insertBefore(icon, next);
			}
			if (platforms.linux) {
				const icon = document.createElement("i");
				icon.classList.add("giveaway__icon", "fa", "fa-linux");
				next.parentNode.insertBefore(icon, next);
			}
		};

		document.querySelectorAll("a.giveaway__icon[href*='//store.steampowered.com/']").forEach((element) => {
			const match = element.href.match(/^[^:]+:\/\/store.steampowered.com\/((?:app|sub)\/\d+)/);
			if (match) {
				Platforms.get(match[1]).then((platforms) => {
					add_platform_icons(element, platforms);
				});
			}
		});
	}

	const xsrfElement = document.querySelector("[name=xsrf_token]");
	if (xsrfElement) {
		const xsrf_token = xsrfElement.value;
		if (xsrfElement && xsrf_token) {
			if (settings.enter_button) {
				const click_enter_icon = function(event) {
					const enter_icon = event.target;
					const code = enter_icon.parentElement.firstElementChild.href.match(/\/giveaway\/([^\/\?]+)/);
					const wrapper = enter_icon.closest(".giveaway__row-inner-wrap");
					const entered = wrapper.classList.contains("is-faded");
					enter_icon.classList.remove("fa-plus-circle", "fa-minus-circle");
					enter_icon.classList.add("fa-spin", "fa-refresh");
					const parameters = "xsrf_token=" + encodeURIComponent(xsrf_token) + "&do=entry_" + (entered ? "delete" : "insert") + "&code=" + encodeURIComponent(code[1]);
					requestJSON("POST", "/ajax.php", parameters).then((data) => {
						enter_icon.classList.remove("fa-spin", "fa-refresh");
						if (data.type === "success") {
							if (wrapper.classList.contains("is-faded")) {
								wrapper.classList.remove("is-faded");
								enter_icon.classList.add("fa-plus-circle");
							} else {
								wrapper.classList.add("is-faded");
								enter_icon.classList.add("fa-minus-circle");
							}
						} else {
							if (wrapper.classList.contains("is-faded")) {
								enter_icon.classList.add("fa-minus-circle");
							} else {
								enter_icon.classList.add("fa-plus-circle");
							}
						}
						updatePoints(data.points);
					});
				};

				document.querySelectorAll("a.giveaway__icon[href*='//store.steampowered.com/app/'], a.giveaway__icon[href*='//store.steampowered.com/sub/']").forEach((element) => {
					// Enable entering giveways from browsing page
					if (settings.enter_button) {
						const enter_icon = document.createElement("i");
						enter_icon.classList.add("giveaway__icon", "fa");
						element.parentNode.insertBefore(enter_icon, element);
						enter_icon.addEventListener("click", click_enter_icon);
						if (enter_icon.closest(".giveaway__row-inner-wrap").classList.contains("is-faded")) {
							enter_icon.classList.add("fa-minus-circle");
						} else {
							enter_icon.classList.add("fa-plus-circle");
						}
					}
				});
			}
		}
	}
}

});

<commit_msg>add platform support icons on giveaway detail pages fixes #29<commit_after>"use strict";

let storage_sync_promise = new Promise((resolve) => {
	chrome.storage.sync.get(resolve);
});
let document_ready_promise = new Promise((resolve) => {
	if (/^(complete|loaded|interactive)$/.test(document.readyState)) {
		resolve();
	} else {
		document.addEventListener("DOMContentLoaded", resolve);
	}
});

let defaultsettings = {
	pin_header: true,
	synchronize_points: true,
	platform_icons: true,
	enter_button: true
};

Promise.all([storage_sync_promise, document_ready_promise]).then(([settings]) => {

settings = {...defaultsettings, ...settings};

function requestJSON(method, url, parameters) {
	return new Promise((resolve) => {
		const xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function() {
			if (xhr.readyState != 4 || xhr.status != 200) return;
			resolve(JSON.parse(xhr.responseText));
		};
		xhr.open(method, url);
		if (method === "POST") xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		xhr.send(parameters);
	});
}

// platform support cache
const Platforms = {
	get: (id) => new Promise((resolve) => {
		chrome.runtime.sendMessage({platformLookup: id}, resolve);
	})
};

// synchronize points across tabs
const pointsElement = document.querySelector(".nav__points");
let previous_points;
if (pointsElement) {
	previous_points = pointsElement.innerText;
}
function updatePoints(points, synchronize) {
	if (pointsElement && points != previous_points) {
		previous_points = points;
		pointsElement.innerText = points;
		if (synchronize !== false && settings.synchronize_points) {
			synchronizePoints(points);
		}
	}
}
function synchronizePoints(points) {
	chrome.runtime.sendMessage({points: points});
}
if (pointsElement && settings.synchronize_points) {
	synchronizePoints(pointsElement.innerText);
	chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
		if (message.points) {
			updatePoints(message.points, false);
		}
	});
	pointsElement.addEventListener("DOMSubtreeModified", (event) => {
		const points = event.target.innerText;
		if (points !== "" && points !== previous_points) {
			synchronizePoints(points);
			previous_points = points;
		}
	});
}

// Pin header bar to top
if (settings.pin_header) {
	document.body.classList.add("pin_header");
}

// clickable magnifying glass in search boxes
document.querySelectorAll(".sidebar__search-container .fa-search").forEach((element) => element.addEventListener("click", () => {
	const script = document.createElement("script");
	script.innerHTML = "$('.sidebar__search-input').trigger($.Event('keypress', {which: 13}));";
	(document.body || document.head).appendChild(script);
}));

// Giveaway browsing pages
if (window.location.pathname.match(/^\/(?:$|giveaways?\/)/)) {
	// Add platform support icons
	if (settings.platform_icons) {
		const add_platform_icons = function(element, platforms = {}) {
			const next = element.nextSibling;
			if (platforms.windows) {
				const icon = document.createElement("i");
				icon.classList.add("giveaway__icon", "fa", "fa-windows");
				next.parentNode.insertBefore(icon, next);
			}
			if (platforms.mac) {
				const icon = document.createElement("i");
				icon.classList.add("giveaway__icon", "fa", "fa-apple");
				next.parentNode.insertBefore(icon, next);
			}
			if (platforms.linux) {
				const icon = document.createElement("i");
				icon.classList.add("giveaway__icon", "fa", "fa-linux");
				next.parentNode.insertBefore(icon, next);
			}
		};

		document.querySelectorAll("a[href*='//store.steampowered.com/']").forEach((element) => {
			if (!element.querySelector("i.fa.fa-steam")) return;
			const match = element.href.match(/^[^:]+:\/\/store.steampowered.com\/((?:app|sub)\/\d+)/);
			if (match) {
				Platforms.get(match[1]).then((platforms) => {
					add_platform_icons(element, platforms);
				});
			}
		});
	}

	// Enable entering giveways from browsing page
	const xsrfElement = document.querySelector("[name=xsrf_token]");
	if (xsrfElement) {
		const xsrf_token = xsrfElement.value;
		if (xsrfElement && xsrf_token) {
			if (settings.enter_button) {
				const click_enter_icon = function(event) {
					const enter_icon = event.target;
					const code = enter_icon.parentElement.firstElementChild.href.match(/\/giveaway\/([^\/\?]+)/);
					const wrapper = enter_icon.closest(".giveaway__row-inner-wrap");
					const entered = wrapper.classList.contains("is-faded");
					enter_icon.classList.remove("fa-plus-circle", "fa-minus-circle");
					enter_icon.classList.add("fa-spin", "fa-refresh");
					const parameters = "xsrf_token=" + encodeURIComponent(xsrf_token) + "&do=entry_" + (entered ? "delete" : "insert") + "&code=" + encodeURIComponent(code[1]);
					requestJSON("POST", "/ajax.php", parameters).then((data) => {
						enter_icon.classList.remove("fa-spin", "fa-refresh");
						if (data.type === "success") {
							if (wrapper.classList.contains("is-faded")) {
								wrapper.classList.remove("is-faded");
								enter_icon.classList.add("fa-plus-circle");
							} else {
								wrapper.classList.add("is-faded");
								enter_icon.classList.add("fa-minus-circle");
							}
						} else {
							if (wrapper.classList.contains("is-faded")) {
								enter_icon.classList.add("fa-minus-circle");
							} else {
								enter_icon.classList.add("fa-plus-circle");
							}
						}
						updatePoints(data.points);
					});
				};

				document.querySelectorAll("a.giveaway__icon[href*='//store.steampowered.com/app/'], a.giveaway__icon[href*='//store.steampowered.com/sub/']").forEach((element) => {
					if (settings.enter_button) {
						const enter_icon = document.createElement("i");
						enter_icon.classList.add("giveaway__icon", "fa");
						element.parentNode.insertBefore(enter_icon, element);
						enter_icon.addEventListener("click", click_enter_icon);
						if (enter_icon.closest(".giveaway__row-inner-wrap").classList.contains("is-faded")) {
							enter_icon.classList.add("fa-minus-circle");
						} else {
							enter_icon.classList.add("fa-plus-circle");
						}
					}
				});
			}
		}
	}
}

});

<|endoftext|>


<commit_before>import { observable, computed, action } from 'mobx';
import moment from 'moment';
import jwt from 'jsonwebtoken';

import Store from './lib/Store';
import Request from './lib/Request';
import CachedRequest from './lib/CachedRequest';
import { gaEvent } from '../lib/analytics';

// TODO: split stores into UserStore and AuthStore
export default class UserStore extends Store {
  BASE_ROUTE = '/auth';
  WELCOME_ROUTE = `${this.BASE_ROUTE}/welcome`;
  LOGIN_ROUTE = `${this.BASE_ROUTE}/login`;
  LOGOUT_ROUTE = `${this.BASE_ROUTE}/logout`;
  SIGNUP_ROUTE = `${this.BASE_ROUTE}/signup`;
  PRICING_ROUTE = `${this.BASE_ROUTE}/signup/pricing`;
  IMPORT_ROUTE = `${this.BASE_ROUTE}/signup/import`;
  INVITE_ROUTE = `${this.BASE_ROUTE}/signup/invite`;
  PASSWORD_ROUTE = `${this.BASE_ROUTE}/password`;

  @observable loginRequest = new Request(this.api.user, 'login');
  @observable signupRequest = new Request(this.api.user, 'signup');
  @observable passwordRequest = new Request(this.api.user, 'password');
  @observable inviteRequest = new Request(this.api.user, 'invite');
  @observable getUserInfoRequest = new CachedRequest(this.api.user, 'getInfo');
  @observable updateUserInfoRequest = new Request(this.api.user, 'updateInfo');
  @observable getLegacyServicesRequest = new CachedRequest(this.api.user, 'getLegacyServices');

  @observable isImportLegacyServicesExecuting = false;
  @observable isImportLegacyServicesCompleted = false;

  @observable id;
  @observable authToken = localStorage.getItem('authToken') || null;
  @observable accountType;

  @observable hasCompletedSignup = null;

  @observable userData = {};

  @observable actionStatus = [];

  logoutReasonTypes = {
    SERVER: 'SERVER',
  };
  @observable logoutReason = null;

  constructor(...args) {
    super(...args);

    // Register action handlers
    this.actions.user.login.listen(this._login.bind(this));
    this.actions.user.retrievePassword.listen(this._retrievePassword.bind(this));
    this.actions.user.logout.listen(this._logout.bind(this));
    this.actions.user.signup.listen(this._signup.bind(this));
    this.actions.user.invite.listen(this._invite.bind(this));
    this.actions.user.update.listen(this._update.bind(this));
    this.actions.user.resetStatus.listen(this._resetStatus.bind(this));
    this.actions.user.importLegacyServices.listen(this._importLegacyServices.bind(this));

    // Reactions
    this.registerReactions([
      this._requireAuthenticatedUser,
      this._getUserData.bind(this),
    ]);
  }

  // Routes
  get loginRoute() {
    return this.LOGIN_ROUTE;
  }

  get logoutRoute() {
    return this.LOGOUT_ROUTE;
  }

  get signupRoute() {
    return this.SIGNUP_ROUTE;
  }

  get pricingRoute() {
    return this.PRICING_ROUTE;
  }

  get inviteRoute() {
    return this.INVITE_ROUTE;
  }

  get importRoute() {
    return this.IMPORT_ROUTE;
  }

  get passwordRoute() {
    return this.PASSWORD_ROUTE;
  }

  // Data
  @computed get isLoggedIn() {
    return this.authToken !== null && this.authToken !== undefined;
  }

  // @computed get isTokenValid() {
  //   return this.authToken !== null && moment(this.tokenExpiry).isAfter(moment());
  // }

  @computed get isTokenExpired() {
    if (!this.authToken) return false;

    const { tokenExpiry } = this._parseToken(this.authToken);
    return this.authToken !== null && moment(tokenExpiry).isBefore(moment());
  }

  @computed get data() {
    this.getUserInfoRequest.execute();
    return this.getUserInfoRequest.result || {};
  }

  @computed get legacyServices() {
    this.getLegacyServicesRequest.execute();
    return this.getLegacyServicesRequest.result || [];
  }

  // Actions
  @action async _login({ email, password }) {
    const authToken = await this.loginRequest.execute(email, password)._promise;
    this._setUserData(authToken);

    this.stores.router.push('/');

    gaEvent('User', 'login');
  }

  @action async _signup({ firstname, lastname, email, password, accountType, company }) {
    const authToken = await this.signupRequest.execute({
      firstname,
      lastname,
      email,
      password,
      accountType,
      company,
    });

    this.hasCompletedSignup = false;

    this._setUserData(authToken);

    this.stores.router.push(this.PRICING_ROUTE);

    gaEvent('User', 'signup');
  }

  @action async _retrievePassword({ email }) {
    const request = this.passwordRequest.execute(email);

    await request._promise;
    this.actionStatus = request.result.status || [];

    gaEvent('User', 'retrievePassword');
  }

  @action _invite({ invites }) {
    const data = invites.filter(invite => invite.email !== '');

    this.inviteRequest.execute(data);

    // we do not wait for a server response before redirecting the user
    this.stores.router.push('/');

    gaEvent('User', 'inviteUsers');
  }

  @action async _update({ userData }) {
    const response = await this.updateUserInfoRequest.execute(userData)._promise;

    this.getUserInfoRequest.patch(() => response.data);
    this.actionStatus = response.status || [];

    gaEvent('User', 'update');
  }

  @action _resetStatus() {
    this.actionStatus = [];
  }

  @action _logout() {
    localStorage.removeItem('authToken');
    this.getUserInfoRequest.invalidate().reset();
    this.authToken = null;
    // this.data = {};
  }

  @action async _importLegacyServices({ services }) {
    this.isImportLegacyServicesExecuting = true;

    for (const service of services) {
      this.actions.service.createFromLegacyService({
        data: service,
      });
      await this.stores.services.createServiceRequest._promise; // eslint-disable-line
    }

    this.isImportLegacyServicesExecuting = false;
    this.isImportLegacyServicesCompleted = true;
  }

  // This is a mobx autorun which forces the user to login if not authenticated
  _requireAuthenticatedUser = () => {
    if (this.isTokenExpired) {
      this._logout();
    }

    const { router } = this.stores;
    const currentRoute = router.location.pathname;
    if (!this.isLoggedIn
      && !currentRoute.includes(this.BASE_ROUTE)) {
      router.push(this.WELCOME_ROUTE);
    } else if (this.isLoggedIn
      && currentRoute === this.LOGOUT_ROUTE) {
      this.actions.user.logout();
      router.push(this.LOGIN_ROUTE);
    } else if (this.isLoggedIn
      && currentRoute.includes(this.BASE_ROUTE)
      && (this.hasCompletedSignup
        || this.hasCompletedSignup === null)) {
      this.stores.router.push('/');
    }
  };

  // Reactions
  async _getUserData() {
    if (this.isLoggedIn) {
      const data = await this.getUserInfoRequest.execute()._promise;

      // We need to set the beta flag for the SettingsStore
      this.actions.settings.update({
        settings: {
          beta: data.beta,
        },
      });
    }
  }

  // Helpers
  _parseToken(authToken) {
    try {
      const decoded = jwt.decode(authToken);

      return ({
        id: decoded.userId,
        tokenExpiry: moment.unix(decoded.exp).toISOString(),
        authToken,
      });
    } catch (err) {
      console.error('AccessToken Invalid');

      return false;
    }
  }

  _setUserData(authToken) {
    const data = this._parseToken(authToken);
    if (data.authToken) {
      localStorage.setItem('authToken', data.authToken);

      this.authToken = data.authToken;
      this.id = data.id;
    } else {
      this.authToken = null;
      this.id = null;
    }
  }
}
<commit_msg>fix(Onboarding): Fix service import<commit_after>import { observable, computed, action } from 'mobx';
import moment from 'moment';
import jwt from 'jsonwebtoken';

import Store from './lib/Store';
import Request from './lib/Request';
import CachedRequest from './lib/CachedRequest';
import { gaEvent } from '../lib/analytics';

// TODO: split stores into UserStore and AuthStore
export default class UserStore extends Store {
  BASE_ROUTE = '/auth';
  WELCOME_ROUTE = `${this.BASE_ROUTE}/welcome`;
  LOGIN_ROUTE = `${this.BASE_ROUTE}/login`;
  LOGOUT_ROUTE = `${this.BASE_ROUTE}/logout`;
  SIGNUP_ROUTE = `${this.BASE_ROUTE}/signup`;
  PRICING_ROUTE = `${this.BASE_ROUTE}/signup/pricing`;
  IMPORT_ROUTE = `${this.BASE_ROUTE}/signup/import`;
  INVITE_ROUTE = `${this.BASE_ROUTE}/signup/invite`;
  PASSWORD_ROUTE = `${this.BASE_ROUTE}/password`;

  @observable loginRequest = new Request(this.api.user, 'login');
  @observable signupRequest = new Request(this.api.user, 'signup');
  @observable passwordRequest = new Request(this.api.user, 'password');
  @observable inviteRequest = new Request(this.api.user, 'invite');
  @observable getUserInfoRequest = new CachedRequest(this.api.user, 'getInfo');
  @observable updateUserInfoRequest = new Request(this.api.user, 'updateInfo');
  @observable getLegacyServicesRequest = new CachedRequest(this.api.user, 'getLegacyServices');

  @observable isImportLegacyServicesExecuting = false;
  @observable isImportLegacyServicesCompleted = false;

  @observable id;
  @observable authToken = localStorage.getItem('authToken') || null;
  @observable accountType;

  @observable hasCompletedSignup = null;

  @observable userData = {};

  @observable actionStatus = [];

  logoutReasonTypes = {
    SERVER: 'SERVER',
  };
  @observable logoutReason = null;

  constructor(...args) {
    super(...args);

    // Register action handlers
    this.actions.user.login.listen(this._login.bind(this));
    this.actions.user.retrievePassword.listen(this._retrievePassword.bind(this));
    this.actions.user.logout.listen(this._logout.bind(this));
    this.actions.user.signup.listen(this._signup.bind(this));
    this.actions.user.invite.listen(this._invite.bind(this));
    this.actions.user.update.listen(this._update.bind(this));
    this.actions.user.resetStatus.listen(this._resetStatus.bind(this));
    this.actions.user.importLegacyServices.listen(this._importLegacyServices.bind(this));

    // Reactions
    this.registerReactions([
      this._requireAuthenticatedUser,
      this._getUserData.bind(this),
    ]);
  }

  // Routes
  get loginRoute() {
    return this.LOGIN_ROUTE;
  }

  get logoutRoute() {
    return this.LOGOUT_ROUTE;
  }

  get signupRoute() {
    return this.SIGNUP_ROUTE;
  }

  get pricingRoute() {
    return this.PRICING_ROUTE;
  }

  get inviteRoute() {
    return this.INVITE_ROUTE;
  }

  get importRoute() {
    return this.IMPORT_ROUTE;
  }

  get passwordRoute() {
    return this.PASSWORD_ROUTE;
  }

  // Data
  @computed get isLoggedIn() {
    return this.authToken !== null && this.authToken !== undefined;
  }

  // @computed get isTokenValid() {
  //   return this.authToken !== null && moment(this.tokenExpiry).isAfter(moment());
  // }

  @computed get isTokenExpired() {
    if (!this.authToken) return false;

    const { tokenExpiry } = this._parseToken(this.authToken);
    return this.authToken !== null && moment(tokenExpiry).isBefore(moment());
  }

  @computed get data() {
    this.getUserInfoRequest.execute();
    return this.getUserInfoRequest.result || {};
  }

  @computed get legacyServices() {
    this.getLegacyServicesRequest.execute();
    return this.getLegacyServicesRequest.result || [];
  }

  // Actions
  @action async _login({ email, password }) {
    const authToken = await this.loginRequest.execute(email, password)._promise;
    this._setUserData(authToken);

    this.stores.router.push('/');

    gaEvent('User', 'login');
  }

  @action async _signup({ firstname, lastname, email, password, accountType, company }) {
    const authToken = await this.signupRequest.execute({
      firstname,
      lastname,
      email,
      password,
      accountType,
      company,
    });

    this.hasCompletedSignup = false;

    this._setUserData(authToken);

    this.stores.router.push(this.PRICING_ROUTE);

    gaEvent('User', 'signup');
  }

  @action async _retrievePassword({ email }) {
    const request = this.passwordRequest.execute(email);

    await request._promise;
    this.actionStatus = request.result.status || [];

    gaEvent('User', 'retrievePassword');
  }

  @action _invite({ invites }) {
    const data = invites.filter(invite => invite.email !== '');

    this.inviteRequest.execute(data);

    // we do not wait for a server response before redirecting the user
    this.stores.router.push('/');

    gaEvent('User', 'inviteUsers');
  }

  @action async _update({ userData }) {
    const response = await this.updateUserInfoRequest.execute(userData)._promise;

    this.getUserInfoRequest.patch(() => response.data);
    this.actionStatus = response.status || [];

    gaEvent('User', 'update');
  }

  @action _resetStatus() {
    this.actionStatus = [];
  }

  @action _logout() {
    localStorage.removeItem('authToken');
    this.getUserInfoRequest.invalidate().reset();
    this.authToken = null;
    // this.data = {};
  }

  @action async _importLegacyServices({ services }) {
    this.isImportLegacyServicesExecuting = true;

    // Reduces recipe duplicates
    const recipes = services.filter((obj, pos, arr) => arr.map(mapObj => mapObj.recipe.id).indexOf(obj.recipe.id) === pos).map(s => s.recipe.id);

    // Install recipes
    for (const recipe of recipes) {
      // eslint-disable-next-line
      await this.stores.recipes._install({ recipeId: recipe });
    }

    for (const service of services) {
      this.actions.service.createFromLegacyService({
        data: service,
      });
      await this.stores.services.createServiceRequest._promise; // eslint-disable-line
    }

    this.isImportLegacyServicesExecuting = false;
    this.isImportLegacyServicesCompleted = true;
  }

  // This is a mobx autorun which forces the user to login if not authenticated
  _requireAuthenticatedUser = () => {
    if (this.isTokenExpired) {
      this._logout();
    }

    const { router } = this.stores;
    const currentRoute = router.location.pathname;
    if (!this.isLoggedIn
      && !currentRoute.includes(this.BASE_ROUTE)) {
      router.push(this.WELCOME_ROUTE);
    } else if (this.isLoggedIn
      && currentRoute === this.LOGOUT_ROUTE) {
      this.actions.user.logout();
      router.push(this.LOGIN_ROUTE);
    } else if (this.isLoggedIn
      && currentRoute.includes(this.BASE_ROUTE)
      && (this.hasCompletedSignup
        || this.hasCompletedSignup === null)) {
      this.stores.router.push('/');
    }
  };

  // Reactions
  async _getUserData() {
    if (this.isLoggedIn) {
      const data = await this.getUserInfoRequest.execute()._promise;

      // We need to set the beta flag for the SettingsStore
      this.actions.settings.update({
        settings: {
          beta: data.beta,
        },
      });
    }
  }

  // Helpers
  _parseToken(authToken) {
    try {
      const decoded = jwt.decode(authToken);

      return ({
        id: decoded.userId,
        tokenExpiry: moment.unix(decoded.exp).toISOString(),
        authToken,
      });
    } catch (err) {
      console.error('AccessToken Invalid');

      return false;
    }
  }

  _setUserData(authToken) {
    const data = this._parseToken(authToken);
    if (data.authToken) {
      localStorage.setItem('authToken', data.authToken);

      this.authToken = data.authToken;
      this.id = data.id;
    } else {
      this.authToken = null;
      this.id = null;
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

const ioServer = require('socket.io');
const ioClient = require('socket.io-client');
const getSessionFromCookieAsync = require('./helpers/get-session-from-cookie-async');
const ClientStreamingManager = require('./helpers/client-streaming-manager');
const ServerStreamingManager = require('./helpers/server-streaming-manager');
const StreamingProxy = require('./helpers/streaming-proxy');

/**
 * Webクライアントのストリーミング接続をサポートします。
 * セッション経由でAPIにアクセスできる機能が含まれます。
 */
module.exports = (http, sessionStore, config) => {
	const ioServerToFront = ioServer(http);

	ioServerToFront.sockets.on('connection', ioServerToFrontSocket => {
		(async () => {
			const frontManager = new ServerStreamingManager(ioServerToFront, ioServerToFrontSocket);

			// セッションからaccessKeyを取得
			const session = await getSessionFromCookieAsync(ioServerToFrontSocket.request.headers.cookie, config.web.session.name, config.web.session.SecretToken, sessionStore);

			const apiManager = new ClientStreamingManager(ioClient(config.web.apiBaseUrl, {
				query: {
					applicationKey: config.web.applicationKey,
					accessKey: session.accessKey
				}
			}));

			// 接続されるまで待機
			await apiManager.waitConnectAsync();

			apiManager.onDisconnect(() => {
				frontManager.disconnect();
				console.log('api disconnect');
			});

			// 認証チェック
			if ((await apiManager.waitEventAsync('authorization')).success === false) {
				console.log('failure authorization');
				return;
			}

			const streamingProxy = new StreamingProxy(frontManager, apiManager, false, config);
			streamingProxy.start();

			const userId = session.accessKey.split('-')[0];
			frontManager.stream('ready', {userId: userId});
		})();
	});
};
<commit_msg>update<commit_after>'use strict';

const ioServer = require('socket.io');
const ioClient = require('socket.io-client');
const getSessionFromCookieAsync = require('./helpers/get-session-from-cookie-async');
const ClientStreamingManager = require('./helpers/client-streaming-manager');
const ServerStreamingManager = require('./helpers/server-streaming-manager');
const StreamingProxy = require('./helpers/streaming-proxy');

/**
 * Webクライアントのストリーミング接続をサポートします。
 * セッション経由でAPIにアクセスできる機能が含まれます。
 */
module.exports = (http, sessionStore, config) => {
	const ioServerToFront = ioServer(http);

	ioServerToFront.sockets.on('connection', ioServerToFrontSocket => {
		(async () => {
			const frontManager = new ServerStreamingManager(ioServerToFront, ioServerToFrontSocket);

			// セッションからaccessKeyを取得
			const session = await getSessionFromCookieAsync(ioServerToFrontSocket.request.headers.cookie, config.web.session.name, config.web.session.SecretToken, sessionStore);

			const apiManager = new ClientStreamingManager(ioClient(config.web.apiBaseUrl, {
				query: {
					applicationKey: config.web.applicationKey,
					accessKey: session.accessKey
				}
			}));

			apiManager.onDisconnect(() => {
				frontManager.disconnect();
				console.log('api disconnect');
			});

			// 接続されるまで待機
			await apiManager.waitConnectAsync();

			// 認証チェック
			if ((await apiManager.waitEventAsync('authorization')).success === false) {
				console.log('failure authorization');
				return;
			}

			// API代理
			const streamingProxy = new StreamingProxy(frontManager, apiManager, false, config);
			streamingProxy.start();

			const userId = session.accessKey.split('-')[0];
			frontManager.stream('ready', {userId: userId});
		})();
	});
};
<|endoftext|>


<commit_before>import AUheader, { AUheaderBrand } from '../../scripts/uikit/header';
import AUskipLink          from '../../scripts/uikit/skip-link';
import React, { Fragment } from 'react';
import PropTypes           from 'prop-types';

/**
 * The header component
 */
const Header = ({ title, title_badge, mainmenu, header_govau, _relativeURL, _ID, _pages, _body }) => (

			<AUskipLink links={[
				{
					link: '#mainmenu',
					text: 'Skip to navigation',
				},
				{
					link: '#content',
					text: 'Skip to content',
				},
			]} />
			{ header_govau }

				<AUheader dark hero={ _ID === 'homepage' ? true : false }>

				        <AUheaderBrand
				          title="Digital Guides"
				          brandImage="/assets/img/header-logo-agov.png"
				          brandImageAlt="The Australian Government coat of Arms"
				        />

							<form className="search__searchbox" role="search" autoComplete="off" action="/search" method="get">
								<input type="text" className="au-text-input round--left" name="q" id="search-input" placeholder="Digital Guides"/>
								<button type="submit" className="au-btn icon icon--search--dark round--right" id="search-btn">Search</button>
							</form>

								<button id="search-toggle"
												className="search-toggle au-btn au-btn--tertiary au-btn--dark au-btn--block icon au-accordion--closed"
												aria-controls="mainmenu"
												aria-expanded="false"
												aria-selected="false"
												role="tab">Open Search
								</button>

								<button id="mainmenu-toggle"
												className="mainmenu-toggle au-btn au-btn--tertiary au-btn--dark au-btn--block icon au-accordion--closed"
												aria-controls="mainmenu"
												aria-expanded="false"
												aria-selected="false"
												role="tab">Open menu
								</button>

				</AUheader>

								{ mainmenu }

);

Header.propTypes = {
	/**
	 * title: Design System
	 */
	title: PropTypes.node.isRequired,
	/**
	 * title_badge: Beta
	 */
	title_badge: PropTypes.node,
};

Header.defaultProps = {};

export default Header;
<commit_msg>Use dark theme for components<commit_after>import AUheader, { AUheaderBrand } from '../../scripts/uikit/header';
import AUskipLink          from '../../scripts/uikit/skip-link';
import React, { Fragment } from 'react';
import PropTypes           from 'prop-types';

/**
 * The header component
 */
const Header = ({ title, title_badge, mainmenu, header_govau, _relativeURL, _ID, _pages, _body }) => (

			<AUskipLink links={[
				{
					link: '#mainmenu',
					text: 'Skip to navigation',
				},
				{
					link: '#content',
					text: 'Skip to content',
				},
			]} />
			{ header_govau }

				<AUheader dark hero={ _ID === 'homepage' ? true : false }>

				        <AUheaderBrand
				          title="Digital Guides"
				          brandImage="/assets/img/header-logo-agov.png"
				          brandImageAlt="The Australian Government coat of Arms"
				        />

							<form className="search__searchbox" role="search" autoComplete="off" action="/search" method="get">
								<input type="text" className="au-text-input au-text-input--dark round--left" name="q" id="search-input" placeholder="Digital Guides"/>
								<button type="submit" className="au-btn au-btn--dark icon icon--search--dark round--right" id="search-btn">Search</button>
							</form>

								<button id="search-toggle"
												className="search-toggle au-btn au-btn--tertiary au-btn--dark au-btn--block icon au-accordion--closed"
												aria-controls="mainmenu"
												aria-expanded="false"
												aria-selected="false"
												role="tab">Open Search
								</button>

								<button id="mainmenu-toggle"
												className="mainmenu-toggle au-btn au-btn--tertiary au-btn--dark au-btn--block icon au-accordion--closed"
												aria-controls="mainmenu"
												aria-expanded="false"
												aria-selected="false"
												role="tab">Open menu
								</button>

				</AUheader>

								{ mainmenu }

);

Header.propTypes = {
	/**
	 * title: Design System
	 */
	title: PropTypes.node.isRequired,
	/**
	 * title_badge: Beta
	 */
	title_badge: PropTypes.node,
};

Header.defaultProps = {};

export default Header;
<|endoftext|>


<commit_before>'use strict';

var config = require('./../build.config.js').getConfig();
var utils = require('./../utils/common.js');
var cacheBusting = require('./../utils/cacheBusting.js');
var project = require('./../utils/project.js');

var runKarmaInDev = config.build.tests.spec.runInDev && utils.hasFiles(config.app.files.root, config.app.files.js_spec);
var bowerRc = project.getBowerRc();

var devTasksConfig = {

  updateConfig: {
    dev: {
      update: {
        'compass.options.debugInfo': true,
        'less.options.dumpLineNumbers': 'mediaquery',
        'preprocess.options.context.target': 'prepare'
      }
    }
  },

  connect: {
    dev: {
      options: {
        port: config.build.server.port,
        hostname: '0.0.0.0',
        livereload: config.build.server.withLiveReloadInDev,
        base: config.build.prepare.outdir,
        middleware: utils.connectMiddleware
      },
      proxies: config.build.server.proxies
    }
  },

  replace: {
    /**
     * We need to replace variables for manual cache busting in our code. Otherwise the paths won't match in dev mode.
     * There can't be dynamically calculated paths in css code, so we don't need to check the css files.
     */
    dev_cacheBusting: {
      options: {
        prefix: '',
        patterns: cacheBusting.devPatterns()
      },
      files: [
        {
          expand: true,
          cwd: '.',
          src: [ '<%= preprocess.prepare_app_js.options.out %>/**/*.js' ],
          dest: '.'
        }
      ]
    }
  },

  /**
   * And for rapid development, we have a watch set up that checks to see if
   * any of the files listed below change, and then to execute the listed
   * tasks when they do. This just saves us from having to type "grunt" into
   * the command-line every time we want to see what we're working on; we can
   * instead just leave grunt running in a background terminal.
   *
   * But we don't need the same thing to happen for all the files.
   */
  watch: {
    /**
     * By default, we want the Live Reload to work for all tasks; this is
     * overridden in some tasks (like this file) where browser resources are
     * unaffected. It runs by default on port 35729, which your browser
     * plugin should auto-detect.
     */
    options: {
      livereload: config.build.server.withLiveReloadInDev,
      interval: 1007
    },

    /*
     * Not really a watch (empty files array). This target should execute some tasks at the beginning of watch.
     * By running the tasks from watch instead of running them before start watching, a failure will
     * not stop the hole grunt command. If a failure (jslint error or broken test) happens, grunt will remain in
     * watch mode, and we will have the change to fix the error without the need to start grunt again manually.
     */
    runOnce: {
      files: [],
      tasks: (function () {
        return [].concat(
          'replace:dev_cacheBusting'
          // do not run any test or linting on startup
//          utils.includeIf('jshint', config.build.jshint.runInDev),
//          utils.includeIf('karma:dev_spec:run', runKarmaInDev)
        );
      })(),
      options: {
        atBegin: true
      }
    },

    /**
     * When our JavaScript source files change, we want to run lint them and
     * run our unit tests.
     */
    js: {
      files: [
        config.app.files.js.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: (function () {
        return [].concat(
          utils.includeIf('jshint:src', config.build.jshint.runInDev),
          utils.includeIf('karma:dev_spec:run', runKarmaInDev),
          'preprocess:prepare_app_js',
          'replace:dev_cacheBusting',
          'processHtml:prepare'
        );
      })()
    },

    js_mock: {
      files: [
        config.app.files.js_mock.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: (function () {
        return [].concat(
          utils.includeIf('jshint:mock', config.build.jshint.runInDev),
          utils.includeIf('karma:dev_spec:run', runKarmaInDev),
          utils.includeIf('copy:prepare_app_js_mock', config.build.mocks.loadInBrowser),
          'processHtml:prepare'
        );
      })()
    },

    /**
     * When a JavaScript unit test file changes, we only want to lint it and
     * run the unit tests. We don't want to do any live reloading.
     */
    js_spec: {
      options: {
        livereload: false
      },
      files: [
        config.app.files.js_spec.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: (function () {
        return [].concat(
          utils.includeIf('jshint:spec', config.build.jshint.runInDev),
          utils.includeIf('karma:dev_spec:run', runKarmaInDev)
        );
      })()
    },

    e2e: {
      options: {
        livereload: false
      },
      files: [
        config.app.files.js_e2e.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: (function () {
        return [].concat(
          utils.includeIf('jshint:e2e', config.build.jshint.runInDev)
        );
      })()
    },

    translations: {
      files: [
        config.app.files.translations.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: [ 'translations2js:prepare' ]
    },

    /**
     * When assets are changed, copy them. Note that this will *not* copy new
     * files, so this is probably not very useful.
     */
    assets: {
      files: [
        'assets/**/*'
      ],
      tasks: [ 'copy:prepare_app_assets' ]
    },

    html: {
      files: config.app.files.html.map(utils.addCwdToPattern(config.app.files.root)),
      tasks: [ 'processHtml:prepare' ]
    },

    templates: {
      files: [
        config.app.files.templates.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: [
        'preprocess:prepare_app_templates'
      ]
    },

    less: {
      options: {
        livereload: false
      },
      files: [
        'src/**/*.less'
      ],
      tasks: (function () {
        return [].concat(
          'preprocess:prepare_app_less',
          utils.includeIf('less:prepare_app', config.build.less.enabled && utils.hasFiles(config.app.files.root, config.app.files.less)),
          'concat:prepare_css',
          'processHtml:prepare'
        );
      })()
    },
    sass: {
      options: {
        livereload: false
      },
      files: [
        'src/**/*.scss'
      ],
      tasks: (function () {
        return [].concat(
          'preprocess:prepare_app_sass',
          utils.includeIf('compass:prepare_app', config.build.sass.enabled && utils.hasFiles(config.app.files.root, config.app.files.sass)),
          'concat:prepare_css',
          'processHtml:prepare'
        );
      })()
    },
    css: {
      files: [
        '<%= concat.prepare_css.options.out %>'
      ],
      tasks: [
        'preprocess:prepare_app_css',
        'concat:prepare_css',
        'processHtml:prepare'
      ]
    }
  },

  karma: {
    dev_spec: {
      configFile: '<%= karmaConfig.spec.options.out %>',
      background: true,
      reporters: [
        'progress'
      ]
    }
  }
};

if (config.build.bower.runInDev) {
  devTasksConfig.watch.vendor = {
    options: {
      livereload: false
    },
    files: [ bowerRc.json ],
    tasks: ['shell:bower']
  };
}

module.exports = devTasksConfig;
<commit_msg>fix(live-reload): listen to all network interfaces<commit_after>'use strict';

var config = require('./../build.config.js').getConfig();
var utils = require('./../utils/common.js');
var cacheBusting = require('./../utils/cacheBusting.js');
var project = require('./../utils/project.js');

var runKarmaInDev = config.build.tests.spec.runInDev && utils.hasFiles(config.app.files.root, config.app.files.js_spec);
var bowerRc = project.getBowerRc();

var devTasksConfig = {

  updateConfig: {
    dev: {
      update: {
        'compass.options.debugInfo': true,
        'less.options.dumpLineNumbers': 'mediaquery',
        'preprocess.options.context.target': 'prepare'
      }
    }
  },

  connect: {
    dev: {
      options: {
        port: config.build.server.port,
        hostname: '*',
        livereload: config.build.server.withLiveReloadInDev,
        base: config.build.prepare.outdir,
        middleware: utils.connectMiddleware
      },
      proxies: config.build.server.proxies
    }
  },

  replace: {
    /**
     * We need to replace variables for manual cache busting in our code. Otherwise the paths won't match in dev mode.
     * There can't be dynamically calculated paths in css code, so we don't need to check the css files.
     */
    dev_cacheBusting: {
      options: {
        prefix: '',
        patterns: cacheBusting.devPatterns()
      },
      files: [
        {
          expand: true,
          cwd: '.',
          src: [ '<%= preprocess.prepare_app_js.options.out %>/**/*.js' ],
          dest: '.'
        }
      ]
    }
  },

  /**
   * And for rapid development, we have a watch set up that checks to see if
   * any of the files listed below change, and then to execute the listed
   * tasks when they do. This just saves us from having to type "grunt" into
   * the command-line every time we want to see what we're working on; we can
   * instead just leave grunt running in a background terminal.
   *
   * But we don't need the same thing to happen for all the files.
   */
  watch: {
    /**
     * By default, we want the Live Reload to work for all tasks; this is
     * overridden in some tasks (like this file) where browser resources are
     * unaffected. It runs by default on port 35729, which your browser
     * plugin should auto-detect.
     */
    options: {
      livereload: config.build.server.withLiveReloadInDev,
      interval: 1007
    },

    /*
     * Not really a watch (empty files array). This target should execute some tasks at the beginning of watch.
     * By running the tasks from watch instead of running them before start watching, a failure will
     * not stop the hole grunt command. If a failure (jslint error or broken test) happens, grunt will remain in
     * watch mode, and we will have the change to fix the error without the need to start grunt again manually.
     */
    runOnce: {
      files: [],
      tasks: (function () {
        return [].concat(
          'replace:dev_cacheBusting'
          // do not run any test or linting on startup
//          utils.includeIf('jshint', config.build.jshint.runInDev),
//          utils.includeIf('karma:dev_spec:run', runKarmaInDev)
        );
      })(),
      options: {
        atBegin: true
      }
    },

    /**
     * When our JavaScript source files change, we want to run lint them and
     * run our unit tests.
     */
    js: {
      files: [
        config.app.files.js.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: (function () {
        return [].concat(
          utils.includeIf('jshint:src', config.build.jshint.runInDev),
          utils.includeIf('karma:dev_spec:run', runKarmaInDev),
          'preprocess:prepare_app_js',
          'replace:dev_cacheBusting',
          'processHtml:prepare'
        );
      })()
    },

    js_mock: {
      files: [
        config.app.files.js_mock.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: (function () {
        return [].concat(
          utils.includeIf('jshint:mock', config.build.jshint.runInDev),
          utils.includeIf('karma:dev_spec:run', runKarmaInDev),
          utils.includeIf('copy:prepare_app_js_mock', config.build.mocks.loadInBrowser),
          'processHtml:prepare'
        );
      })()
    },

    /**
     * When a JavaScript unit test file changes, we only want to lint it and
     * run the unit tests. We don't want to do any live reloading.
     */
    js_spec: {
      options: {
        livereload: false
      },
      files: [
        config.app.files.js_spec.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: (function () {
        return [].concat(
          utils.includeIf('jshint:spec', config.build.jshint.runInDev),
          utils.includeIf('karma:dev_spec:run', runKarmaInDev)
        );
      })()
    },

    e2e: {
      options: {
        livereload: false
      },
      files: [
        config.app.files.js_e2e.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: (function () {
        return [].concat(
          utils.includeIf('jshint:e2e', config.build.jshint.runInDev)
        );
      })()
    },

    translations: {
      files: [
        config.app.files.translations.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: [ 'translations2js:prepare' ]
    },

    /**
     * When assets are changed, copy them. Note that this will *not* copy new
     * files, so this is probably not very useful.
     */
    assets: {
      files: [
        'assets/**/*'
      ],
      tasks: [ 'copy:prepare_app_assets' ]
    },

    html: {
      files: config.app.files.html.map(utils.addCwdToPattern(config.app.files.root)),
      tasks: [ 'processHtml:prepare' ]
    },

    templates: {
      files: [
        config.app.files.templates.map(utils.addCwdToPattern(config.app.files.root))
      ],
      tasks: [
        'preprocess:prepare_app_templates'
      ]
    },

    less: {
      options: {
        livereload: false
      },
      files: [
        'src/**/*.less'
      ],
      tasks: (function () {
        return [].concat(
          'preprocess:prepare_app_less',
          utils.includeIf('less:prepare_app', config.build.less.enabled && utils.hasFiles(config.app.files.root, config.app.files.less)),
          'concat:prepare_css',
          'processHtml:prepare'
        );
      })()
    },
    sass: {
      options: {
        livereload: false
      },
      files: [
        'src/**/*.scss'
      ],
      tasks: (function () {
        return [].concat(
          'preprocess:prepare_app_sass',
          utils.includeIf('compass:prepare_app', config.build.sass.enabled && utils.hasFiles(config.app.files.root, config.app.files.sass)),
          'concat:prepare_css',
          'processHtml:prepare'
        );
      })()
    },
    css: {
      files: [
        '<%= concat.prepare_css.options.out %>'
      ],
      tasks: [
        'preprocess:prepare_app_css',
        'concat:prepare_css',
        'processHtml:prepare'
      ]
    }
  },

  karma: {
    dev_spec: {
      configFile: '<%= karmaConfig.spec.options.out %>',
      background: true,
      reporters: [
        'progress'
      ]
    }
  }
};

if (config.build.bower.runInDev) {
  devTasksConfig.watch.vendor = {
    options: {
      livereload: false
    },
    files: [ bowerRc.json ],
    tasks: ['shell:bower']
  };
}

module.exports = devTasksConfig;
<|endoftext|>


<commit_before>var React = require('react');
var ReactDom = require('react-dom');
var Velocity = require('velocity-animate');

/**
 * The Velociraptor contains components and animates transitions with velocity.
 * It will only pick up direct changes to properties ('left', currently), and so
 * will not work for animating positional changes where the position is implicit
 * from DOM order. This makes it a lot simpler and lighter: if you need fully
 * automatic positional animation, look at react-shuffle or similar libraries.
 */
module.exports = React.createClass({
    displayName: 'Velociraptor',

    propTypes: {
        children: React.PropTypes.array,
        transition: React.PropTypes.object,
        container: React.PropTypes.string
    },

    componentWillMount: function() {
        this.children = {};
        this.nodes = {};
        var self = this;
        React.Children.map(this.props.children, function(c) {
            self.children[c.key] = c;
        });
    },

    componentWillReceiveProps: function(nextProps) {
        var self = this;
        var oldChildren = this.children;
        this.children = {};
        React.Children.map(nextProps.children, function(c) {
            if (oldChildren[c.key]) {
                var old = oldChildren[c.key];
                var oldNode = ReactDom.findDOMNode(self.nodes[old.key]);

                if (oldNode && oldNode.style.left != c.props.style.left) {
                    Velocity(oldNode, { left: c.props.style.left }, self.props.transition).then(function() {
                        // special case visibility because it's nonsensical to animate an invisible element
                        // so we always hidden->visible pre-transition and visible->hidden after
                        if (oldNode.style.visibility == 'visible' && c.props.style.visibility == 'hidden') {
                            oldNode.style.visibility = c.props.style.visibility;
                        }
                    });
                    if (oldNode.style.visibility == 'hidden' && c.props.style.visibility == 'visible') {
                        oldNode.style.visibility = c.props.style.visibility;
                    }
                    //console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left);
                }
                self.children[c.key] = old;
            } else {
                // new element. If it has a startStyle, use that as the style and go through
                // the enter animations
                var newProps = {
                    ref: self.collectNode.bind(self, c.key)
                };
                if (c.props.startStyle && Object.keys(c.props.startStyle).length) {
                    var startStyle = c.props.startStyle;
                    if (Array.isArray(startStyle)) {
                        startStyle = startStyle[0];
                    }
                    newProps._restingStyle = c.props.style;
                    newProps.style = startStyle;
                    //console.log("mounted@startstyle0: "+JSON.stringify(startStyle));
                    // apply the enter animations once it's mounted
                }
                self.children[c.key] = React.cloneElement(c, newProps);
            }
        });
    },

    collectNode: function(k, node) {
        if (
            node &&
            this.nodes[k] === undefined &&
            node.props.startStyle &&
            Object.keys(node.props.startStyle).length
        ) {
            var domNode = ReactDom.findDOMNode(node);
            var startStyles = node.props.startStyle;
            var transitionOpts = node.props.enterTransitionOpts;
            if (!Array.isArray(startStyles)) {
                startStyles = [ startStyles ];
                transitionOpts = [ transitionOpts ];
            }
            // start from startStyle 1: 0 is the one we gave it
            // to start with, so now we animate 1 etc.
            for (var i = 1; i < startStyles.length; ++i) {
                Velocity(domNode, startStyles[i], transitionOpts[i-1]);
                //console.log("start: "+JSON.stringify(startStyles[i]));
            }
            // and then we animate to the resting state
            Velocity(domNode, node.props._restingStyle, transitionOpts[i-1]);
            //console.log("enter: "+JSON.stringify(node.props._restingStyle));
        }
        this.nodes[k] = node;
    },

    render: function() {
        var self = this;
        var childList = Object.keys(this.children).map(function(k) {
            return React.cloneElement(self.children[k], {
                ref: self.collectNode.bind(self, self.children[k].key)
            });
        });
        return (

                {childList}

        );
    },
});
<commit_msg>Various fixes to the velociraptor<commit_after>var React = require('react');
var ReactDom = require('react-dom');
var Velocity = require('velocity-animate');

/**
 * The Velociraptor contains components and animates transitions with velocity.
 * It will only pick up direct changes to properties ('left', currently), and so
 * will not work for animating positional changes where the position is implicit
 * from DOM order. This makes it a lot simpler and lighter: if you need fully
 * automatic positional animation, look at react-shuffle or similar libraries.
 */
module.exports = React.createClass({
    displayName: 'Velociraptor',

    propTypes: {
        // either a list of child nodes, or a single child.
        children: React.PropTypes.any,

        // optional transition information for changing existing children
        transition: React.PropTypes.object,
    },

    componentWillMount: function() {
        this.children = {};
        this.nodes = {};
        this.componentWillReceiveProps(this.props);
    },

    componentWillReceiveProps: function(nextProps) {
        var self = this;
        var oldChildren = this.children;
        this.children = {};
        React.Children.toArray(nextProps.children).forEach(function(c) {
            if (oldChildren[c.key]) {
                var old = oldChildren[c.key];
                var oldNode = ReactDom.findDOMNode(self.nodes[old.key]);

                if (oldNode && oldNode.style.left != c.props.style.left) {
                    Velocity(oldNode, { left: c.props.style.left }, self.props.transition).then(function() {
                        // special case visibility because it's nonsensical to animate an invisible element
                        // so we always hidden->visible pre-transition and visible->hidden after
                        if (oldNode.style.visibility == 'visible' && c.props.style.visibility == 'hidden') {
                            oldNode.style.visibility = c.props.style.visibility;
                        }
                    });
                    if (oldNode.style.visibility == 'hidden' && c.props.style.visibility == 'visible') {
                        oldNode.style.visibility = c.props.style.visibility;
                    }
                    //console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left);
                }
                self.children[c.key] = old;
            } else {
                // new element. If it has a startStyle, use that as the style and go through
                // the enter animations
                var newProps = {
                    ref: self.collectNode.bind(self, c.key)
                };
                if (c.props.startStyle && Object.keys(c.props.startStyle).length) {
                    var startStyle = c.props.startStyle;
                    if (Array.isArray(startStyle)) {
                        startStyle = startStyle[0];
                    }
                    newProps._restingStyle = c.props.style;
                    newProps.style = startStyle;
                    //console.log("mounted@startstyle0: "+JSON.stringify(startStyle));
                    // apply the enter animations once it's mounted
                }
                self.children[c.key] = React.cloneElement(c, newProps);
            }
        });
    },

    collectNode: function(k, node) {
        if (
            node &&
            this.nodes[k] === undefined &&
            node.props.startStyle &&
            Object.keys(node.props.startStyle).length
        ) {
            var domNode = ReactDom.findDOMNode(node);
            var startStyles = node.props.startStyle;
            var transitionOpts = node.props.enterTransitionOpts;
            if (!Array.isArray(startStyles)) {
                startStyles = [ startStyles ];
                transitionOpts = [ transitionOpts ];
            }
            // start from startStyle 1: 0 is the one we gave it
            // to start with, so now we animate 1 etc.
            for (var i = 1; i < startStyles.length; ++i) {
                Velocity(domNode, startStyles[i], transitionOpts[i-1]);
                //console.log("start: "+JSON.stringify(startStyles[i]));
            }
            // and then we animate to the resting state
            Velocity(domNode, node.props._restingStyle,
                     transitionOpts[i-1])
            .then(() => {
                // once we've reached the resting state, hide the element if
                // appropriate
                domNode.style.visibility = node.props._restingStyle.visibility;
            });

            //console.log("enter: "+JSON.stringify(node.props._restingStyle));
        }
        this.nodes[k] = node;
    },

    render: function() {
        return (

                {Object.values(this.children)}

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


<commit_before>var React = require('react');
var ReactDom = require('react-dom');
var Velocity = require('velocity-vector');

/**
 * The Velociraptor contains components and animates transitions with velocity.
 * It will only pick up direct changes to properties ('left', currently), and so
 * will not work for animating positional changes where the position is implicit
 * from DOM order. This makes it a lot simpler and lighter: if you need fully
 * automatic positional animation, look at react-shuffle or similar libraries.
 */
module.exports = React.createClass({
    displayName: 'Velociraptor',

    propTypes: {
        // either a list of child nodes, or a single child.
        children: React.PropTypes.any,

        // optional transition information for changing existing children
        transition: React.PropTypes.object,

        // a list of state objects to apply to each child node in turn
        startStyles: React.PropTypes.array,

        // a list of transition options from the corresponding startStyle
        enterTransitionOpts: React.PropTypes.array,
    },

    getDefaultProps: function() {
        return {
            startStyles: [],
            enterTransitionOpts: [],
        };
    },

    componentWillMount: function() {
        this.nodes = {};
        this._updateChildren(this.props.children);
    },

    componentWillReceiveProps: function(nextProps) {
        this._updateChildren(nextProps.children);
    },

    /**
     * update `this.children` according to the new list of children given
     */
    _updateChildren: function(newChildren) {
        var self = this;
        var oldChildren = this.children || {};
        this.children = {};
        React.Children.toArray(newChildren).forEach(function(c) {
            if (oldChildren[c.key]) {
                var old = oldChildren[c.key];
                var oldNode = ReactDom.findDOMNode(self.nodes[old.key]);

                if (oldNode && oldNode.style.left != c.props.style.left) {
                    Velocity(oldNode, { left: c.props.style.left }, self.props.transition).then(function() {
                        // special case visibility because it's nonsensical to animate an invisible element
                        // so we always hidden->visible pre-transition and visible->hidden after
                        if (oldNode.style.visibility == 'visible' && c.props.style.visibility == 'hidden') {
                            oldNode.style.visibility = c.props.style.visibility;
                        }
                    });
                    //console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left);
                }
                if (oldNode.style.visibility == 'hidden' && c.props.style.visibility == 'visible') {
                    oldNode.style.visibility = c.props.style.visibility;
                }
                self.children[c.key] = old;
            } else {
                // new element. If we have a startStyle, use that as the style and go through
                // the enter animations
                var newProps = {};
                var restingStyle = c.props.style;

                var startStyles = self.props.startStyles;
                if (startStyles.length > 0) {
                    var startStyle = startStyles[0];
                    newProps.style = startStyle;
                    // console.log("mounted@startstyle0: "+JSON.stringify(startStyle));
                }

                newProps.ref = (n => self._collectNode(
                    c.key, n, restingStyle
                ));

                self.children[c.key] = React.cloneElement(c, newProps);
            }
        });
    },

    /**
     * called when a child element is mounted/unmounted
     *
     * @param {string}     k              key of the child
     * @param {null|Object} node          On mount: React node. On unmount: null
     * @param {Object}     restingStyle   final style
     */
    _collectNode: function(k, node, restingStyle) {
        if (
            node &&
            this.nodes[k] === undefined &&
            this.props.startStyles.length > 0
        ) {
            var startStyles = this.props.startStyles;
            var transitionOpts = this.props.enterTransitionOpts;
            const domNode = ReactDom.findDOMNode(node);
            // start from startStyle 1: 0 is the one we gave it
            // to start with, so now we animate 1 etc.
            for (var i = 1; i < startStyles.length; ++i) {
                Velocity(domNode, startStyles[i], transitionOpts[i-1]);
                /*
                console.log("start:",
                            JSON.stringify(transitionOpts[i-1]),
                            "->",
                            JSON.stringify(startStyles[i]),
                            );
                */
            }

            // and then we animate to the resting state
            Velocity(domNode, restingStyle,
                     transitionOpts[i-1])
            .then(() => {
                // once we've reached the resting state, hide the element if
                // appropriate
                domNode.style.visibility = restingStyle.visibility;
            });

            /*
            console.log("enter:",
                        JSON.stringify(transitionOpts[i-1]),
                        "->",
                        JSON.stringify(restingStyle));
            */
        } else if (node === null) {
            // Velocity stores data on elements using the jQuery .data()
            // method, and assumes you'll be using jQuery's .remove() to
            // remove the element, but we don't use jQuery, so we need to
            // blow away the element's data explicitly otherwise it will leak.
            // This uses Velocity's internal jQuery compatible wrapper.
            // See the bug at
            // https://github.com/julianshapiro/velocity/issues/300
            // and the FAQ entry, "Preventing memory leaks when
            // creating/destroying large numbers of elements"
            // (https://github.com/julianshapiro/velocity/issues/47)
            const domNode = ReactDom.findDOMNode(this.nodes[k]);
            Velocity.Utilities.removeData(domNode);
        }
        this.nodes[k] = node;
    },

    render: function() {
        return (

                {Object.values(this.children)}

        );
    },
});
<commit_msg>Null-guard RR logic<commit_after>var React = require('react');
var ReactDom = require('react-dom');
var Velocity = require('velocity-vector');

/**
 * The Velociraptor contains components and animates transitions with velocity.
 * It will only pick up direct changes to properties ('left', currently), and so
 * will not work for animating positional changes where the position is implicit
 * from DOM order. This makes it a lot simpler and lighter: if you need fully
 * automatic positional animation, look at react-shuffle or similar libraries.
 */
module.exports = React.createClass({
    displayName: 'Velociraptor',

    propTypes: {
        // either a list of child nodes, or a single child.
        children: React.PropTypes.any,

        // optional transition information for changing existing children
        transition: React.PropTypes.object,

        // a list of state objects to apply to each child node in turn
        startStyles: React.PropTypes.array,

        // a list of transition options from the corresponding startStyle
        enterTransitionOpts: React.PropTypes.array,
    },

    getDefaultProps: function() {
        return {
            startStyles: [],
            enterTransitionOpts: [],
        };
    },

    componentWillMount: function() {
        this.nodes = {};
        this._updateChildren(this.props.children);
    },

    componentWillReceiveProps: function(nextProps) {
        this._updateChildren(nextProps.children);
    },

    /**
     * update `this.children` according to the new list of children given
     */
    _updateChildren: function(newChildren) {
        var self = this;
        var oldChildren = this.children || {};
        this.children = {};
        React.Children.toArray(newChildren).forEach(function(c) {
            if (oldChildren[c.key]) {
                var old = oldChildren[c.key];
                var oldNode = ReactDom.findDOMNode(self.nodes[old.key]);

                if (oldNode && oldNode.style.left != c.props.style.left) {
                    Velocity(oldNode, { left: c.props.style.left }, self.props.transition).then(function() {
                        // special case visibility because it's nonsensical to animate an invisible element
                        // so we always hidden->visible pre-transition and visible->hidden after
                        if (oldNode.style.visibility == 'visible' && c.props.style.visibility == 'hidden') {
                            oldNode.style.visibility = c.props.style.visibility;
                        }
                    });
                    //console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left);
                }
                if (oldNode && oldNode.style.visibility == 'hidden' && c.props.style.visibility == 'visible') {
                    oldNode.style.visibility = c.props.style.visibility;
                }
                self.children[c.key] = old;
            } else {
                // new element. If we have a startStyle, use that as the style and go through
                // the enter animations
                var newProps = {};
                var restingStyle = c.props.style;

                var startStyles = self.props.startStyles;
                if (startStyles.length > 0) {
                    var startStyle = startStyles[0];
                    newProps.style = startStyle;
                    // console.log("mounted@startstyle0: "+JSON.stringify(startStyle));
                }

                newProps.ref = (n => self._collectNode(
                    c.key, n, restingStyle
                ));

                self.children[c.key] = React.cloneElement(c, newProps);
            }
        });
    },

    /**
     * called when a child element is mounted/unmounted
     *
     * @param {string}     k              key of the child
     * @param {null|Object} node          On mount: React node. On unmount: null
     * @param {Object}     restingStyle   final style
     */
    _collectNode: function(k, node, restingStyle) {
        if (
            node &&
            this.nodes[k] === undefined &&
            this.props.startStyles.length > 0
        ) {
            var startStyles = this.props.startStyles;
            var transitionOpts = this.props.enterTransitionOpts;
            const domNode = ReactDom.findDOMNode(node);
            // start from startStyle 1: 0 is the one we gave it
            // to start with, so now we animate 1 etc.
            for (var i = 1; i < startStyles.length; ++i) {
                Velocity(domNode, startStyles[i], transitionOpts[i-1]);
                /*
                console.log("start:",
                            JSON.stringify(transitionOpts[i-1]),
                            "->",
                            JSON.stringify(startStyles[i]),
                            );
                */
            }

            // and then we animate to the resting state
            Velocity(domNode, restingStyle,
                     transitionOpts[i-1])
            .then(() => {
                // once we've reached the resting state, hide the element if
                // appropriate
                domNode.style.visibility = restingStyle.visibility;
            });

            /*
            console.log("enter:",
                        JSON.stringify(transitionOpts[i-1]),
                        "->",
                        JSON.stringify(restingStyle));
            */
        } else if (node === null) {
            // Velocity stores data on elements using the jQuery .data()
            // method, and assumes you'll be using jQuery's .remove() to
            // remove the element, but we don't use jQuery, so we need to
            // blow away the element's data explicitly otherwise it will leak.
            // This uses Velocity's internal jQuery compatible wrapper.
            // See the bug at
            // https://github.com/julianshapiro/velocity/issues/300
            // and the FAQ entry, "Preventing memory leaks when
            // creating/destroying large numbers of elements"
            // (https://github.com/julianshapiro/velocity/issues/47)
            const domNode = ReactDom.findDOMNode(this.nodes[k]);
            Velocity.Utilities.removeData(domNode);
        }
        this.nodes[k] = node;
    },

    render: function() {
        return (

                {Object.values(this.children)}

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