<commit_before>const AppTicket = require('steam-appticket');
const ByteBuffer = require('bytebuffer');
const Crypto = require('crypto');
const StdLib = require('@doctormckay/stdlib');
const SteamCrypto = require('@doctormckay/steam-crypto');
const SteamID = require('steamid');

const Helpers = require('./helpers.js');
const Messages = require('./messages.js');
const Schema = require('../protobufs/generated/_load.js');
const SteamUser = require('../index.js');

/**
 * Request an encrypted appticket for a particular app. The app must be set up on the Steam backend for encrypted apptickets.
 * @param {int} appid - The Steam AppID of the app you want a ticket for
 * @param {Buffer} [userData] - If the app expects some "user data", provide it here
 * @param {function} [callback] - First argument is "err", second is the ticket as a Buffer (on success)
 * @return {Promise}
 */
SteamUser.prototype.getEncryptedAppTicket = function(appid, userData, callback) {
	if (typeof userData === 'function') {
		callback = userData;
		userData = Buffer.alloc(0);
	}

	return StdLib.Promises.timeoutCallbackPromise(10000, ['encryptedAppTicket'], callback, (resolve, reject) => {
		this._send(SteamUser.EMsg.ClientRequestEncryptedAppTicket, {"app_id": appid, "userdata": userData}, (body) => {
			let err = Helpers.eresultError(body.eresult);
			if (err) {
				return reject(err);
			}

			if (body.app_id != appid) {
				// Don't know if this can even happen
				return reject(new Error("Steam did not send an appticket for the requested appid"));
			}

			if (!body.encrypted_app_ticket) {
				return reject(new Error("No appticket in response"));
			}

			resolve({
				"encryptedAppTicket": Messages.encodeProto(Schema.EncryptedAppTicket, body.encrypted_app_ticket)
			});
		});
	});
};

/**
 *
 * @param {Buffer} ticket - The raw encrypted appticket
 * @param {Buffer|string} encryptionKey - The app's encryption key, either raw hex or a Buffer
 * @returns {object}
 */
SteamUser.parseEncryptedAppTicket = AppTicket.parseEncryptedAppTicket;

/**
 * Parse a Steam app or session ticket and return an object containing its details. Static.
 * @param {Buffer|ByteBuffer} ticket - The binary appticket
 * @param {boolean} [allowInvalidSignature=false] - Pass true to get back data even if the ticket has no valid signature.
 * @returns {object|null} - object if well-formed ticket (may not be valid), or null if not well-formed
 */
SteamUser.parseAppTicket = AppTicket.parseAppTicket;

SteamUser.prototype.getAuthSessionTicket = function(appid, callback) {
	return StdLib.Promises.callbackPromise(['appTicket'], callback, (resolve, reject) => {
		// For an auth session ticket we need the following:
		// 1. Length-prefixed GCTOKEN
		// 2. Length-prefixed SESSIONHEADER
		// 3. Length-prefixed OWNERSHIPTICKET (yes, even though the ticket itself has a length)
		// The GCTOKEN and SESSIONHEADER portion is passed to ClientAuthList for reuse validation
		this.getAppOwnershipTicket(appid, (err, ticket) => {
			if (err) {
				return reject(err);
			}

			let buildToken = (err) => {
				if (err) {
					return reject(err);
				}

				let gcToken = this._gcTokens.splice(0, 1)[0];
				this.emit('debug', `Using GC token ${gcToken.toString('hex')}. We now have ${this._gcTokens.length} tokens left.`);
				let buffer = ByteBuffer.allocate(4 + gcToken.length + 4 + 24 + 4 + ticket.length, ByteBuffer.LITTLE_ENDIAN);
				buffer.writeUint32(gcToken.length);
				buffer.append(gcToken);
				buffer.writeUint32(24); // length of the session header, which is always 24 bytes
				buffer.writeUint32(1); // unknown 1
				buffer.writeUint32(2); // unknown 2
				buffer.writeUint32(StdLib.IPv4.stringToInt(this.publicIP)); // external IP
				buffer.writeUint32(0); // filler
				buffer.writeUint32(Date.now() - this._connectTime); // timestamp
				buffer.writeUint32(++this._connectionCount); // connection count
				buffer.writeUint32(ticket.length);
				buffer.append(ticket);

				buffer = buffer.flip().toBuffer();

				// We need to activate our ticket
				this.activateAuthTickets(appid, buffer, () => {
					resolve({appTicket: buffer});
				});
			};

			// Do we have any GC tokens?
			if (this._gcTokens.length > 0) {
				buildToken();
			} else {
				Helpers.onceTimeout(10000, this, '_gcTokens', buildToken);
			}
		});
	});
};

SteamUser.prototype.getAppOwnershipTicket = function(appid, callback) {
	return StdLib.Promises.timeoutCallbackPromise(10000, ['appOwnershipTicket'], callback, async (resolve, reject) => {
		// See if we have one saved
		let filename = `appOwnershipTicket_${this.steamID}_${appid}.bin`;
		let file = await this._readFile(filename);
		if (file) {
			let parsed = SteamUser.parseAppTicket(file);
			// Only return the saved ticket if it has a valid signature, expires more than 6 hours from now, and has the same external IP as we have right now.
			if (parsed && parsed.isValid && parsed.ownershipTicketExpires - Date.now() >= (1000 * 60 * 60 * 6) && parsed.ownershipTicketExternalIP == this.publicIP) {
				return resolve({"appOwnershipTicket": file});
			}
		}

		// No saved ticket, we'll have to get a new one
		this._send(SteamUser.EMsg.ClientGetAppOwnershipTicket, {"app_id": appid}, async (body) => {
			let err = Helpers.eresultError(body.eresult);
			if (err) {
				return reject(err);
			}

			if (body.app_id != appid) {
				return reject(new Error("Cannot get app ownership ticket"));
			}

			let ticket = body.ticket;
			if (ticket && ticket.length > 10 && this.options.saveAppTickets) {
				await this._saveFile(filename, ticket);
			}

			resolve({"appOwnershipTicket": ticket});
		});
	});
};

SteamUser.prototype.activateAuthTickets = function(appid, tickets, callback) {
	if (!Array.isArray(tickets)) {
		tickets = [tickets];
	}

	return StdLib.Promises.timeoutCallbackPromise(10000, null, callback, true, async (resolve, reject) => {
		tickets.forEach((ticket, idx) => {
			let authTicket = null;

			if (ticket instanceof Buffer && ticket.length == 52) {
				authTicket = ticket;
			}

			if (ticket.authTicket) {
				authTicket = ticket.authTicket;
			} else {
				ticket = SteamUser.parseAppTicket(ticket);
				if (!ticket) {
					return reject(new Error(`Ticket ${idx} is invalid`));
				}

				authTicket = ticket.authTicket;
			}

			let sid = new SteamID();
			sid.universe = this.steamID.universe;
			sid.type = SteamID.Type.INDIVIDUAL;
			sid.instance = SteamID.Instance.DESKTOP;
			sid.accountid = authTicket.readUInt32LE(12);
			sid = sid.getSteamID64();

			let isOurTicket = (sid == this.steamID.getSteamID64());

			// If we already have an active ticket for this appid/steamid combo, remove it
			let existingTicketIdx = this._activeAuthTickets.findIndex(tkt => tkt.steamid == (isOurTicket ? 0 : sid) && tkt.gameid == appid);
			if (existingTicketIdx != -1) {
				this.emit('debug', `Canceling existing ticket ${existingTicketIdx} for ${appid}/${isOurTicket ? 'self' : sid}`);
				this._activeAuthTickets.splice(existingTicketIdx, 1);
			}

			this._activeAuthTickets.push({
				estate: isOurTicket ? 0 : 1,
				steamid: isOurTicket ? 0 : sid,
				gameid: appid,
				h_steam_pipe: this._hSteamPipe,
				ticket_crc: StdLib.Hashing.crc32(authTicket),
				ticket: authTicket
			});
		});

		await this._sendAuthList();
		resolve();
	});
};

SteamUser.prototype.cancelAuthTickets = function(appid, ticketOwnerSteamIds, callback) {
	if (typeof ticketOwnerSteamIds == 'function') {
		callback = ticketOwnerSteamIds;
		ticketOwnerSteamIds = null;
	}

	if (!Array.isArray(ticketOwnerSteamIds)) {
		ticketOwnerSteamIds = [ticketOwnerSteamIds];
	}

	return StdLib.Promises.timeoutCallbackPromise(10000, null, callback, true, async (resolve) => {
		let canceledTicketCount = 0;
		ticketOwnerSteamIds.forEach((sid) => {
			if (!sid) {
				// any falsy steamids should be treated as our own
				sid = this.steamID;
			}

			sid = Helpers.steamID(sid);
			sid = sid.accountid == this.steamID.accountid ? 0 : sid.getSteamID64();

			let ticketToCancel = this._activeAuthTickets.findIndex(tkt => tkt.gameid == appid && tkt.steamid == sid);
			if (ticketToCancel != -1) {
				this._activeAuthTickets.splice(ticketToCancel, 1);
				canceledTicketCount++;
			}
		});

		await this._sendAuthList(appid);
		resolve({canceledTicketCount});
	});
};

SteamUser.prototype.cancelAllAuthTickets = function(appid, callback) {
	let steamids = this._activeAuthTickets.filter(tkt => tkt.gameid == appid).map(tkt => tkt.steamid);
	return this.cancelAuthTickets(appid, steamids, callback);
};

SteamUser.prototype._sendAuthList = function(forceAppId) {
	return StdLib.Promises.timeoutPromise(10000, (resolve) => {
		let uniqueAppIds = this._activeAuthTickets.map(tkt => tkt.gameid).filter((appid, idx, arr) => arr.indexOf(appid) == idx);
		if (forceAppId && !uniqueAppIds.includes(forceAppId)) {
			uniqueAppIds.push(forceAppId);
		}

		this.emit('debug', `Sending auth list with ${this._activeAuthTickets.length} active tickets`);

		this._send(SteamUser.EMsg.ClientAuthList, {
			tokens_left: this._gcTokens ? this._gcTokens.length : 0,
			last_request_seq: this._authSeqMe,
			last_request_seq_from_server: this._authSeqThem,
			tickets: this._activeAuthTickets,
			app_ids: uniqueAppIds,
			message_sequence: ++this._authSeqMe
		}, (body) => {
			this._authSeqThem = body.message_sequence;
			resolve();
		});
	});
};

// Handlers

SteamUser.prototype._handlerManager.add(SteamUser.EMsg.ClientGameConnectTokens, function(body) {
	this.emit('debug', `Received ${body.tokens.length} game connect tokens. Had ${this._gcTokens.length} tokens.`);
	body.tokens.forEach((token) => {
		this._gcTokens.push(token);
	});

	this.emit('_gcTokens'); // internal private event
});

SteamUser.prototype._handlerManager.add(SteamUser.EMsg.ClientTicketAuthComplete, function(body) {
	// First find the ticket in our local cache that matches this crc
	let idx = this._activeAuthTickets.findIndex(tkt => tkt.ticket_crc == body.ticket_crc);
	if (idx == -1) {
		this.emit('debug', `Cannot find CRC ${body.ticket_crc} for ticket from user ${body.steam_id} with state ${body.eauth_session_response}`);
		return;
	}

	let cacheTicket = this._activeAuthTickets[idx];

	// If the auth session response is anything besides OK, we need to remove the ticket from our auth list
	if (body.eauth_session_response != SteamUser.EAuthSessionResponse.OK) {
		this._activeAuthTickets.splice(idx, 1);
		this.emit('debug', `Removed canceled ticket ${body.ticket_crc} with state ${body.eauth_session_response}. Now have ${this._activeAuthTickets.length} active tickets.`);
	}

	// Update the cached ticket's state
	cacheTicket.estate = body.estate;

	let eventBody = {
		steamID: new SteamID(body.steam_id.toString()), // if our own ticket, this is the steamid that just validated it
		appOwnerSteamID: body.owner_steam_id.toString() == '0' ? null : new SteamID(body.owner_steam_id.toString()),
		appID: parseInt(body.game_id.toString(), 10),
		ticketCrc: body.ticket_crc,
		state: body.estate,
		authSessionResponse: body.eauth_session_response
	};

	this.emit(cacheTicket.steamid == 0 ? 'authTicketStatus' : 'authTicketValidation', eventBody);
});
<commit_msg>Overhauled and improved appticket validation, again<commit_after>const AppTicket = require('steam-appticket');
const ByteBuffer = require('bytebuffer');
const Crypto = require('crypto');
const StdLib = require('@doctormckay/stdlib');
const SteamCrypto = require('@doctormckay/steam-crypto');
const SteamID = require('steamid');

const Helpers = require('./helpers.js');
const Messages = require('./messages.js');
const Schema = require('../protobufs/generated/_load.js');
const SteamUser = require('../index.js');

/**
 * Request an encrypted appticket for a particular app. The app must be set up on the Steam backend for encrypted apptickets.
 * @param {int} appid - The Steam AppID of the app you want a ticket for
 * @param {Buffer} [userData] - If the app expects some "user data", provide it here
 * @param {function} [callback] - First argument is "err", second is the ticket as a Buffer (on success)
 * @return {Promise}
 */
SteamUser.prototype.getEncryptedAppTicket = function(appid, userData, callback) {
	if (typeof userData === 'function') {
		callback = userData;
		userData = Buffer.alloc(0);
	}

	return StdLib.Promises.timeoutCallbackPromise(10000, ['encryptedAppTicket'], callback, (resolve, reject) => {
		this._send(SteamUser.EMsg.ClientRequestEncryptedAppTicket, {"app_id": appid, "userdata": userData}, (body) => {
			let err = Helpers.eresultError(body.eresult);
			if (err) {
				return reject(err);
			}

			if (body.app_id != appid) {
				// Don't know if this can even happen
				return reject(new Error("Steam did not send an appticket for the requested appid"));
			}

			if (!body.encrypted_app_ticket) {
				return reject(new Error("No appticket in response"));
			}

			resolve({
				"encryptedAppTicket": Messages.encodeProto(Schema.EncryptedAppTicket, body.encrypted_app_ticket)
			});
		});
	});
};

/**
 *
 * @param {Buffer} ticket - The raw encrypted appticket
 * @param {Buffer|string} encryptionKey - The app's encryption key, either raw hex or a Buffer
 * @returns {object}
 */
SteamUser.parseEncryptedAppTicket = AppTicket.parseEncryptedAppTicket;

/**
 * Parse a Steam app or session ticket and return an object containing its details. Static.
 * @param {Buffer|ByteBuffer} ticket - The binary appticket
 * @param {boolean} [allowInvalidSignature=false] - Pass true to get back data even if the ticket has no valid signature.
 * @returns {object|null} - object if well-formed ticket (may not be valid), or null if not well-formed
 */
SteamUser.parseAppTicket = AppTicket.parseAppTicket;

SteamUser.prototype.createAuthSessionTicket = function(appid, callback) {
	return StdLib.Promises.callbackPromise(['appTicket'], callback, (resolve, reject) => {
		// For an auth session ticket we need the following:
		// 1. Length-prefixed GCTOKEN
		// 2. Length-prefixed SESSIONHEADER
		// 3. Length-prefixed OWNERSHIPTICKET (yes, even though the ticket itself has a length)
		// The GCTOKEN and SESSIONHEADER portion is passed to ClientAuthList for reuse validation
		this.getAppOwnershipTicket(appid, (err, ticket) => {
			if (err) {
				return reject(err);
			}

			let buildToken = async (err) => {
				if (err) {
					return reject(err);
				}

				let gcToken = this._gcTokens.splice(0, 1)[0];
				this.emit('debug', `Using GC token ${gcToken.toString('hex')}. We now have ${this._gcTokens.length} tokens left.`);
				let buffer = ByteBuffer.allocate(4 + gcToken.length + 4 + 24 + 4 + ticket.length, ByteBuffer.LITTLE_ENDIAN);
				buffer.writeUint32(gcToken.length);
				buffer.append(gcToken);
				buffer.writeUint32(24); // length of the session header, which is always 24 bytes
				buffer.writeUint32(1); // unknown 1
				buffer.writeUint32(2); // unknown 2
				buffer.writeUint32(StdLib.IPv4.stringToInt(this.publicIP)); // external IP
				buffer.writeUint32(0); // filler
				buffer.writeUint32(Date.now() - this._connectTime); // timestamp
				buffer.writeUint32(++this._connectionCount); // connection count
				buffer.writeUint32(ticket.length);
				buffer.append(ticket);

				buffer = buffer.flip().toBuffer();

				// We need to activate our ticket
				await this.activateAuthSessionTickets(appid, buffer);
				resolve({appTicket: buffer});
			};

			// Do we have any GC tokens?
			if (this._gcTokens.length > 0) {
				buildToken();
			} else {
				Helpers.onceTimeout(10000, this, '_gcTokens', buildToken);
			}
		});
	});
};

SteamUser.prototype.getAppOwnershipTicket = function(appid, callback) {
	return StdLib.Promises.timeoutCallbackPromise(10000, ['appOwnershipTicket'], callback, async (resolve, reject) => {
		// See if we have one saved
		let filename = `appOwnershipTicket_${this.steamID}_${appid}.bin`;
		let file = await this._readFile(filename);
		if (file) {
			let parsed = SteamUser.parseAppTicket(file);
			// Only return the saved ticket if it has a valid signature, expires more than 6 hours from now, and has the same external IP as we have right now.
			if (parsed && parsed.isValid && parsed.ownershipTicketExpires - Date.now() >= (1000 * 60 * 60 * 6) && parsed.ownershipTicketExternalIP == this.publicIP) {
				return resolve({appOwnershipTicket: file});
			}
		}

		// No saved ticket, we'll have to get a new one
		this._send(SteamUser.EMsg.ClientGetAppOwnershipTicket, {app_id: appid}, async (body) => {
			let err = Helpers.eresultError(body.eresult);
			if (err) {
				return reject(err);
			}

			if (body.app_id != appid) {
				return reject(new Error("Cannot get app ownership ticket"));
			}

			let ticket = body.ticket;
			if (ticket && ticket.length > 10 && this.options.saveAppTickets) {
				await this._saveFile(filename, ticket);
			}

			resolve({appOwnershipTicket: ticket});
		});
	});
};

SteamUser.prototype.activateAuthSessionTickets = function(appid, tickets, callback) {
	if (!Array.isArray(tickets)) {
		tickets = [tickets];
	}

	return StdLib.Promises.timeoutCallbackPromise(10000, null, callback, true, async (resolve, reject) => {
		tickets.forEach((ticket, idx) => {
			if (ticket instanceof Buffer) {
				ticket = SteamUser.parseAppTicket(ticket);
			}

			if (!ticket || !ticket.isValid || !Buffer.isBuffer(ticket.authTicket)) {
				let invalidReason = 'is invalid';
				if (ticket.isExpired) {
					invalidReason = 'is expired';
				}
				if (!ticket.hasValidSignature) {
					invalidReason = ticket.signature ? 'has an invalid signature' : 'has no signature';
				}

				return reject(new Error(`Ticket ${idx} ${invalidReason}`));
			}

			// Make sure this is for the game we expect
			if (ticket.appID != appid) {
				return reject(new Error(`Ticket ${idx} is for the wrong app: ${ticket.appID}`));
			}

			let sid = ticket.steamID.getSteamID64();

			let isOurTicket = (sid == this.steamID.getSteamID64());
			let thisTicket = {
				estate: isOurTicket ? 0 : 1,
				steamid: isOurTicket ? 0 : sid,
				gameid: ticket.appID,
				h_steam_pipe: this._hSteamPipe,
				ticket_crc: StdLib.Hashing.crc32(ticket.authTicket),
				ticket: ticket.authTicket
			};

			// Check if this ticket is already active
			if (this._activeAuthTickets.find(tkt => tkt.steamid == thisTicket.steamid && tkt.ticket_crc == thisTicket.ticket_crc)) {
				this.emit('debug', `Not attempting to activate already active ticket ${thisTicket.ticket_crc} for ${appid}/${isOurTicket ? 'self' : sid}`);
				return;
			}

			// If we already have an active ticket for this appid/steamid combo, remove it, but not if it's our own
			if (!isOurTicket) {
				let existingTicketIdx = this._activeAuthTickets.findIndex(tkt => tkt.steamid == thisTicket.steamid && tkt.gameid == thisTicket.gameid);
				if (existingTicketIdx != -1) {
					let existingTicket = this._activeAuthTickets[existingTicketIdx];
					this.emit('debug', `Canceling existing ticket ${existingTicket.ticket_crc} for ${existingTicket.gameid}/${existingTicket.steamid}`);
					this._activeAuthTickets.splice(existingTicketIdx, 1);
				}
			}

			this._activeAuthTickets.push(thisTicket);
		});

		await this._sendAuthList();
		resolve();
	});
};

/**
 * Cancel your own auth session tickets. Once canceled, every client that activated your ticket will receive a
 * notification that your ticket has been canceled. If you are still in-game, they will probably kick you.
 * @param {number} appid
 * @param {string[]|string|null} [gcTokens=null] - The gcToken from the specific ticket(s) you want to cancel. Omit or pass null to cancel all active tickets.
 * @param {function} callback
 * @returns {Promise}
 */
SteamUser.prototype.cancelAuthSessionTickets = function(appid, gcTokens, callback) {
	if (typeof gcTokens == 'function') {
		callback = gcTokens;
		gcTokens = null;
	}

	if (typeof gcTokens == 'string') {
		gcTokens = [gcTokens];
	}

	return StdLib.Promises.timeoutCallbackPromise(10000, null, callback, true, async (resolve) => {
		let matchingTickets = this._activeAuthTickets.filter(tkt => tkt.steamid == 0 && tkt.gameid == appid);
		if (gcTokens) {
			matchingTickets = matchingTickets.filter((tkt) => {
				let authTicket = ByteBuffer.wrap(tkt.ticket, ByteBuffer.LITTLE_ENDIAN);
				authTicket.skip(4);
				let gcToken = authTicket.readUint64();
				return gcTokens.includes(gcToken);
			});
		}

		this.emit('debug', `Got ${matchingTickets.length} matching tickets to cancel`);
		let canceledTicketCount = matchingTickets.length;
		while (matchingTickets.length > 0) {
			let ticket = matchingTickets.splice(0, 1)[0];
			let idx = this._activeAuthTickets.indexOf(ticket);
			if (idx != -1) {
				this._activeAuthTickets.splice(idx, 1);
			}
		}

		await this._sendAuthList(appid);
		resolve({canceledTicketCount});
	});
};

/**
 * Ends our auth sessions with other users. Once ended, we will no longer receive notifications when users' auth session
 * tickets are canceled or otherwise invalidated. If we've already been notified that a ticket was canceled or invalidated,
 * the session was already automatically ended.
 * @param {number} appid
 * @param {null|string|string[]|SteamID|SteamID[]} steamIDs - SteamID objects or strings that can parse into SteamIDs. Null or omit to cancel all auth sessions for the app.
 * @param {function} callback
 * @returns {Promise}
 */
SteamUser.prototype.endAuthSessions = function(appid, steamIDs, callback) {
	if (typeof steamIDs == 'function') {
		callback = steamIDs;
		steamIDs = null;
	}

	if (steamIDs && !Array.isArray(steamIDs)) {
		steamIDs = [steamIDs];
	}

	return StdLib.Promises.timeoutCallbackPromise(10000, null, callback, true, async (resolve) => {
		let matchingTickets = this._activeAuthTickets.filter(tkt => tkt.gameid == appid && tkt.steamid != 0);
		if (steamIDs) {
			steamIDs = steamIDs.map(Helpers.steamID).map(sid => sid.getSteamID64());
			matchingTickets = matchingTickets.filter(tkt => steamIDs.includes(tkt.steamid.getSteamID64()));
		}

		this.emit('debug', `Got ${matchingTickets.length} matching tickets to end auth sessions for`);
		let canceledTicketCount = matchingTickets.length;
		while (matchingTickets.length > 0) {
			let ticket = matchingTickets.splice(0, 1)[0];
			let idx = this._activeAuthTickets.indexOf(ticket);
			if (idx != -1) {
				this._activeAuthTickets.splice(idx, 1);
			}
		}

		await this._sendAuthList(appid);
		resolve({canceledTicketCount});
	});
};

SteamUser.prototype.getActiveAuthTickets = function() {
	return this._activeAuthTickets.map((ticket) => {
		let authTicket = ByteBuffer.wrap(ticket.ticket, ByteBuffer.LITTLE_ENDIAN);
		authTicket.skip(4);
		let gcToken = authTicket.readUint64();

		return {
			appID: ticket.gameid,
			steamID: ticket.steamid == 0 ? this.steamID : Helpers.steamID(ticket.steamid),
			ticketCrc: ticket.ticket_crc,
			gcToken,
			validated: ticket.estate > 1
		};
	});
};

SteamUser.prototype._sendAuthList = function(forceAppId) {
	return StdLib.Promises.timeoutPromise(10000, (resolve) => {
		let uniqueAppIds = this._activeAuthTickets.map(tkt => tkt.gameid).filter((appid, idx, arr) => arr.indexOf(appid) == idx);
		if (forceAppId && !uniqueAppIds.includes(forceAppId)) {
			uniqueAppIds.push(forceAppId);
		}

		this.emit('debug', `Sending auth list with ${this._activeAuthTickets.length} active tickets`);

		this._send(SteamUser.EMsg.ClientAuthList, {
			tokens_left: this._gcTokens ? this._gcTokens.length : 0,
			last_request_seq: this._authSeqMe,
			last_request_seq_from_server: this._authSeqThem,
			tickets: this._activeAuthTickets,
			app_ids: uniqueAppIds,
			message_sequence: ++this._authSeqMe
		}, (body) => {
			this._authSeqThem = body.message_sequence;
			resolve();
		});
	});
};

// Handlers

SteamUser.prototype._handlerManager.add(SteamUser.EMsg.ClientGameConnectTokens, function(body) {
	this.emit('debug', `Received ${body.tokens.length} game connect tokens. Had ${this._gcTokens.length} tokens.`);
	body.tokens.forEach((token) => {
		this._gcTokens.push(token);
	});

	this.emit('_gcTokens'); // internal private event
});

SteamUser.prototype._handlerManager.add(SteamUser.EMsg.ClientTicketAuthComplete, function(body) {
	// First find the ticket in our local cache that matches this crc
	let idx = this._activeAuthTickets.findIndex(tkt => tkt.ticket_crc == body.ticket_crc);
	if (idx == -1) {
		this.emit('debug', `Cannot find CRC ${body.ticket_crc} for ticket from user ${body.steam_id} with state ${body.eauth_session_response}`);
		return;
	}

	let cacheTicket = this._activeAuthTickets[idx];

	// If the auth session response is anything besides OK, we need to remove the ticket from our auth list
	if (body.eauth_session_response != SteamUser.EAuthSessionResponse.OK) {
		this._activeAuthTickets.splice(idx, 1);
		this.emit('debug', `Removed canceled ticket ${body.ticket_crc} with state ${body.eauth_session_response}. Now have ${this._activeAuthTickets.length} active tickets.`);
	}

	// Update the cached ticket's state
	cacheTicket.estate = body.estate;

	// Get the GC token
	let authTicket = ByteBuffer.wrap(cacheTicket.ticket, ByteBuffer.LITTLE_ENDIAN);
	authTicket.skip(4);
	let ticketGcToken = authTicket.readUint64();

	let eventBody = {
		steamID: new SteamID(body.steam_id.toString()), // if our own ticket, this is the steamid that just validated it
		appOwnerSteamID: body.owner_steam_id.toString() == '0' ? null : new SteamID(body.owner_steam_id.toString()),
		appID: parseInt(body.game_id.toString(), 10),
		ticketCrc: body.ticket_crc,
		ticketGcToken,
		state: body.estate,
		authSessionResponse: body.eauth_session_response
	};

	this.emit(cacheTicket.steamid == 0 ? 'authTicketStatus' : 'authTicketValidation', eventBody);
});
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import { expandCommunityTimeline } from '../../actions/timelines';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import ColumnSettingsContainer from './containers/column_settings_container';
import { connectCommunityStream } from '../../actions/streaming';

const messages = defineMessages({
  title: { id: 'column.community', defaultMessage: 'Local timeline' },
});

const mapStateToProps = (state, { onlyMedia, columnId }) => {
  const uuid = columnId;
  const columns = state.getIn(['settings', 'columns']);
  const index = columns.findIndex(c => c.get('uuid') === uuid);

  return {
    hasUnread: state.getIn(['timelines', `community${onlyMedia ? ':media' : ''}`, 'unread']) > 0 || state.getIn(['timelines', `community${onlyMedia ? ':media' : ''}`, 'pendingItems']).size > 0,
    onlyMedia: (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'community', 'other', 'onlyMedia']),
  };
};

export default @connect(mapStateToProps)
@injectIntl
class CommunityTimeline extends React.PureComponent {

  static contextTypes = {
    router: PropTypes.object,
  };

  static defaultProps = {
    onlyMedia: false,
  };

  static propTypes = {
    dispatch: PropTypes.func.isRequired,
    shouldUpdateScroll: PropTypes.func,
    columnId: PropTypes.string,
    intl: PropTypes.object.isRequired,
    hasUnread: PropTypes.bool,
    multiColumn: PropTypes.bool,
    onlyMedia: PropTypes.bool,
  };

  handlePin = () => {
    const { columnId, dispatch, onlyMedia } = this.props;

    if (columnId) {
      dispatch(removeColumn(columnId));
    } else {
      dispatch(addColumn('COMMUNITY', { other: { onlyMedia } }));
    }
  }

  handleMove = (dir) => {
    const { columnId, dispatch } = this.props;
    dispatch(moveColumn(columnId, dir));
  }

  handleHeaderClick = () => {
    this.column.scrollTop();
  }

  componentDidMount () {
    const { dispatch, onlyMedia } = this.props;

    dispatch(expandCommunityTimeline({ onlyMedia }));
    this.disconnect = dispatch(connectCommunityStream({ onlyMedia }));
  }

  componentDidUpdate (prevProps) {
    if (prevProps.onlyMedia !== this.props.onlyMedia) {
      const { dispatch, onlyMedia } = this.props;

      this.disconnect();
      dispatch(expandCommunityTimeline({ onlyMedia }));
      this.disconnect = dispatch(connectCommunityStream({ onlyMedia }));
    }
  }

  componentWillUnmount () {
    if (this.disconnect) {
      this.disconnect();
      this.disconnect = null;
    }
  }

  setRef = c => {
    this.column = c;
  }

  handleLoadMore = maxId => {
    const { dispatch, onlyMedia } = this.props;

    dispatch(expandCommunityTimeline({ maxId, onlyMedia }));
  }

  render () {
    const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
    const pinned = !!columnId;

    return (
      <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
        <ColumnHeader
          icon='users'
          active={hasUnread}
          title={intl.formatMessage(messages.title)}
          onPin={this.handlePin}
          onMove={this.handleMove}
          onClick={this.handleHeaderClick}
          pinned={pinned}
          multiColumn={multiColumn}
        >
          <ColumnSettingsContainer columnId={columnId} />
        </ColumnHeader>

        <StatusListContainer
          trackScroll={!pinned}
          scrollKey={`community_timeline-${columnId}`}
          timelineId={`community${onlyMedia ? ':media' : ''}`}
          onLoadMore={this.handleLoadMore}
          emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
          shouldUpdateScroll={shouldUpdateScroll}
          bindToDocument={!multiColumn}
        />
      </Column>
    );
  }

}
<commit_msg>Check existence of timeline state in community timeline (#11880)<commit_after>import React from 'react';
import { connect } from 'react-redux';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import { expandCommunityTimeline } from '../../actions/timelines';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import ColumnSettingsContainer from './containers/column_settings_container';
import { connectCommunityStream } from '../../actions/streaming';

const messages = defineMessages({
  title: { id: 'column.community', defaultMessage: 'Local timeline' },
});

const mapStateToProps = (state, { onlyMedia, columnId }) => {
  const uuid = columnId;
  const columns = state.getIn(['settings', 'columns']);
  const index = columns.findIndex(c => c.get('uuid') === uuid);
  const timelineState = state.getIn(['timelines', `community${onlyMedia ? ':media' : ''}`]);

  return {
    hasUnread: !!timelineState && (timelineState.get('unread') > 0 || timelineState.get('pendingItems').size > 0),
    onlyMedia: (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'community', 'other', 'onlyMedia']),
  };
};

export default @connect(mapStateToProps)
@injectIntl
class CommunityTimeline extends React.PureComponent {

  static contextTypes = {
    router: PropTypes.object,
  };

  static defaultProps = {
    onlyMedia: false,
  };

  static propTypes = {
    dispatch: PropTypes.func.isRequired,
    shouldUpdateScroll: PropTypes.func,
    columnId: PropTypes.string,
    intl: PropTypes.object.isRequired,
    hasUnread: PropTypes.bool,
    multiColumn: PropTypes.bool,
    onlyMedia: PropTypes.bool,
  };

  handlePin = () => {
    const { columnId, dispatch, onlyMedia } = this.props;

    if (columnId) {
      dispatch(removeColumn(columnId));
    } else {
      dispatch(addColumn('COMMUNITY', { other: { onlyMedia } }));
    }
  }

  handleMove = (dir) => {
    const { columnId, dispatch } = this.props;
    dispatch(moveColumn(columnId, dir));
  }

  handleHeaderClick = () => {
    this.column.scrollTop();
  }

  componentDidMount () {
    const { dispatch, onlyMedia } = this.props;

    dispatch(expandCommunityTimeline({ onlyMedia }));
    this.disconnect = dispatch(connectCommunityStream({ onlyMedia }));
  }

  componentDidUpdate (prevProps) {
    if (prevProps.onlyMedia !== this.props.onlyMedia) {
      const { dispatch, onlyMedia } = this.props;

      this.disconnect();
      dispatch(expandCommunityTimeline({ onlyMedia }));
      this.disconnect = dispatch(connectCommunityStream({ onlyMedia }));
    }
  }

  componentWillUnmount () {
    if (this.disconnect) {
      this.disconnect();
      this.disconnect = null;
    }
  }

  setRef = c => {
    this.column = c;
  }

  handleLoadMore = maxId => {
    const { dispatch, onlyMedia } = this.props;

    dispatch(expandCommunityTimeline({ maxId, onlyMedia }));
  }

  render () {
    const { intl, shouldUpdateScroll, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
    const pinned = !!columnId;

    return (
      <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
        <ColumnHeader
          icon='users'
          active={hasUnread}
          title={intl.formatMessage(messages.title)}
          onPin={this.handlePin}
          onMove={this.handleMove}
          onClick={this.handleHeaderClick}
          pinned={pinned}
          multiColumn={multiColumn}
        >
          <ColumnSettingsContainer columnId={columnId} />
        </ColumnHeader>

        <StatusListContainer
          trackScroll={!pinned}
          scrollKey={`community_timeline-${columnId}`}
          timelineId={`community${onlyMedia ? ':media' : ''}`}
          onLoadMore={this.handleLoadMore}
          emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
          shouldUpdateScroll={shouldUpdateScroll}
          bindToDocument={!multiColumn}
        />
      </Column>
    );
  }

}
<|endoftext|>


<commit_before>
function modifiedToday(dateArg)
{
	var fileDate = new Date(dateArg);
	fileDate.setHours(0,0,0,0);
	var now = new Date();
	now.setHours(0,0,0,0);

	return fileDate.toString() === now.toString();
}

function processToken(value, token)
{
	if(token == "+")
	{
		n = Number(value)+1;
		return ""+n;
	}
	if(token == "*")
	{
		return value;
	}
	else
	{
		return token;
	}
}

objArgs = WScript.Arguments;
if(objArgs.length != 2)
{
	WScript.Echo("wrong arguments");
	WScript.Quit(-1);
}

var fileToProcess = objArgs(0);
var mask = objArgs(1);

var WshShell = WScript.CreateObject ("WScript.Shell");

var re = /assembly: (Assembly\w+\(")(\d+)\.(\d+)\.(\d+)\.(\d+)"\)/g
var maskRe = /(\d+|\+|\*)\.(\d+|\+|\*)\.(\d+|\+|\*)\.(\d+|\+|\*)/
var maskMajor;
var maskMinor;
var maskRelease;
var maskBuild;

maskRe.exec(mask);
maskMajor = RegExp.$1;
maskMinor = RegExp.$2;
maskRelease = RegExp.$3;
maskBuild = RegExp.$4;

var fso = WScript.CreateObject("Scripting.FileSystemObject");
var file = fso.GetFile(fileToProcess);

if(modifiedToday(file.DateLastModified))
{
	WScript.Quit(0);
}
file = file.OpenAsTextStream(1,-2);
var content = file.ReadAll();
file.Close();

result = re.exec(content);

var major;
var minor;
var release;
var build;

var newContent;

while (result != null)
{
	 major = processToken(RegExp.$2,maskMajor);
	 minor = processToken(RegExp.$3,maskMinor);
	 release = processToken(RegExp.$4,maskRelease);
	 build = processToken(RegExp.$5,maskBuild);

	 newContent = content.replace(re, "assembly: $1" + major + "." + minor + "." + release + "." + build+"\")");

	 result = re.exec(content);
}

stream = fso.CreateTextFile(fileToProcess, true);
stream.Write(newContent);
stream.Close();
WScript.Echo("Version processed");<commit_msg>Удален неиспользуемый больше скрипт versionInc.js<commit_after><|endoftext|>


<commit_before>window.init_redactor = function(){
  var csrf_token = $('meta[name=csrf-token]').attr('content');
  var csrf_param = $('meta[name=csrf-param]').attr('content');
  var params;
  if (csrf_param !== undefined && csrf_token !== undefined) {
    params = csrf_param + "=" + encodeURIComponent(csrf_token);
  }
  $('.visual_editor').redactor(
    { "imageUpload":"/redactor_images?" + params,
      "path":"/assets/redactor-rails",
      "css":"style.css"}
  );
}

$(document).on( 'ready page:load', window.init_redactor );
<commit_msg>Load plugins by default<commit_after>window.init_redactor = function(){
  var csrf_token = $('meta[name=csrf-token]').attr('content');
  var csrf_param = $('meta[name=csrf-param]').attr('content');
  var params;
  if (csrf_param !== undefined && csrf_token !== undefined) {
    params = csrf_param + "=" + encodeURIComponent(csrf_token);
  }
  $('.visual_editor').redactor(
    { "imageUpload":"/redactor_images?" + params,
      "path":"/assets/redactor-rails",
      "css":"style.css"}
  );
}
        "plugins": [
          'fontsize',
          'fontcolor',
          'fontfamily',
          'fullscreen',
          'clips'
        ],

$(document).on( 'ready page:load', window.init_redactor );
<|endoftext|>


<commit_before>(function(fn) {
  if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading"){
    fn();
  } else {
    document.addEventListener('DOMContentLoaded', fn);
  }
})(function(){

  window.doofinderControllers = {};

  // some utilities
  var $ = doofinder.util.dfdom;
  var bean = doofinder.util.bean;

  function configureStatusHandler(controller, container) {
    controller.on("df:results:success", function(res){
      $(container).html(this.serializeStatus());
    });
  }

  //
  // Basic
  //

  // create a search client
  var basicClient = new doofinder.Client(HASHID, {zone: 'eu1'});
  // create a basic controller, get 20 results per page
  var basicController = new doofinder.Controller(basicClient, {rpp: 20});
  // create an input widget
  var basicInputWidget = new doofinder.widgets.QueryInput("#basicInput");
  // create a results widget
  var basicResultsWidget = new doofinder.widgets.ScrollDisplay("#basicResults");
  // register widgets in the controller
  basicController.registerWidgets([basicInputWidget, basicResultsWidget]);
  configureStatusHandler(basicController, "#basicStatus");

  window.doofinderControllers.basic = basicController;

  //
  // Basic Paged
  //

  // create a search client
  var pagedClient = new doofinder.Client(HASHID, {zone: 'eu1'});
  // create a basic controller, get 20 results per page
  var pagedController = new doofinder.Controller(pagedClient, {rpp: 10});
  // create an input widget
  var pagedInputWidget = new doofinder.widgets.QueryInput("#pagedInput");
  // create a results widget
  var pagedResultsWidget = new doofinder.widgets.Display("#pagedResults");
  // create a pager widget
  var pagedPagerWidget = new doofinder.widgets.Pager("#pagedPager");
  // register widgets in the controller
  pagedController.registerWidgets([pagedInputWidget, pagedResultsWidget, pagedPagerWidget]);
  configureStatusHandler(pagedController, "#pagedStatus");

  window.doofinderControllers.paged = pagedController;

  //
  // Advanced
  //

  // create a search client
  var advancedClient = new doofinder.Client(HASHID, {zone: 'eu1'});
  // create a new controller
  var advancedController = new doofinder.Controller(advancedClient, {rpp: 50});
  configureStatusHandler(advancedController, "#advancedStatus");
  // create an input widget
  var advancedInputWidget = new doofinder.widgets.QueryInput("#advancedInput");
  // create a results widget, results are rendered inside an inner node
  var advancedResultsWidget = new doofinder.widgets.ScrollDisplay("#advancedResults", {
    contentElement: ".df-results__content"
  });
  // register widgets in the controller
  advancedController.registerWidgets([advancedInputWidget, advancedResultsWidget]);
  // get options from doofinder server, to fetch facets configuration
  advancedClient.options(function(err, options){
    options.facets.reverse().forEach(function(facetOptions){
      var widget, node;

      try {
        node = $("#" + facetOptions.name);
      } catch (e) {
        console.log(e);
        return;
      }

      switch(facetOptions.type) {
        case 'terms':
          if (facetOptions.name === 'brand') {
            widget = new doofinder.widgets.TermsFacet(
              node,
              facetOptions.name,
              {
                size: 5,
                startCollapsed: true,
                translations: {
                  "View more…": "More…",
                  "View less…": "Less…"
                }
              }
            );
          } else {
            widget = new doofinder.widgets.TermsFacet(
              node,
              facetOptions.name
            );
          }
          break;
        case 'range':
          // assume there's only one, insert it inside a div
          widget = new doofinder.widgets.RangeFacet(
            node,
            facetOptions.name, {
              format: function(value) {
                return value.toFixed(2) + "€";
              }
            }
          );
          widget.on("df:range:change", function(filter, boundaries){
            console.log("There are products between " + boundaries.min + " and " + boundaries.max + "€");
            console.log("Filtering products between " + filter.start + " and " + filter.end + "€");
          });
          break;
        default:
          console.warn('Invalid facet type: ' + facetOptions.type);
          return;
      }
      advancedController.registerWidget(widget);
    });
  });

  window.doofinderControllers.advanced = advancedController;

  //
  // Multiple Controllers, same input
  //

  // create a search client
  var commonClient = new doofinder.Client(HASHID, {zone: 'eu1'});
  // create an input widget
  var commonInputWidget = new doofinder.widgets.QueryInput("#commonInput");

  // create first controller, get 20 results per page
  var firstController = new doofinder.Controller(commonClient, {rpp: 20});
  configureStatusHandler(firstController, "#firstStatus");
  // create first results widget (horizontal!)
  var firstResultsWidget = new doofinder.widgets.ScrollDisplay("#firstResults", {
    offset: 200,
    horizontal: true
  });
  // register widgets
  firstController.registerWidgets([commonInputWidget, firstResultsWidget]);

  // create second controller, get 20 results per page
  var secondController = new doofinder.Controller(commonClient, {rpp: 20});
  configureStatusHandler(secondController, "#secondStatus");
  // create second results widget
  var secondResultsWidget = new doofinder.widgets.ScrollDisplay("#secondResults", {
    template: document.getElementById('myCustomTemplate').innerHTML,
    templateFunctions: {
      bold: function() {
        return function(text, render) {
          return "" + render(text) + "";
        }
      }
    },
    templateVars: {
      meaningOfLife: 42
    },
    translations: {
      "Hello!": "Hola!"
    }
  });
  // register widgets
  secondController.registerWidgets([commonInputWidget, secondResultsWidget]);

  window.doofinderControllers.multiple = {first: firstController, second: secondController};

  //
  // Multiple Inputs and multiple controllers
  //

  // create a search client
  var multipleClient = new doofinder.Client(HASHID, {zone: 'eu1'});

  // create an input widget
  var multipleInputWidget = new doofinder.widgets.QueryInput([".Multiple", "#multipleInput"]);
  try {
    var secondmultipleInputWidget = new doofinder.widgets.QueryInput("#multipleInput");
  } catch (err) {
    console.warn("You can't attach more than one QueryInput to the same input and this error demonstrates it.");
    console.warn(err);
    var secondmultipleInputWidget = multipleInputWidget;
  }

  // create a controllers, get 20 results per page
  var firstControllerMultiple = new doofinder.Controller(multipleClient, {rpp: 20});
  configureStatusHandler(firstControllerMultiple, "#firstStatusMultiple");
  var secondControllerMultiple = new doofinder.Controller(multipleClient, {rpp: 20, transformer: "onlyid"});
  configureStatusHandler(secondControllerMultiple, "#secondStatusMultiple");

  // create a results widget
  var firstResultsMultiple = new doofinder.widgets.ScrollDisplay("#firstResultsMultiple", {
    offset: 200,
    horizontal: true
  });
  var secondResultsMultiple = new doofinder.widgets.ScrollDisplay("#secondResultsMultiple", {
    template: "{{#results}}
{{id}}
{{/results}}"
  });

  firstControllerMultiple.registerWidgets([multipleInputWidget, firstResultsMultiple]);
  secondControllerMultiple.registerWidgets([secondmultipleInputWidget, secondResultsMultiple]);

  window.doofinderControllers.multipleInputsAndControllers = {first: firstControllerMultiple, second: secondControllerMultiple};
});
<commit_msg>Fixed demo.<commit_after>(function(fn) {
  if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading"){
    fn();
  } else {
    document.addEventListener('DOMContentLoaded', fn);
  }
})(function(){

  window.doofinderControllers = {};

  // some utilities
  var $ = doofinder.util.dfdom;
  var bean = doofinder.util.bean;

  function configureStatusHandler(controller, container) {
    controller.on("df:results:success", function(res){
      $(container).html(this.serializeStatus());
    });
  }

  //
  // Basic
  //

  // create a search client
  var basicClient = new doofinder.Client(HASHID, {zone: 'eu1'});
  // create a basic controller, get 20 results per page
  var basicController = new doofinder.Controller(basicClient, {rpp: 20});
  // create an input widget
  var basicInputWidget = new doofinder.widgets.QueryInput("#basicInput");
  // create a results widget
  var basicResultsWidget = new doofinder.widgets.ScrollDisplay("#basicResults");
  // register widgets in the controller
  basicController.registerWidgets([basicInputWidget, basicResultsWidget]);
  configureStatusHandler(basicController, "#basicStatus");

  window.doofinderControllers.basic = basicController;

  //
  // Basic Paged
  //

  // create a search client
  var pagedClient = new doofinder.Client(HASHID, {zone: 'eu1'});
  // create a basic controller, get 20 results per page
  var pagedController = new doofinder.Controller(pagedClient, {rpp: 10});
  // create an input widget
  var pagedInputWidget = new doofinder.widgets.QueryInput("#pagedInput");
  // create a results widget
  var pagedResultsWidget = new doofinder.widgets.Display("#pagedResults");
  // create a pager widget
  var pagedPagerWidget = new doofinder.widgets.Pager("#pagedPager");
  // register widgets in the controller
  pagedController.registerWidgets([pagedInputWidget, pagedResultsWidget, pagedPagerWidget]);
  configureStatusHandler(pagedController, "#pagedStatus");

  window.doofinderControllers.paged = pagedController;

  //
  // Advanced
  //

  // create a search client
  var advancedClient = new doofinder.Client(HASHID, {zone: 'eu1'});
  // create a new controller
  var advancedController = new doofinder.Controller(advancedClient, {rpp: 50});
  configureStatusHandler(advancedController, "#advancedStatus");
  // create an input widget
  var advancedInputWidget = new doofinder.widgets.QueryInput("#advancedInput");
  // create a results widget, results are rendered inside an inner node
  var advancedResultsWidget = new doofinder.widgets.ScrollDisplay("#advancedResults", {
    contentElement: ".df-results__content"
  });
  // register widgets in the controller
  advancedController.registerWidgets([advancedInputWidget, advancedResultsWidget]);
  // get options from doofinder server, to fetch facets configuration
  advancedClient.options(function(err, options){
    options.facets.reverse().forEach(function(facetOptions){
      var widget, node;

      try {
        node = $("#" + facetOptions.name);
      } catch (e) {
        console.log(e);
        return;
      }

      switch(facetOptions.type) {
        case 'terms':
          if (facetOptions.name === 'brand') {
            widget = new doofinder.widgets.TermsFacet(
              node,
              facetOptions.name,
              {
                size: 5,
                startCollapsed: true,
                translations: {
                  "View more…": "More…",
                  "View less…": "Less…"
                }
              }
            );
          } else {
            widget = new doofinder.widgets.TermsFacet(
              node,
              facetOptions.name
            );
          }
          break;
        case 'range':
          // assume there's only one, insert it inside a div
          widget = new doofinder.widgets.RangeFacet(
            node,
            facetOptions.name, {
              format: function(value) {
                return value.toFixed(2) + "€";
              }
            }
          );
          widget.on("df:range:change", function(filter, boundaries){
            console.log("There are products between " + boundaries.min + " and " + boundaries.max + "€");
            console.log("Filtering products between " + filter.start + " and " + filter.end + "€");
          });
          break;
        default:
          console.warn('Invalid facet type: ' + facetOptions.type);
          return;
      }
      advancedController.registerWidget(widget);
    });
  });

  window.doofinderControllers.advanced = advancedController;

  //
  // Multiple Controllers, same input
  //

  // create a search client
  var commonClient = new doofinder.Client(HASHID, {zone: 'eu1'});
  // create an input widget
  var commonInputWidget = new doofinder.widgets.QueryInput("#commonInput");

  // create first controller, get 20 results per page
  var firstController = new doofinder.Controller(commonClient, {rpp: 20});
  configureStatusHandler(firstController, "#firstStatus");
  // create first results widget (horizontal!)
  var firstResultsWidget = new doofinder.widgets.ScrollDisplay("#firstResults", {
    offset: 200,
    horizontal: true
  });
  // register widgets
  firstController.registerWidgets([commonInputWidget, firstResultsWidget]);

  // create second controller, get 20 results per page
  var secondController = new doofinder.Controller(commonClient, {rpp: 20});
  configureStatusHandler(secondController, "#secondStatus");
  // create second results widget
  var secondResultsWidget = new doofinder.widgets.ScrollDisplay("#secondResults", {
    template: document.getElementById('myCustomTemplate').innerHTML,
    templateFunctions: {
      bold: function() {
        return function(text, render) {
          return "" + render(text) + "";
        }
      }
    },
    templateVars: {
      meaningOfLife: 42
    },
    translations: {
      "Hello!": "Hola!"
    }
  });
  // register widgets
  secondController.registerWidgets([commonInputWidget, secondResultsWidget]);

  window.doofinderControllers.multiple = {first: firstController, second: secondController};

  //
  // Multiple Inputs and multiple controllers
  //

  // create a search client
  var multipleClient = new doofinder.Client(HASHID, {zone: 'eu1'});

  // create an input widget
  var multipleInputWidget = new doofinder.widgets.QueryInput([".Multiple", "#multipleInput"]);

  // create a controllers, get 20 results per page
  var firstControllerMultiple = new doofinder.Controller(multipleClient, {rpp: 20});
  configureStatusHandler(firstControllerMultiple, "#firstStatusMultiple");
  var secondControllerMultiple = new doofinder.Controller(multipleClient, {rpp: 20, transformer: "onlyid"});
  configureStatusHandler(secondControllerMultiple, "#secondStatusMultiple");

  // create a results widget
  var firstResultsMultiple = new doofinder.widgets.ScrollDisplay("#firstResultsMultiple", {
    offset: 200,
    horizontal: true
  });
  var secondResultsMultiple = new doofinder.widgets.ScrollDisplay("#secondResultsMultiple", {
    template: "{{#results}}
{{id}}
{{/results}}"
  });

  firstControllerMultiple.registerWidgets([multipleInputWidget, firstResultsMultiple]);
  secondControllerMultiple.registerWidgets([multipleInputWidget, secondResultsMultiple]);

  window.doofinderControllers.multipleInputsAndControllers = {first: firstControllerMultiple, second: secondControllerMultiple};
});
<|endoftext|>


<commit_before>  (function ($) {

      $.fn.phes15Slider = function(options) {

      return this.each(function () {

          var settings = $.extend({
            img1: "",
            img2: "",
            width: 400,
            height: "",
            direction: "vertical",
            handle: "img/handleH.png"
          }, options);

          var main = $(this).children("div").first();
          var sidebar = $(this).children("div").children("div").first();
          var container = $(this);
          if (settings.handle) {
              $(container).find(".sidebar").append("

");
              var handle = $(container).find(".handle");
          }

          var image = new Image();
          image.src = settings.img1;

          //Currently not used
          var pxToPercent = function (child, parent) {
              console.log("child: " + child + "   |   parent: " + parent);
              return (child / parent) * 100;
          }

          $(image).load(function () { //Image will not load on refresch if not used.

              $(main).css({
                "background-image": "url(" + settings.img1 + ")",
                "width": image.width,
                "height": image.height
              });

              $(sidebar).css({
                 "width": settings.width,
                 "background-image": "url(" + settings.img2 + ")"
              });

              if (settings.handle) {
                  $(handle).css({
                      "background-image": "url(" + settings.handle + ")",
                      "top": (image.height / 2) - $(handle).height() / 2,
                      "left": (image.width / 2) - $(handle).width() / 2
                  });
              }
          });

         if (settings.direction === "vertical") {

             $(main).mousedown(function(e){
                 e.preventDefault(); //Ignores text on the slider
                 $(document).mousemove(function(e){
                     $(sidebar).css("width", e.pageX - $(sidebar).offset().left); //calculates the width by subtracting the pos of e when mouse is down with the left offset of the sidebar.
                     if (settings.handle) {
                        $(handle).css("left", parseFloat($(sidebar).css("width")) - $(handle).width() / 2); //making the handle follow the slider
                     }
                 });
             });

         } else {

             $(image).load(function () {
                 $(sidebar).css({
                    "width": image.width,
                    "height": settings.height,
                    "borderRight": "none",
                    "borderBottom": "2px solid #fff",
                    "minHeight": "0%",
                    "maxHeight": "100%"
                 });
             });

             $(main).mousedown(function(e){
                 e.preventDefault(); //Ignores text on the slider
                 $(document).mousemove(function(e){
                     $(sidebar).css("height", e.pageY - $(sidebar).offset().top); //calculates the width by subtracting the pos of e when mouse is down with the left offset of the sidebar.
                     if (settings.handle) {
                         $(handle).css("top", parseFloat($(sidebar).css("height")) - $(handle).height() / 2);
                     }
                 });
             });
         }

         $(document).mouseup(function(e){
             $(document).unbind('mousemove');
         });

     });

    };

  } (jQuery));
<commit_msg>Changed name from phes15Slider to phesSlider<commit_after>  (function ($) {

      $.fn.phesSlider = function(options) {

      return this.each(function () {

          var settings = $.extend({
            img1: "",
            img2: "",
            width: 400,
            height: "",
            direction: "vertical",
            handle: "img/handleH.png"
          }, options);

          var main = $(this).children("div").first();
          var sidebar = $(this).children("div").children("div").first();
          var container = $(this);
          if (settings.handle) {
              $(container).find(".sidebar").append("

");
              var handle = $(container).find(".handle");
          }

          var image = new Image();
          image.src = settings.img1;

          //Currently not used
          var pxToPercent = function (child, parent) {
              console.log("child: " + child + "   |   parent: " + parent);
              return (child / parent) * 100;
          }

          $(image).load(function () { //Image will not load on refresch if not used.

              $(main).css({
                "background-image": "url(" + settings.img1 + ")",
                "width": image.width,
                "height": image.height
              });

              $(sidebar).css({
                 "width": settings.width,
                 "background-image": "url(" + settings.img2 + ")"
              });

              if (settings.handle) {
                  $(handle).css({
                      "background-image": "url(" + settings.handle + ")",
                      "top": (image.height / 2) - $(handle).height() / 2,
                      "left": (image.width / 2) - $(handle).width() / 2
                  });
              }
          });

         if (settings.direction === "vertical") {

             $(main).mousedown(function(e){
                 e.preventDefault(); //Ignores text on the slider
                 $(document).mousemove(function(e){
                     $(sidebar).css("width", e.pageX - $(sidebar).offset().left); //calculates the width by subtracting the pos of e when mouse is down with the left offset of the sidebar.
                     if (settings.handle) {
                        $(handle).css("left", parseFloat($(sidebar).css("width")) - $(handle).width() / 2); //making the handle follow the slider
                     }
                 });
             });

         } else {

             $(image).load(function () {
                 $(sidebar).css({
                    "width": image.width,
                    "height": settings.height,
                    "borderRight": "none",
                    "borderBottom": "2px solid #fff",
                    "minHeight": "0%",
                    "maxHeight": "100%"
                 });
             });

             $(main).mousedown(function(e){
                 e.preventDefault(); //Ignores text on the slider
                 $(document).mousemove(function(e){
                     $(sidebar).css("height", e.pageY - $(sidebar).offset().top); //calculates the width by subtracting the pos of e when mouse is down with the left offset of the sidebar.
                     if (settings.handle) {
                         $(handle).css("top", parseFloat($(sidebar).css("height")) - $(handle).height() / 2);
                     }
                 });
             });
         }

         $(document).mouseup(function(e){
             $(document).unbind('mousemove');
         });

     });

    };

  } (jQuery));
<|endoftext|>


<commit_before>
var VerticalStackedBarChartView = BaseChartView.extend({
  initialize : function() {
    this.initialize_defaults();
  },

  render : function() {
    this.clear_container();
    InitializeVerticalStackedBar(this.model.get("container"),
      this.results(),
      this.ticks(),
      this.filler(),
      this.model.get('show_point_label'),
      this.parsed_unit(),
      this.model.colors(),
      this.model.labels());
  },

  results : function() {
    var results = this.results_without_targets();
    var smallest_scale = null;

    if (!this.model.get('percentage')) {
      var smallest_value = _.min(this.model.values());
      smallest_scale = Metric.scaled_scale(smallest_value, 3);

      results = _.map(results, function(x) {
        return _.map(x, function(value) {
          // DEBT DEBT DEBT UGLY UGLY UGLY rewrite Metric.js!!!
          if (value < 1 && smallest_scale === 0) return value;
          return Metric.scaled_value(value, 3, smallest_scale);
        });
      });
    }

    _.each(this.model.target_series(), function(serie) {
      var result = serie.result()[0][1]; // target_series has only present or future value
      if (smallest_scale)
        result = Metric.scaled_value(result, 3, smallest_scale);
      var x = parseFloat(serie.get('target_line_position'));
      results.push([[x - 0.4, result], [x + 0.4, result]]);
    });
    return results;
  },

  results_without_targets : function() {
    return _.map(this.model.non_target_series(), function(serie) { return serie.result_pairs(); });
  },
  filler : function() {
    return _.map(this.model.non_target_series(), function(serie) { return {}; });
  },
  ticks : function() {
    return [App.settings.get("start_year"), App.settings.get("end_year")];
  }
});
<commit_msg>stacked bar charts: removed scaling<commit_after>
var VerticalStackedBarChartView = BaseChartView.extend({
  initialize : function() {
    this.initialize_defaults();
  },

  render : function() {
    this.clear_container();
    InitializeVerticalStackedBar(this.model.get("container"),
      this.results(),
      this.ticks(),
      this.filler(),
      this.model.get('show_point_label'),
      this.parsed_unit(),
      this.model.colors(),
      this.model.labels());
  },

  results : function() {
    var results = this.results_without_targets();
    var smallest_scale = null;

    if (!this.model.get('percentage')) {
      results = _.map(results, function(x) {
        return _.map(x, function(value) {
          return value;
        });
      });
    }

    _.each(this.model.target_series(), function(serie) {
      var result = serie.result()[0][1]; // target_series has only present or future value
      var x = parseFloat(serie.get('target_line_position'));
      results.push([[x - 0.4, result], [x + 0.4, result]]);
    });
    return results;
  },

  results_without_targets : function() {
    return _.map(this.model.non_target_series(), function(serie) { return serie.result_pairs(); });
  },
  filler : function() {
    return _.map(this.model.non_target_series(), function(serie) { return {}; });
  },
  ticks : function() {
    return [App.settings.get("start_year"), App.settings.get("end_year")];
  }
});
<|endoftext|>


<commit_before>import Piece from './piece';

export default class Board {
  constructor() {
    this.setBoard(this.initialUsiBoard());
  }

  initialUsiBoard() {
    return (
      [
        ['l', 'n', 's', 'g', 'k', 'g', 's', 'n', 'l'],
        ['*', 'b', '*', '*', '*', '*', '*', 'r', '*'],
        ['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
        ['*', '*', '*', '*', '*', '*', '*', '*', '*'],
        ['*', '*', '*', '*', '*', '*', '*', '*', '*'],
        ['*', '*', '*', '*', '*', '*', '*', '*', '*'],
        ['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
        ['*', 'B', '*', '*', '*', '*', '*', 'R', '*'],
        ['L', 'N', 'S', 'G', 'K', 'G', 'S', 'N', 'L']
      ]
    );
  }

  setBoard(board) {
    this.board = this.createBoard(board);
  }

  createBoard(board) {
    var _board = board.map((row, y) => {
      var yCor = y + 1;
      var rows = row.map((type, x) => {
        var xCor = 10 - x - 1;
        return (
          new Piece({ type: type, x: xCor, y: yCor })
        );
      });
      return(rows);
    });

    return _board;
  }

  enhanceMovablePoint(piece) {
    var [xCor, yCor] = [piece.x, piece.y];
    var moveDef = piece.moveDef();
    var [x, y] = this.invertCor(xCor, yCor);

    // if piece of argument is not match piece in the board, throw exception
    //
    if (! this.matchPiece(piece)) {
      var pos = `xCor = ${xCor}, yCor = ${yCor}`;
      throw new Error(`Does not match coordinates in board. ${pos}`);
    }

    // if moveDef has just property, piece moves just coordinates on board.
    //
    if (moveDef.just) {
      this.movablePointsByJust(piece);
    }

    // if moveDef has fly property, piece moves recursion on board.
    //
    if (moveDef.fly) {
      this.movablePointsByFly(piece);
    }
  }

  // if piece of argument is match in the board, return true, else return false
  //
  matchPiece(piece) {
    var [xCor, yCor] = [piece.x, piece.y];
    var [x, y] = this.invertCor(xCor, yCor);

    var isUndefined = typeof this.findPiece(x, y) === 'undefined';
    var isEqualPiece = this.findPiece(x, y).equals(
      new Piece({
        x: piece.x,
        y: piece.y,
        type: piece.type,
        movable: piece.movable
      })
    );

    return (isUndefined || isEqualPiece);
  }

  // if moveDef has just property, piece moves just coordinates on board.
  //
  movablePointsByJust(piece) {
    var [xCor, yCor] = [piece.x, piece.y];
    var moveDef = piece.moveDef();
    var [x, y] = this.invertCor(xCor, yCor);

    moveDef.just.forEach((def) => {
      var [defX, defY] = def;
      var piece = this.findPiece(x + defX, y + defY);
      if (piece) { piece.movable = true; }
    });
  }

  movablePointsByFly(piece) {
    var [xCor, yCor] = [piece.x, piece.y];
    var moveDef = piece.moveDef();
    var [x, y] = this.invertCor(xCor, yCor);

    moveDef.fly.forEach((def) => {
      var [defX, defY] = def;
      var dx, dy;

      dx = x + defX;
      dy = y + defY;

      var piece = this.findPiece(dx, dy);

      while (piece) {
        piece.movable = true;
        dx = dx + defX;
        dy = dy + defY;

        piece = this.findPiece(dx, dy);

        if (piece && piece.type !== '*') {
          piece.movable = true;
          break;
        }
      }
    });
  }

  invertToIndexX(xCor) {
    return 9 - xCor;
  }

  invertToIndexY(yCor) {
    return yCor - 1;
  }

  invertCor(xCor, yCor) {
    return [this.invertToIndexX(xCor), this.invertToIndexY(yCor)];
  }

  findPiece(xIndex, yIndex) {
    var row = this.board[yIndex];
    return row ? row[xIndex] : undefined;
  }

  // transposeToCorX(xIndex) {
  // }

  // transposeToCorY(xIndex) {
  // }
};
<commit_msg>Remove unnecessary variables<commit_after>import Piece from './piece';

export default class Board {
  constructor() {
    this.setBoard(this.initialUsiBoard());
  }

  initialUsiBoard() {
    return (
      [
        ['l', 'n', 's', 'g', 'k', 'g', 's', 'n', 'l'],
        ['*', 'b', '*', '*', '*', '*', '*', 'r', '*'],
        ['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
        ['*', '*', '*', '*', '*', '*', '*', '*', '*'],
        ['*', '*', '*', '*', '*', '*', '*', '*', '*'],
        ['*', '*', '*', '*', '*', '*', '*', '*', '*'],
        ['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
        ['*', 'B', '*', '*', '*', '*', '*', 'R', '*'],
        ['L', 'N', 'S', 'G', 'K', 'G', 'S', 'N', 'L']
      ]
    );
  }

  setBoard(board) {
    this.board = this.createBoard(board);
  }

  createBoard(board) {
    var _board = board.map((row, y) => {
      var yCor = y + 1;
      var rows = row.map((type, x) => {
        var xCor = 10 - x - 1;
        return (
          new Piece({ type: type, x: xCor, y: yCor })
        );
      });
      return(rows);
    });

    return _board;
  }

  enhanceMovablePoint(piece) {
    var [xCor, yCor] = [piece.x, piece.y];
    var moveDef = piece.moveDef();

    // if piece of argument is not match piece in the board, throw exception
    //
    if (! this.matchPiece(piece)) {
      var pos = `xCor = ${xCor}, yCor = ${yCor}`;
      throw new Error(`Does not match coordinates in board. ${pos}`);
    }

    // if moveDef has just property, piece moves just coordinates on board.
    //
    if (moveDef.just) {
      this.movablePointsByJust(piece);
    }

    // if moveDef has fly property, piece moves recursion on board.
    //
    if (moveDef.fly) {
      this.movablePointsByFly(piece);
    }
  }

  // if piece of argument is match in the board, return true, else return false
  //
  matchPiece(piece) {
    var [xCor, yCor] = [piece.x, piece.y];
    var [x, y] = this.invertCor(xCor, yCor);

    var isUndefined = typeof this.findPiece(x, y) === 'undefined';
    var isEqualPiece = this.findPiece(x, y).equals(
      new Piece({
        x: piece.x,
        y: piece.y,
        type: piece.type,
        movable: piece.movable
      })
    );

    return (isUndefined || isEqualPiece);
  }

  // if moveDef has just property, piece moves just coordinates on board.
  //
  movablePointsByJust(piece) {
    var [xCor, yCor] = [piece.x, piece.y];
    var moveDef = piece.moveDef();
    var [x, y] = this.invertCor(xCor, yCor);

    moveDef.just.forEach((def) => {
      var [defX, defY] = def;
      var piece = this.findPiece(x + defX, y + defY);
      if (piece) { piece.movable = true; }
    });
  }

  movablePointsByFly(piece) {
    var [xCor, yCor] = [piece.x, piece.y];
    var moveDef = piece.moveDef();
    var [x, y] = this.invertCor(xCor, yCor);

    moveDef.fly.forEach((def) => {
      var [defX, defY] = def;
      var dx, dy;

      dx = x + defX;
      dy = y + defY;

      var piece = this.findPiece(dx, dy);

      while (piece) {
        piece.movable = true;
        dx = dx + defX;
        dy = dy + defY;

        piece = this.findPiece(dx, dy);

        if (piece && piece.type !== '*') {
          piece.movable = true;
          break;
        }
      }
    });
  }

  invertToIndexX(xCor) {
    return 9 - xCor;
  }

  invertToIndexY(yCor) {
    return yCor - 1;
  }

  invertCor(xCor, yCor) {
    return [this.invertToIndexX(xCor), this.invertToIndexY(yCor)];
  }

  findPiece(xIndex, yIndex) {
    var row = this.board[yIndex];
    return row ? row[xIndex] : undefined;
  }

  // transposeToCorX(xIndex) {
  // }

  // transposeToCorY(xIndex) {
  // }
};
<|endoftext|>


<commit_before>'use strict';

// https://www.hackerrank.com/contests/projecteuler/challenges/euler014

// FIXME: this code gives "wrong answer" for test case #4 and above

const start = new Date();
const max = 5 * Math.pow(10, 6);
let collatz = {1: 1};
for (let i = 2; i <= max; i++)
    collatz[i] = null;

function main() {
    for (let i = 2; i <= max; i++) {
        let chain = [], j = 0;
        for (var steps = 1, n = i; n > 1; steps++) {
            chain[j++] = n;
            if (collatz[n] !== null) {
                steps += collatz[n] - 1;
                break;
            } else {
                if (n % 2 === 0) n /= 2;
                else n = 3 * n + 1;
            }
        }
        for (j = 0; j < chain.length; j++) {
            if (chain[j] <= max && collatz[chain[j]] === null) {
                collatz[chain[j]] = steps - j;
            }
        }
    }
    input.split('\n').slice(1).map(Number).forEach(processLine);
}

function processLine(lineN) {
    for (var maxN, maxStep = -Infinity; lineN > 0; lineN--) {
        if (collatz[lineN] > maxStep) {
            maxStep = collatz[lineN];
            maxN = lineN;
        }
    }
    process.stdout.write(maxN + '\n');
}

process.stdin.resume();
process.stdin.setEncoding('ascii');

let input = "";
process.stdin.on('data', function (data) {
    input += data;
});

process.stdin.on('end', main);

if (process.argv[2] === 'test') {
    process.stdin.pause();
    input = `
    5
    10
    15
    20
    1
    ${max}
    `.replace(/^\s+/mg, "").trim();
    process.stdout.write(`Input:\n${input}\n\nOutput:\n`);
    main();
    const end = new Date();
    process.stdout.write((end - start) / 1000 + ' seconds\n');
}
<commit_msg>finally I got it right<commit_after>'use strict';

// https://www.hackerrank.com/contests/projecteuler/challenges/euler014
// tip: this is all about improving performance without sacrificing memory

const start = new Date();

// I don't know why, but some test cases failed when max = 5e6
const max = 7e6;

function main() {
    computeLongestSteps();
    input.split('\n').slice(1).map(Number).forEach(processLine);
}

const longestStep = [0, 1];
let maxElement = 1, length = 2;

function computeLongestSteps() {
    for (let i = 2; i <= max; i++) {
        let s = steps(i);
        if (s >= maxElement) {
            maxElement = s;
            longestStep[length++] = i;
        }
    }
}

function processLine(n) {
    for (var i = 0; longestStep[i] <= n; i++);
    process.stdout.write(longestStep[i - 1] + '\n');
}

const stepsCache = [0, 1];

function steps(n) {
    if (n === 1) return 1;
    let chain = [], length = 0;
    for (var i = 0, j = n; j > 1; i++) {
        chain[length++] = j;
        if (stepsCache[j]) {
            i += stepsCache[j];
            break;
        } else {
            j = next(j);
        }
    }
    for (j = 0; j < length; j++) {
        if (chain[j] <= max && ! stepsCache[chain[j]]) {
            stepsCache[chain[j]] = i - j;
        }
    }
    return stepsCache[n];
}

function next(n) {
    return (n % 2 === 0) ? (n / 2) : (3 * n + 1);
}

process.stdin.resume();
process.stdin.setEncoding('ascii');

let input = "";
process.stdin.on('data', function (data) {
    input += data;
});

process.stdin.on('end', main);

if (process.argv[2] === 'test') {
    process.stdin.pause();
    input = `
    8
    0
    10
    15
    20
    3
    4
    1
    ${max}
    `.replace(/^\s+/mg, "").trim();
    process.stderr.write(`Input:\n${input}\n\nOutput:\n`);
    main();
    const end = new Date();
    process.stderr.write((end - start) / 1000 + ' seconds\n');
}
<|endoftext|>


<commit_before>/*globals Panel, Input, google, Query, DataSelector*/
'use strict'

var Plot = {
	types: {
		area: {
			className: 'AreaChart',
			allowStacked: true,
			focusTarget: 'category'
		},
		bar: {
			className: 'BarChart',
			allowStacked: true,
			focusTarget: 'category'
		},
		column: {
			className: 'ColumnChart',
			allowStacked: true,
			focusTarget: 'category'
		},
		line: {
			className: 'LineChart',
			allowStacked: false,
			focusTarget: 'category'
		},
		histogram: {
			className: 'Histogram',
			allowStacked: false,
			focusTarget: undefined
		}
	},
	/**
	 * Whether google charts has loaded
	 * @property {boolean}
	 */
	ready: false
}

/**
 * @typedef {Object} Plot~Series
 * @property {HTMLElement} el
 * @property {DataSelector} dataSelector
 * @property {HTMLElement} addEl
 * @property {HTMLElement} deleteEl
 */

/**
 * Bind DOM events
 */
Plot.init = function () {
	Panel.get('plot').onclick = Plot.start
	Panel.get('plot-clear').onclick = Plot.stop
	Panel.get('plot-type').onchange = Plot.update
	Panel.get('plot-stacked').onchange = Plot.update
	Plot.titleInput = new Input('plot-title')
	Plot.titleInput.oninput = Plot.update
	Plot.xAxis = new DataSelector('plot-x-axis')
	Plot.xAxis.onchange = Plot.update
	Plot.series = []
	Panel.get('plot-add-series').onclick = function () {
		Plot.addSeries(0)
	}
	Plot.addSeries(0, true)

	// Load google charts
	google.charts.load('current', {
		packages: ['corechart']
	})
	google.charts.setOnLoadCallback(function () {
		Plot.ready = true
	})
}

window.addEventListener('load', Plot.init)

/**
 * Show plot options
 */
Plot.start = function () {
	Panel.get('plot').style.display = 'none'
	Panel.get('plot-options').style.display = ''
}

/**
 * Hide plot options
 */
Plot.stop = function () {
	Panel.get('plot').style.display = ''
	Panel.get('plot-options').style.display = 'none'
}

/**
 * Add a new series at the given position
 * @param {number} [pos=-1] - default: at the end (0=first)
 * @param {boolean} [skipSelection] - if true, does not start field selection
 * @returns {Plot~Series}
 */
Plot.addSeries = function (pos, skipSelection) {
	var series = {
		dataSelector: new DataSelector(Panel.create('span')),
		addEl: Panel.create('span.add'),
		deleteEl: Panel.create('span.delete')
	}

	// Create series
	series.el = Panel.create('li', [
		series.dataSelector.el,
		' ',
		series.addEl,
		' ',
		series.deleteEl
	])

	series.addEl.onclick = function () {
		Plot.addSeries(Plot.series.indexOf(series) + 1)
	}
	series.deleteEl.onclick = function () {
		Plot.deleteSeries(series)
	}
	series.dataSelector.onchange = Plot.update

	if (pos === -1 || pos === undefined || pos === Plot.series.length) {
		Panel.get('plot-series').appendChild(series.el)
		Plot.series.push(series)
	} else {
		Panel.get('plot-series').insertBefore(series.el, Plot.series[pos].el)
		Plot.series.splice(pos, 0, series)
	}

	Plot.update()
	if (!skipSelection) {
		setTimeout(function () {
			series.dataSelector.selectField()
		}, 0)
	}

	return series
}

/**
 * @param {Plot~Series} series
 */
Plot.deleteSeries = function (series) {
	var pos = Plot.series.indexOf(series)
	if (pos === -1) {
		return
	}
	Plot.series.splice(pos, 1)
	series.el.parentElement.removeChild(series.el)
	Plot.update()
}

/**
 * Update the current plot and the options interface
 */
Plot.update = function () {
	var type = Panel.value('plot-type'),
		plotType = Plot.types[type],
		numSeries = Plot.series.filter(function (series) {
			return series.dataSelector.getField()
		}).length
	Panel.get('plot-stacked').style.display = plotType.allowStacked ? '' : 'none'

	if (type === 'histogram' && numSeries === 1) {
		Panel.get('plot-x-axis-label').textContent = 'Data name'
		Plot.xAxis.setInputDisabled(true)
	} else {
		Panel.get('plot-x-axis-label').textContent = 'X axis'
		Plot.xAxis.setInputDisabled(false)
	}
	Plot.xAxis.setFieldDisabled(type === 'histogram' && numSeries > 1)

	Plot.updatePlot()
}

/**
 * Update the current plot
 */
Plot.updatePlot = function () {
	// Load current plot options
	var xField = Plot.xAxis.getField().split('.').filter(Boolean),
		xName = Plot.xAxis.getName(),
		type = Panel.value('plot-type'),
		plotType = Plot.types[type],
		series = Plot.series.map(function (series) {
			return {
				field: series.dataSelector.getField().split('.').filter(Boolean),
				name: series.dataSelector.getName()
			}
		}).filter(function (series) {
			// Ignore empty ones
			return series.field.length
		}),
		hasX = type !== 'histogram' || series.length < 2,
		xOffset = hasX ? 1 : 0

	if ((hasX && !xField.length) ||
		!series.length ||
		!Plot.ready ||
		!Query.result ||
		!Query.result.length) {
		// Not enough data
		return
	}

	// Create data table header
	var nCols = xOffset + series.length,
		nRows = Query.result.length + 1,
		header = new Array(nCols)
	if (hasX) {
		header[0] = xName
	}
	series.forEach(function (each, i) {
		header[i + xOffset] = each.name
	})

	// Load table data
	var body = new Array(nRows - 1),
		i, row, doc, xValue, j, yValue
	for (i = 1; i < nRows; i++) {
		row = new Array(nCols)
		doc = Query.result[i - 1]

		// X value
		if (hasX) {
			xValue = readField(doc, xField)
			if (typeof xValue !== 'string' &&
				typeof xValue !== 'number' &&
				!(xValue instanceof Date)) {
				// Only strings, numbers and dates are allowed in the x axis
				xValue = String(xValue)
			} else if (type === 'histogram') {
				// For one-series histogram, x must be a string
				xValue = String(xValue)
			}
			row[0] = xValue
		}

		// Series
		for (j = xOffset; j < nCols; j++) {
			yValue = readField(doc, series[j - xOffset].field)
			if (typeof yValue !== 'number') {
				// Only numbers are allowed as series data
				yValue = null
			}
			row[j] = yValue
		}

		body[i - 1] = row
	}

	// Sort by x value
	if (hasX) {
		if (typeof body[0][0] === 'string') {
			// String comparison
			body.sort(function (a, b) {
				var a0 = a[0],
					b0 = b[0]
				return a0 === b0 ? 0 : (a0 > b0 ? 1 : -1)
			})
		} else {
			// Numeric comparison
			body.sort(function (a, b) {
				var a0 = +a[0],
					b0 = +b[0]
				return a0 - b0
			})
		}
	}

	// Create final table
	var table = new Array(nRows)
	table[0] = header
	for (i = 1; i < nRows; i++) {
		table[i] = body[i - 1]
	}

	// Draw plot
	var dataTable = google.visualization.arrayToDataTable(table),
		PlotClass = google.visualization[plotType.className],
		hAxisTitle = xName,
		legend, isStacked

	if (type === 'histogram' && series.length === 1) {
		legend = {
			position: 'none'
		}
		hAxisTitle = series[0].name
	}

	var chart = new PlotClass(Panel.get('plot-canvas'))
	chart.draw(dataTable, {
		title: Plot.titleInput.value,
		focusTarget: plotType.focusTarget,
		hAxis: {
			title: hAxisTitle
		},
		isStacked: isStacked,
		legend: legend
	})

	/**
	 * Extract a field from the document
	 */
	function readField(doc, field) {
		var result = doc,
			i, len

		for (i = 0, len = field.length; i < len; i++) {
			if (!result || typeof result !== 'object') {
				break
			}
			result = result[field[i]]
		}

		return result
	}
}<commit_msg>Respect strings labels order<commit_after>/*globals Panel, Input, google, Query, DataSelector*/
'use strict'

var Plot = {
	types: {
		area: {
			className: 'AreaChart',
			allowStacked: true,
			focusTarget: 'category'
		},
		bar: {
			className: 'BarChart',
			allowStacked: true,
			focusTarget: 'category'
		},
		column: {
			className: 'ColumnChart',
			allowStacked: true,
			focusTarget: 'category'
		},
		line: {
			className: 'LineChart',
			allowStacked: false,
			focusTarget: 'category'
		},
		histogram: {
			className: 'Histogram',
			allowStacked: false,
			focusTarget: undefined
		}
	},
	/**
	 * Whether google charts has loaded
	 * @property {boolean}
	 */
	ready: false
}

/**
 * @typedef {Object} Plot~Series
 * @property {HTMLElement} el
 * @property {DataSelector} dataSelector
 * @property {HTMLElement} addEl
 * @property {HTMLElement} deleteEl
 */

/**
 * Bind DOM events
 */
Plot.init = function () {
	Panel.get('plot').onclick = Plot.start
	Panel.get('plot-clear').onclick = Plot.stop
	Panel.get('plot-type').onchange = Plot.update
	Panel.get('plot-stacked').onchange = Plot.update
	Plot.titleInput = new Input('plot-title')
	Plot.titleInput.oninput = Plot.update
	Plot.xAxis = new DataSelector('plot-x-axis')
	Plot.xAxis.onchange = Plot.update
	Plot.series = []
	Panel.get('plot-add-series').onclick = function () {
		Plot.addSeries(0)
	}
	Plot.addSeries(0, true)

	// Load google charts
	google.charts.load('current', {
		packages: ['corechart']
	})
	google.charts.setOnLoadCallback(function () {
		Plot.ready = true
	})
}

window.addEventListener('load', Plot.init)

/**
 * Show plot options
 */
Plot.start = function () {
	Panel.get('plot').style.display = 'none'
	Panel.get('plot-options').style.display = ''
}

/**
 * Hide plot options
 */
Plot.stop = function () {
	Panel.get('plot').style.display = ''
	Panel.get('plot-options').style.display = 'none'
}

/**
 * Add a new series at the given position
 * @param {number} [pos=-1] - default: at the end (0=first)
 * @param {boolean} [skipSelection] - if true, does not start field selection
 * @returns {Plot~Series}
 */
Plot.addSeries = function (pos, skipSelection) {
	var series = {
		dataSelector: new DataSelector(Panel.create('span')),
		addEl: Panel.create('span.add'),
		deleteEl: Panel.create('span.delete')
	}

	// Create series
	series.el = Panel.create('li', [
		series.dataSelector.el,
		' ',
		series.addEl,
		' ',
		series.deleteEl
	])

	series.addEl.onclick = function () {
		Plot.addSeries(Plot.series.indexOf(series) + 1)
	}
	series.deleteEl.onclick = function () {
		Plot.deleteSeries(series)
	}
	series.dataSelector.onchange = Plot.update

	if (pos === -1 || pos === undefined || pos === Plot.series.length) {
		Panel.get('plot-series').appendChild(series.el)
		Plot.series.push(series)
	} else {
		Panel.get('plot-series').insertBefore(series.el, Plot.series[pos].el)
		Plot.series.splice(pos, 0, series)
	}

	Plot.update()
	if (!skipSelection) {
		setTimeout(function () {
			series.dataSelector.selectField()
		}, 0)
	}

	return series
}

/**
 * @param {Plot~Series} series
 */
Plot.deleteSeries = function (series) {
	var pos = Plot.series.indexOf(series)
	if (pos === -1) {
		return
	}
	Plot.series.splice(pos, 1)
	series.el.parentElement.removeChild(series.el)
	Plot.update()
}

/**
 * Update the current plot and the options interface
 */
Plot.update = function () {
	var type = Panel.value('plot-type'),
		plotType = Plot.types[type],
		numSeries = Plot.series.filter(function (series) {
			return series.dataSelector.getField()
		}).length
	Panel.get('plot-stacked').style.display = plotType.allowStacked ? '' : 'none'

	if (type === 'histogram' && numSeries === 1) {
		Panel.get('plot-x-axis-label').textContent = 'Data name'
		Plot.xAxis.setInputDisabled(true)
	} else {
		Panel.get('plot-x-axis-label').textContent = 'X axis'
		Plot.xAxis.setInputDisabled(false)
	}
	Plot.xAxis.setFieldDisabled(type === 'histogram' && numSeries > 1)

	Plot.updatePlot()
}

/**
 * Update the current plot
 */
Plot.updatePlot = function () {
	// Load current plot options
	var xField = Plot.xAxis.getField().split('.').filter(Boolean),
		xName = Plot.xAxis.getName(),
		type = Panel.value('plot-type'),
		plotType = Plot.types[type],
		series = Plot.series.map(function (series) {
			return {
				field: series.dataSelector.getField().split('.').filter(Boolean),
				name: series.dataSelector.getName()
			}
		}).filter(function (series) {
			// Ignore empty ones
			return series.field.length
		}),
		hasX = type !== 'histogram' || series.length < 2,
		xOffset = hasX ? 1 : 0

	if ((hasX && !xField.length) ||
		!series.length ||
		!Plot.ready ||
		!Query.result ||
		!Query.result.length) {
		// Not enough data
		return
	}

	// Create data table header
	var nCols = xOffset + series.length,
		nRows = Query.result.length + 1,
		header = new Array(nCols)
	if (hasX) {
		header[0] = xName
	}
	series.forEach(function (each, i) {
		header[i + xOffset] = each.name
	})

	// Load table data
	var body = new Array(nRows - 1),
		i, row, doc, xValue, j, yValue
	for (i = 1; i < nRows; i++) {
		row = new Array(nCols)
		doc = Query.result[i - 1]

		// X value
		if (hasX) {
			xValue = readField(doc, xField)
			if (typeof xValue !== 'string' &&
				typeof xValue !== 'number' &&
				!(xValue instanceof Date)) {
				// Only strings, numbers and dates are allowed in the x axis
				xValue = String(xValue)
			} else if (type === 'histogram') {
				// For one-series histogram, x must be a string
				xValue = String(xValue)
			}
			row[0] = xValue
		}

		// Series
		for (j = xOffset; j < nCols; j++) {
			yValue = readField(doc, series[j - xOffset].field)
			if (typeof yValue !== 'number') {
				// Only numbers are allowed as series data
				yValue = null
			}
			row[j] = yValue
		}

		body[i - 1] = row
	}

	if (hasX && typeof body[0][0] !== 'string') {
		// Sort by x numeric value
		// This avoid google charts visual glitches with non-monotonic x values
		body.sort(function (a, b) {
			var a0 = +a[0],
				b0 = +b[0]
			return a0 - b0
		})
	}

	// Create final table
	var table = new Array(nRows)
	table[0] = header
	for (i = 1; i < nRows; i++) {
		table[i] = body[i - 1]
	}

	// Draw plot
	var dataTable = google.visualization.arrayToDataTable(table),
		PlotClass = google.visualization[plotType.className],
		hAxisTitle = xName,
		legend, isStacked

	if (type === 'histogram' && series.length === 1) {
		legend = {
			position: 'none'
		}
		hAxisTitle = series[0].name
	}

	var chart = new PlotClass(Panel.get('plot-canvas'))
	chart.draw(dataTable, {
		title: Plot.titleInput.value,
		focusTarget: plotType.focusTarget,
		hAxis: {
			title: hAxisTitle
		},
		isStacked: isStacked,
		legend: legend
	})

	/**
	 * Extract a field from the document
	 */
	function readField(doc, field) {
		var result = doc,
			i, len

		for (i = 0, len = field.length; i < len; i++) {
			if (!result || typeof result !== 'object') {
				break
			}
			result = result[field[i]]
		}

		return result
	}
}<|endoftext|>


<commit_before>alert("Playing!");

document.getElementById("up").onclick = function() {move(38);};
document.getElementById("down").onclick = function() {move(40);};
document.getElementById("right").onclick = function() {move(39);};
document.getElementById("left").onclick = function() {move(37);};

var puppyImg = document.getElementById("puppy");
var baconImg = document.getElementById("bacon");
var boneImg1 = document.getElementById("bone1");
var boneImg2 = document.getElementById("bone2");

var puppy = {
  posX: 490,
  posY: 650,
  hunger: 10,
  success: false,
  canMove: true
};

var bacon = {
  posX: 190,
  posY: 50
};

var bone1 = {
  posX: 400,
  posY: 250
};

var bone2 = {
  posX: 200,
  posY: 550
};

function move(direction){
  if (puppy.canMove){
    console.log('moving');
    pup_move(bacon, baconImg);
    pup_move(bone1, boneImg1);
    pup_move(bone2, boneImg2);
    if (direction.keyCode === 38 || direction === 38 || direction.keyCode === 73){ // move up
      if (puppy.posY > 0){
        puppy.posY = (puppy.posY - 40);
        puppyImg.style.top = (puppy.posY + "px");
      }
    }else if (direction.keyCode === 40 || direction === 40 || direction.keyCode === 75){ // move down
      if (puppy.posY < 440){
        puppy.posY = (starfish.y + 40);
        puppyImg.style.top = (puppy.posY + "px");
      }
    }else if (direction.keyCode === 39 || direction == 39 || direction.keyCode === 76){ // move right
      if (puppy.posX < 600){
        puppy.posX = (starfish.x + 40);
        puppyImg.style.left = (puppy.posX + "px");
      }

    }else if (direction.keyCode === 37 || direction == 37 || direction.keyCode === 74){ // move left
      if (puppy.posX > 0){
        puppy.posX = (puppy.posX - 40);
        puppyImg.style.left = (puppy.posX + "px");
      }
    }
    status(bacon, baconImg);
    status(bone1, boneImg1);
    status(bone2, boneImg2);
    setTimeout(function(){
      puppy.canMove = true;
    }, 200)
    puppy.canMove = false;
  }
}

function pup_move(player, playerImg){
  var random = Math.floor((Math.random() * 4) + 1);
  // move up
  if (random === 1){
    if (player.y > 0){
      player.y = (player.y - 40);
      playerImg.style.top = (player.y + "px");
    }
  }else if (random === 2){
    // move down
    if (player.y < 440){
      player.y = (player.y + 40);
      playerImg.style.top = (player.y + "px");
    }
  }else if (random === 3){
    // move right
    if (player.x < 600){
      player.x = (player.x + 40);
      playerImg.style.left = (player.x + "px");
    }
 }else{
    //move left
    if (player.x > 0){
      player.x = (player.x - 40);
      playerImg.style.left = (player.x + "px");
    }
 }
}

document.onkeydown = move;<commit_msg>Make puppy move<commit_after>alert("Playing!");

var puppy = {
  posX: 490,
  posY: 650,
  hunger: 10,
  success: false,
  canMove: true
};

var bacon = {
  posX: 190,
  posY: 50
};

var bone1 = {
  posX: 400,
  posY: 250
};

var bone2 = {
  posX: 200,
  posY: 550
};

document.getElementById("up").onclick = function() {move(38);};
document.getElementById("down").onclick = function() {move(40);};
document.getElementById("right").onclick = function() {move(39);};
document.getElementById("left").onclick = function() {move(37);};

var puppyImg = document.getElementById("puppy");
var baconImg = document.getElementById("bacon");
var boneImg1 = document.getElementById("bone1");
var boneImg2 = document.getElementById("bone2");

function move(direction){
  if (puppy.canMove){
    console.log('moving');
    pup_move(bacon, baconImg);
    pup_move(bone1, boneImg1);
    pup_move(bone2, boneImg2);
    if (direction.keyCode === 38 || direction === 38 || direction.keyCode === 73){ // move up
      if (puppy.posY > 0){
        puppy.posY = (puppy.posY - 40);
        puppyImg.style.top = (puppy.posY + "px");
      }
    }else if (direction.keyCode === 40 || direction === 40 || direction.keyCode === 75){ // move down
      if (puppy.posY < 440){
        puppy.posY = (starfish.y + 40);
        puppyImg.style.top = (puppy.posY + "px");
      }
    }else if (direction.keyCode === 39 || direction == 39 || direction.keyCode === 76){ // move right
      if (puppy.posX < 600){
        puppy.posX = (starfish.x + 40);
        puppyImg.style.left = (puppy.posX + "px");
      }

    }else if (direction.keyCode === 37 || direction == 37 || direction.keyCode === 74){ // move left
      if (puppy.posX > 0){
        puppy.posX = (puppy.posX - 40);
        puppyImg.style.left = (puppy.posX + "px");
      }
    }
    status(bacon, baconImg);
    status(bone1, boneImg1);
    status(bone2, boneImg2);
    setTimeout(function(){
      puppy.canMove = true;
    }, 200)
    puppy.canMove = false;
  }
}

function pup_move(player, playerImg){
  var random = Math.floor((Math.random() * 4) + 1);
  // move up
  if (random === 1){
    if (player.y > 0){
      player.y = (player.y - 40);
      playerImg.style.top = (player.y + "px");
    }
  }else if (random === 2){
    // move down
    if (player.y < 440){
      player.y = (player.y + 40);
      playerImg.style.top = (player.y + "px");
    }
  }else if (random === 3){
    // move right
    if (player.x < 600){
      player.x = (player.x + 40);
      playerImg.style.left = (player.x + "px");
    }
 }else{
    //move left
    if (player.x > 0){
      player.x = (player.x - 40);
      playerImg.style.left = (player.x + "px");
    }
 }
}

document.onkeydown = move;<|endoftext|>


<commit_before>import Ember from 'ember';

const { RSVP, isEmpty, run, computed } = Ember;

import config from '../config/environment';
import OAuth2PasswordGrantAuthenticator from 'ember-simple-auth/authenticators/oauth2-password-grant';

export default OAuth2PasswordGrantAuthenticator.extend({

    /** Use refresh access token. */
    refreshAccessTokens: true,

    /** Endpoint for authentication. */
    serverTokenEndpoint: `${config.AUTHENTICATION_HOST}/auth/realms/dina/tokens/grants/access`,

    /** Endpoint for refreshing token. */
    serverRefreshTokenEndpoint: `${config.AUTHENTICATION_HOST}/auth/realms/dina/protocol/openid-connect/token`,

    /** Set client id. */
    clientId: 'dina-rest-endpoint',

    /** Override to add client id to data. */
    makeRequest(url, data) {
        const clientId = this.get('clientId');

        if (!Ember.isEmpty(clientId)) {
            data.client_id = clientId;
        }

        return this._super(...arguments);
    },

    /** Override to be able to have different endpoint when refreshing a token. */
    _refreshAccessToken(expiresIn, refreshToken) {
        const data = { 'grant_type': 'refresh_token', 'refresh_token': refreshToken };

        /* Override starts. */
        const serverTokenEndpoint = this.get('serverRefreshTokenEndpoint');
        /* Override ends. */

        return new RSVP.Promise((resolve, reject) => {
          this.makeRequest(serverTokenEndpoint, data).then((response) => {
            run(() => {
              expiresIn = response['expires_in'] || expiresIn;
              refreshToken = response['refresh_token'] || refreshToken;
              const expiresAt = this._absolutizeExpirationTime(expiresIn);
              const data = Ember.merge(response, { 'expires_in': expiresIn, 'expires_at': expiresAt, 'refresh_token': refreshToken });
              this._scheduleAccessTokenRefresh(expiresIn, null, refreshToken);
              this.trigger('sessionDataUpdated', data);
              resolve(data);
            });
          }, (xhr, status, error) => {
            Ember.Logger.warn(`Access token could not be refreshed - server responded with ${error}.`);
            reject();
          });
        });
    },

});
<commit_msg>Fix tests<commit_after>import Ember from 'ember';

const { RSVP, run } = Ember;

import config from '../config/environment';
import OAuth2PasswordGrantAuthenticator from 'ember-simple-auth/authenticators/oauth2-password-grant';

export default OAuth2PasswordGrantAuthenticator.extend({

    /** Use refresh access token. */
    refreshAccessTokens: true,

    /** Endpoint for authentication. */
    serverTokenEndpoint: `${config.AUTHENTICATION_HOST}/auth/realms/dina/tokens/grants/access`,

    /** Endpoint for refreshing token. */
    serverRefreshTokenEndpoint: `${config.AUTHENTICATION_HOST}/auth/realms/dina/protocol/openid-connect/token`,

    /** Set client id. */
    clientId: 'dina-rest-endpoint',

    /** Override to add client id to data. */
    makeRequest(url, data) {
        const clientId = this.get('clientId');

        if (!Ember.isEmpty(clientId)) {
            data.client_id = clientId;
        }

        return this._super(...arguments);
    },

    /** Override to be able to have different endpoint when refreshing a token. */
    _refreshAccessToken(expiresIn, refreshToken) {
        const data = { 'grant_type': 'refresh_token', 'refresh_token': refreshToken };

        /* Override starts. */
        const serverTokenEndpoint = this.get('serverRefreshTokenEndpoint');
        /* Override ends. */

        return new RSVP.Promise((resolve, reject) => {
          this.makeRequest(serverTokenEndpoint, data).then((response) => {
            run(() => {
              expiresIn = response['expires_in'] || expiresIn;
              refreshToken = response['refresh_token'] || refreshToken;
              const expiresAt = this._absolutizeExpirationTime(expiresIn);
              const data = Ember.merge(response, { 'expires_in': expiresIn, 'expires_at': expiresAt, 'refresh_token': refreshToken });
              this._scheduleAccessTokenRefresh(expiresIn, null, refreshToken);
              this.trigger('sessionDataUpdated', data);
              resolve(data);
            });
          }, (xhr, status, error) => {
            Ember.Logger.warn(`Access token could not be refreshed - server responded with ${error}.`);
            reject();
          });
        });
    },

});
<|endoftext|>