<commit_before>import _ from 'underscore';

import { Base64 } from 'meteor/base64';
import { ReactiveVar } from 'meteor/reactive-var';
import { EJSON } from 'meteor/ejson';
import { Random } from 'meteor/random';
import { TimeSync } from 'meteor/mizzao:timesync';

import { RocketChat, call } from 'meteor/rocketchat:lib';
import { e2e } from './rocketchat.e2e';
import {
	Deferred,
	toString,
	toArrayBuffer,
	joinVectorAndEcryptedData,
	splitVectorAndEcryptedData,
	encryptRSA,
	encryptAES,
	decryptRSA,
	decryptAES,
	generateAESKey,
	exportJWKKey,
	importAESKey,
	importRSAKey,
	readFileAsArrayBuffer,
} from './helper';

export class E2ERoom {
	constructor(userId, roomId, t) {
		this.userId = userId;
		this.roomId = roomId;
		this.typeOfRoom = t;
		this.establishing = new ReactiveVar(false);

		this._ready = new ReactiveVar(false);
		this.readyPromise = new Deferred();
		this.readyPromise.then(() => {
			this._ready.set(true);
			this.establishing.set(false);

			RocketChat.Notifications.onRoom(this.roomId, 'e2ekeyRequest', async(keyId) => {
				this.provideKeyToUser(keyId);
			});
		});
	}

	// Initiates E2E Encryption
	async handshake() {
		if (!e2e.isReady()) {
			return;
		}

		if (this._ready.get()) {
			return;
		}

		if (this.establishing.get()) {
			return await this.readyPromise;
		}

		console.log('E2E -> Initiating handshake');

		this.establishing.set(true);

		// Cover private groups and direct messages
		if (!this.isSupportedRoomType(this.typeOfRoom)) {
			return;
		}

		// Fetch encrypted session key from subscription model
		let groupKey;
		try {
			groupKey = RocketChat.models.Subscriptions.findOne({ rid: this.roomId }).E2EKey;
		} catch (error) {
			return console.error('E2E -> Error fetching group key: ', error);
		}

		if (groupKey) {
			await this.importGroupKey(groupKey);
			this.readyPromise.resolve();
			return true;
		}

		const room = RocketChat.models.Rooms.findOne({ _id: this.roomId });

		if (!room.e2eKeyId) {
			await this.createGroupKey();
			this.readyPromise.resolve();
			return true;
		}

		console.log('E2E -> Requesting room key');
		// TODO: request group key

		RocketChat.Notifications.notifyUsersOfRoom(this.roomId, 'e2ekeyRequest', this.roomId, room.e2eKeyId);
	}

	isSupportedRoomType(type) {
		return ['d', 'p'].includes(type);
	}

	async importGroupKey(groupKey) {
		console.log('E2E -> Importing room key');
		// Get existing group key
		// const keyID = groupKey.slice(0, 12);
		groupKey = groupKey.slice(12);
		groupKey = Base64.decode(groupKey);

		// Decrypt obtained encrypted session key
		try {
			const decryptedKey = await decryptRSA(e2e.privateKey, groupKey);
			this.sessionKeyExportedString = toString(decryptedKey);
		} catch (error) {
			return console.error('E2E -> Error decrypting group key: ', error);
		}

		this.keyID = Base64.encode(this.sessionKeyExportedString).slice(0, 12);

		// Import session key for use.
		try {
			const key = await importAESKey(JSON.parse(this.sessionKeyExportedString));
			// Key has been obtained. E2E is now in session.
			this.groupSessionKey = key;
		} catch (error) {
			return console.error('E2E -> Error importing group key: ', error);
		}
	}

	async createGroupKey() {
		console.log('E2E -> Creating room key');
		// Create group key
		let key;
		try {
			key = await generateAESKey();
			this.groupSessionKey = key;
		} catch (error) {
			return console.error('E2E -> Error generating group key: ', error);
		}

		let sessionKeyExported;
		try {
			sessionKeyExported = await exportJWKKey(this.groupSessionKey);
		} catch (error) {
			return console.error('E2E -> Error exporting group key: ', error);
		}

		this.sessionKeyExportedString = JSON.stringify(sessionKeyExported);
		this.keyID = Base64.encode(this.sessionKeyExportedString).slice(0, 12);

		await call('e2e.setRoomKeyID', this.roomId, this.keyID);

		await this.encryptKeyForOtherParticipants();
	}

	async encryptKeyForOtherParticipants() {
		// Encrypt generated session key for every user in room and publish to subscription model.
		let users;
		try {
			users = await call('e2e.getUsersOfRoomWithoutKey', this.roomId);
		} catch (error) {
			return console.error('E2E -> Error getting room users: ', error);
		}

		users.users.forEach((user) => this.encryptForParticipand(user));
	}

	async encryptForParticipand(user) {
		if (user.e2e.public_key) {
			let userKey;
			try {
				userKey = await importRSAKey(JSON.parse(user.e2e.public_key), ['encrypt']);
			} catch (error) {
				return console.error('E2E -> Error importing user key: ', error);
			}
			// const vector = crypto.getRandomValues(new Uint8Array(16));

			// Encrypt session key for this user with his/her public key
			let encryptedUserKey;
			try {
				encryptedUserKey = await encryptRSA(userKey, toArrayBuffer(this.sessionKeyExportedString));
			} catch (error) {
				return console.error('E2E -> Error encrypting user key: ', error);
			}

			// Key has been encrypted. Publish to that user's subscription model for this room.
			await call('e2e.updateGroupKey', this.roomId, user._id, this.keyID + Base64.encode(new Uint8Array(encryptedUserKey)));
		}
	}

	// Encrypts files before upload. I/O is in arraybuffers.
	async encryptFile(file) {
		if (!this.isSupportedRoomType(this.typeOfRoom)) {
			return;
		}

		const fileArrayBuffer = await readFileAsArrayBuffer(file);

		const vector = crypto.getRandomValues(new Uint8Array(16));
		let result;
		try {
			result = await encryptAES(vector, this.groupSessionKey, fileArrayBuffer);
		} catch (error) {
			return console.error('E2E -> Error encrypting group key: ', error);
		}

		const output = joinVectorAndEcryptedData(vector, result);

		const encryptedFile = new File([toArrayBuffer(EJSON.stringify(output))], file.name);

		return encryptedFile;
	}

	// Decrypt uploaded encrypted files. I/O is in arraybuffers.
	async decryptFile(message) {
		if (message[0] !== '{') {
			return;
		}

		const [vector, cipherText] = splitVectorAndEcryptedData(EJSON.parse(message));

		try {
			return await decryptAES(vector, this.groupSessionKey, cipherText);
		} catch (error) {
			console.error('E2E -> Error decrypting file: ', error);

			return false;
		}
	}

	// Encrypts messages
	async encryptText(data) {
		if (!_.isObject(data)) {
			data = new TextEncoder('UTF-8').encode(EJSON.stringify({ text: data, ack: Random.id((Random.fraction() + 1) * 20) }));
		}

		if (!this.isSupportedRoomType(this.typeOfRoom)) {
			return data;
		}

		const vector = crypto.getRandomValues(new Uint8Array(16));
		let result;
		try {
			result = await encryptAES(vector, this.groupSessionKey, data);
		} catch (error) {
			return console.error('E2E -> Error encrypting message: ', error);
		}

		return this.keyID + Base64.encode(joinVectorAndEcryptedData(vector, result));
	}

	// Helper function for encryption of messages
	encrypt(message) {
		let ts;
		if (isNaN(TimeSync.serverOffset())) {
			ts = new Date();
		} else {
			ts = new Date(Date.now() + TimeSync.serverOffset());
		}

		const data = new TextEncoder('UTF-8').encode(EJSON.stringify({
			_id: message._id,
			text: message.msg,
			userId: this.userId,
			ts,
		}));
		const enc = this.encryptText(data);
		return enc;
	}

	// Decrypt messages
	async decrypt(message) {
		if (!this.isSupportedRoomType(this.typeOfRoom)) {
			return message;
		}

		const keyID = message.slice(0, 12);

		if (keyID !== this.keyID) {
			return message;
		}

		message = message.slice(12);

		const [vector, cipherText] = splitVectorAndEcryptedData(Base64.decode(message));

		try {
			const result = await decryptAES(vector, this.groupSessionKey, cipherText);
			return EJSON.parse(toString(result));
		} catch (error) {
			return console.error('E2E -> Error decrypting message: ', error, message);
		}
	}

	provideKeyToUser(keyId) {
		if (this.keyID !== keyId) {
			return;
		}

		this.encryptKeyForOtherParticipants();
	}
}
<commit_msg>[FIX] E2E: Decrypting UTF-8 encoded messages (#12398)<commit_after>import _ from 'underscore';

import { Base64 } from 'meteor/base64';
import { ReactiveVar } from 'meteor/reactive-var';
import { EJSON } from 'meteor/ejson';
import { Random } from 'meteor/random';
import { TimeSync } from 'meteor/mizzao:timesync';

import { RocketChat, call } from 'meteor/rocketchat:lib';
import { e2e } from './rocketchat.e2e';
import {
	Deferred,
	toString,
	toArrayBuffer,
	joinVectorAndEcryptedData,
	splitVectorAndEcryptedData,
	encryptRSA,
	encryptAES,
	decryptRSA,
	decryptAES,
	generateAESKey,
	exportJWKKey,
	importAESKey,
	importRSAKey,
	readFileAsArrayBuffer,
} from './helper';

export class E2ERoom {
	constructor(userId, roomId, t) {
		this.userId = userId;
		this.roomId = roomId;
		this.typeOfRoom = t;
		this.establishing = new ReactiveVar(false);

		this._ready = new ReactiveVar(false);
		this.readyPromise = new Deferred();
		this.readyPromise.then(() => {
			this._ready.set(true);
			this.establishing.set(false);

			RocketChat.Notifications.onRoom(this.roomId, 'e2ekeyRequest', async(keyId) => {
				this.provideKeyToUser(keyId);
			});
		});
	}

	// Initiates E2E Encryption
	async handshake() {
		if (!e2e.isReady()) {
			return;
		}

		if (this._ready.get()) {
			return;
		}

		if (this.establishing.get()) {
			return await this.readyPromise;
		}

		console.log('E2E -> Initiating handshake');

		this.establishing.set(true);

		// Cover private groups and direct messages
		if (!this.isSupportedRoomType(this.typeOfRoom)) {
			return;
		}

		// Fetch encrypted session key from subscription model
		let groupKey;
		try {
			groupKey = RocketChat.models.Subscriptions.findOne({ rid: this.roomId }).E2EKey;
		} catch (error) {
			return console.error('E2E -> Error fetching group key: ', error);
		}

		if (groupKey) {
			await this.importGroupKey(groupKey);
			this.readyPromise.resolve();
			return true;
		}

		const room = RocketChat.models.Rooms.findOne({ _id: this.roomId });

		if (!room.e2eKeyId) {
			await this.createGroupKey();
			this.readyPromise.resolve();
			return true;
		}

		console.log('E2E -> Requesting room key');
		// TODO: request group key

		RocketChat.Notifications.notifyUsersOfRoom(this.roomId, 'e2ekeyRequest', this.roomId, room.e2eKeyId);
	}

	isSupportedRoomType(type) {
		return ['d', 'p'].includes(type);
	}

	async importGroupKey(groupKey) {
		console.log('E2E -> Importing room key');
		// Get existing group key
		// const keyID = groupKey.slice(0, 12);
		groupKey = groupKey.slice(12);
		groupKey = Base64.decode(groupKey);

		// Decrypt obtained encrypted session key
		try {
			const decryptedKey = await decryptRSA(e2e.privateKey, groupKey);
			this.sessionKeyExportedString = toString(decryptedKey);
		} catch (error) {
			return console.error('E2E -> Error decrypting group key: ', error);
		}

		this.keyID = Base64.encode(this.sessionKeyExportedString).slice(0, 12);

		// Import session key for use.
		try {
			const key = await importAESKey(JSON.parse(this.sessionKeyExportedString));
			// Key has been obtained. E2E is now in session.
			this.groupSessionKey = key;
		} catch (error) {
			return console.error('E2E -> Error importing group key: ', error);
		}
	}

	async createGroupKey() {
		console.log('E2E -> Creating room key');
		// Create group key
		let key;
		try {
			key = await generateAESKey();
			this.groupSessionKey = key;
		} catch (error) {
			return console.error('E2E -> Error generating group key: ', error);
		}

		let sessionKeyExported;
		try {
			sessionKeyExported = await exportJWKKey(this.groupSessionKey);
		} catch (error) {
			return console.error('E2E -> Error exporting group key: ', error);
		}

		this.sessionKeyExportedString = JSON.stringify(sessionKeyExported);
		this.keyID = Base64.encode(this.sessionKeyExportedString).slice(0, 12);

		await call('e2e.setRoomKeyID', this.roomId, this.keyID);

		await this.encryptKeyForOtherParticipants();
	}

	async encryptKeyForOtherParticipants() {
		// Encrypt generated session key for every user in room and publish to subscription model.
		let users;
		try {
			users = await call('e2e.getUsersOfRoomWithoutKey', this.roomId);
		} catch (error) {
			return console.error('E2E -> Error getting room users: ', error);
		}

		users.users.forEach((user) => this.encryptForParticipand(user));
	}

	async encryptForParticipand(user) {
		if (user.e2e.public_key) {
			let userKey;
			try {
				userKey = await importRSAKey(JSON.parse(user.e2e.public_key), ['encrypt']);
			} catch (error) {
				return console.error('E2E -> Error importing user key: ', error);
			}
			// const vector = crypto.getRandomValues(new Uint8Array(16));

			// Encrypt session key for this user with his/her public key
			let encryptedUserKey;
			try {
				encryptedUserKey = await encryptRSA(userKey, toArrayBuffer(this.sessionKeyExportedString));
			} catch (error) {
				return console.error('E2E -> Error encrypting user key: ', error);
			}

			// Key has been encrypted. Publish to that user's subscription model for this room.
			await call('e2e.updateGroupKey', this.roomId, user._id, this.keyID + Base64.encode(new Uint8Array(encryptedUserKey)));
		}
	}

	// Encrypts files before upload. I/O is in arraybuffers.
	async encryptFile(file) {
		if (!this.isSupportedRoomType(this.typeOfRoom)) {
			return;
		}

		const fileArrayBuffer = await readFileAsArrayBuffer(file);

		const vector = crypto.getRandomValues(new Uint8Array(16));
		let result;
		try {
			result = await encryptAES(vector, this.groupSessionKey, fileArrayBuffer);
		} catch (error) {
			return console.error('E2E -> Error encrypting group key: ', error);
		}

		const output = joinVectorAndEcryptedData(vector, result);

		const encryptedFile = new File([toArrayBuffer(EJSON.stringify(output))], file.name);

		return encryptedFile;
	}

	// Decrypt uploaded encrypted files. I/O is in arraybuffers.
	async decryptFile(message) {
		if (message[0] !== '{') {
			return;
		}

		const [vector, cipherText] = splitVectorAndEcryptedData(EJSON.parse(message));

		try {
			return await decryptAES(vector, this.groupSessionKey, cipherText);
		} catch (error) {
			console.error('E2E -> Error decrypting file: ', error);

			return false;
		}
	}

	// Encrypts messages
	async encryptText(data) {
		if (!_.isObject(data)) {
			data = new TextEncoder('UTF-8').encode(EJSON.stringify({ text: data, ack: Random.id((Random.fraction() + 1) * 20) }));
		}

		if (!this.isSupportedRoomType(this.typeOfRoom)) {
			return data;
		}

		const vector = crypto.getRandomValues(new Uint8Array(16));
		let result;
		try {
			result = await encryptAES(vector, this.groupSessionKey, data);
		} catch (error) {
			return console.error('E2E -> Error encrypting message: ', error);
		}

		return this.keyID + Base64.encode(joinVectorAndEcryptedData(vector, result));
	}

	// Helper function for encryption of messages
	encrypt(message) {
		let ts;
		if (isNaN(TimeSync.serverOffset())) {
			ts = new Date();
		} else {
			ts = new Date(Date.now() + TimeSync.serverOffset());
		}

		const data = new TextEncoder('UTF-8').encode(EJSON.stringify({
			_id: message._id,
			text: message.msg,
			userId: this.userId,
			ts,
		}));
		const enc = this.encryptText(data);
		return enc;
	}

	// Decrypt messages
	async decrypt(message) {
		if (!this.isSupportedRoomType(this.typeOfRoom)) {
			return message;
		}

		const keyID = message.slice(0, 12);

		if (keyID !== this.keyID) {
			return message;
		}

		message = message.slice(12);

		const [vector, cipherText] = splitVectorAndEcryptedData(Base64.decode(message));

		try {
			const result = await decryptAES(vector, this.groupSessionKey, cipherText);
			return EJSON.parse(new TextDecoder('UTF-8').decode(new Uint8Array(result)));
		} catch (error) {
			return console.error('E2E -> Error decrypting message: ', error, message);
		}
	}

	provideKeyToUser(keyId) {
		if (this.keyID !== keyId) {
			return;
		}

		this.encryptKeyForOtherParticipants();
	}
}
<|endoftext|>


<commit_before>console.log("Overlay loading...");

Node = function( paper, parent, x, y, r )
{
	var ox = 0, oy = 0;

	var c = paper.circle( x, y, r ).attr({
					fill: "hsb(.8, 1, 1)",
					stroke: "none",
					opacity: .5
					});
	this.getC = function(){ return c; }

	var mc = paper.circle( x, y, r + 8 ).attr({
					fill: "rgb(0, 1, 0)",
					stroke: "none",
					opacity: 0
					});

	var children = new Array();
	this.getChildren = function(){ return children; }

	this.parent = parent;
	var l;
	if ( parent != null )
		l = paper.path();

	var drawLine = function()
	{
		l.attr( {path: [ [ "M", c.attrs.cx, c.attrs.cy ], [ "L", parent.getC().attrs.cx, parent.getC().attrs.cy ] ] } );
		l.toBack();
	}

	this.drawLine = drawLine;

	mc.move = function( dx, dy )
	{
		var x = ox + dx;
    	var y = oy + dy;
		c.attr({cx: x,cy: y});
		mc.attr({cx: x,cy: y});
		for ( var i = 0; i < children.length; ++i )
			children[ i ].drawLine();
		drawLine();
	}
	mc.up = function()
	{
		c.attr({opacity:.5});
	}
	mc.start = function()
	{
		ox = mc.attr("cx");
		oy = mc.attr("cy");
		c.attr({opacity:1});
	}
	mc.drag( mc.move, mc.start, mc.up );
}

SVGOverlay = function(
		resolution,			//!< object {x, y, z} resolution of the parent DOM element in nanometer/pixel
		translation
)
{

  this.createdata = function()
  {
    // storing original coordinates

    var ln = null;

    var x = r.width / 2;
    var y = r.height / 2;

    for (var i = 0; i < 100; ++i)
    {
      x = Math.min( r.width, Math.max( 0, x + ( .5 - Math.random() ) * 100 ) );
      y = Math.min( r.height, Math.max( 0, y + ( .5 - Math.random() ) * 100 ) );
      ln = new Node( r, ln, x, y, 8 );
    }

    while ( ln.parent != null )
    {
      ln.parent.getChildren().push( ln );
      ln.drawLine();
      ln = ln.parent;
    }
  }

	this.update = function(width, height)
	{
	  view.style.width = width + "px";
    view.style.height = height + "px";
		r.setSize(width, height);
	}

  this.getView = function()
  {
    return view;
  }

	self = this;
	self.resolution = resolution;
	self.translation = translation;

  var view = document.createElement( "div" );
  view.className = "sliceSVGOverlay";
  view.style.zIndex = 4;

	var r = Raphael(view, 1000, 400);

};<commit_msg>Updated overlay.js<commit_after>console.log("Overlay loading...");

Node = function( paper, parent, x, y, r )
{
	var ox = 0, oy = 0;

	var c = paper.circle( x, y, r ).attr({
					fill: "hsb(.8, 1, 1)",
					stroke: "none",
					opacity: .5
					});
	this.getC = function(){ return c; }

	var mc = paper.circle( x, y, r + 8 ).attr({
					fill: "rgb(0, 1, 0)",
					stroke: "none",
					opacity: 0
					});

	var children = new Array();
	this.getChildren = function(){ return children; }

	this.parent = parent;
	var l;
	if ( parent != null )
		l = paper.path();

	var drawLine = function()
	{
		l.attr( {path: [ [ "M", c.attrs.cx, c.attrs.cy ], [ "L", parent.getC().attrs.cx, parent.getC().attrs.cy ] ] } );
		l.toBack();
	}

	this.drawLine = drawLine;

	mc.move = function( dx, dy )
	{
		var x = ox + dx;
    	var y = oy + dy;
		c.attr({cx: x,cy: y});
		mc.attr({cx: x,cy: y});
		for ( var i = 0; i < children.length; ++i )
			children[ i ].drawLine();
		drawLine();
	}
	mc.up = function()
	{
		c.attr({opacity:.5});
	}
	mc.start = function()
	{
		ox = mc.attr("cx");
		oy = mc.attr("cy");
		c.attr({opacity:1});
	}
	mc.drag( mc.move, mc.start, mc.up );
}

SVGOverlay = function(
		resolution,			//!< object {x, y, z} resolution of the parent DOM element in nanometer/pixel
		translation
)
{

  this.createdata = function()
  {
    // storing original coordinates

    var ln = null;

    var x = r.width / 2;
    var y = r.height / 2;

    for (var i = 0; i < 100; ++i)
    {
      x = Math.min( r.width, Math.max( 0, x + ( .5 - Math.random() ) * 100 ) );
      y = Math.min( r.height, Math.max( 0, y + ( .5 - Math.random() ) * 100 ) );
      ln = new Node( r, ln, x, y, 8 );
    }

    while ( ln.parent != null )
    {
      ln.parent.getChildren().push( ln );
      ln.drawLine();
      ln = ln.parent;
    }
  }

	this.update = function(width, height)
	{
	  view.style.width = width + "px";
    view.style.height = height + "px";
		r.setSize(width, height);
	}

  this.getView = function()
  {
    return view;
  }

  var onclick = function( e )
  {
    var m = ui.getMouse( e );
    console.log("clicked", m);
    return false;
  }

	self = this;
	self.resolution = resolution;
	self.translation = translation;

  var view = document.createElement( "div" );
  view.className = "sliceSVGOverlay";
  view.onclick = onclick;
  view.style.zIndex = 4;

	var r = Raphael(view, 1000, 400);

};<|endoftext|>


<commit_before>var jacks = (function() {
	var plugins = [];
	var exports = {};
	var serializers = {
		"application/json": function(data) {
			return JSON.stringify(data);
		},
		"application/x-www-form-urlencoded": function(data) {

		}
	}

	function JacksRequest(requestType, url) {
		var headers = {};
		var data = null;
		var query = null;
		var type = null;

		/**
		 * Ajout d'un header. Si le nom du header est null on renvoie les headers.
		 */
		this.header = function(name, value) {
			if (name == null) {
				return headers;
			}
			headers[name] = value;
			return this;
		};

		/**
		 * Définitition du type de donnée
		 * Si le parametre est null on renvoie le type
		 */
		this.type = function(value) {
			if (value == null) {
				return type;
			}
			type = value;
			return this;
		};

		/**
		 * Ajoute un data
		 * Si le nom est null on renvoie les data
		 */
		this.data = function(name, value) {
			if (name == null) {
				return data;
			}
			data = data || {};
			if (typeof name === "string") {
				// On envoie un seul parametre
				data[name] = value;
			} else if (typeof name === "object") {
				// On en envoie plusieures sous forme d'objet
				for (var attr in name) {
					data[name[attr]] = name[value];
				}
			}
			return this;
		};

		/**
		 * Ajoute un paramètre en queryString
		 * Si le nom est null on renvoie les données de queryString courantes
		 */
		this.query = function(name, value) {
			if (name == null) {
				return query;
			}
			query = query || {};
			if (typeof name === "string") {
				// On envoie un seul parametre
				query[name] = value;
			} else if (typeof name === "object") {
				// On en envoie plusieures sous forme d'objet
				for (var attr in name) {
					query[name[attr]] = name[value];
				}
			}
			return this;
		};

		/**
		 * Exécute un plugin
		 */
		this.plugin = function(pluginFn) {
			pluginFn.apply(this);
			return this;
		}

		/**
		 * envoie de la requete
		 */
		this.send = function(callback, error) {
			for (var i = 0 ; i < plugins.length ; i++) {
				plugin[i].apply(this);
			}
			var xhr = new XMLHttpRequest();
			xhr.onload = callback;
			xhr.onerror = error;
			if (requestType === "GET")
			xhr.open(requestType, url);
			for (var header in headers) {
				xhr.setRequestHeader(header, headers[header]);
			}
			xhr.send(serialize(data));
		}
	}

	exports.get = function(url) {
		var jacksRequest = new JacksRequest("GET", url);
		return jacksRequest;
	}
	exports.post = function(url) {
		var jacksRequest = new JacksRequest("POST", url);
		return jacksRequest;
	}
	exports.put = function(url) {
		var jacksRequest = new JacksRequest("PUT", url);
		return jacksRequest;
	}
	exports["delete"] = function(url) {
		var jacksRequest = new JacksRequest("DELETE", url);
		return jacksRequest;
	}
	exports.plugin = function(pluginObject) {
		plugins.push(pluginObject);
		return this;
	}
	return exports;
})();<commit_msg>Suppr Jacks.js<commit_after><|endoftext|>


<commit_before>// ==========================================================================
// Project:   SproutCore Handlebar Views
// Copyright: ©2011 Strobe Inc. and contributors.
// License:   Licensed under MIT license (see license.js)
// ==========================================================================
/*globals Handlebars */

require('sproutcore-handlebars/ext');
require('sproutcore-handlebars/views/bindable_span');

var get = SC.get, getPath = SC.getPath;

(function() {
  // Binds a property into the DOM. This will create a hook in DOM that the
  // KVO system will look for and upate if the property changes.
  var bind = function(property, options, preserveContext, shouldDisplay) {
    var data = options.data,
        fn = options.fn,
        inverse = options.inverse,
        view = data.view,
        ctx  = this;

    // Set up observers for observable objects
    if ('object' === typeof this) {
      // Create the view that will wrap the output of this template/property
      // and add it to the nearest view's childViews array.
      // See the documentation of SC._BindableSpanView for more.
      var bindView = view.createChildView(SC._BindableSpanView, {
        preserveContext: preserveContext,
        shouldDisplayFunc: shouldDisplay,
        displayTemplate: fn,
        inverseTemplate: inverse,
        property: property,
        previousContext: ctx,
        isEscaped: options.hash.escaped
      });

      var observer, invoker;

      get(view, 'childViews').pushObject(bindView);

      observer = function() {
        if (get(bindView, 'element')) {
          bindView.rerender();
        } else {
          // If no layer can be found, we can assume somewhere
          // above it has been re-rendered, so remove the
          // observer.
          SC.removeObserver(ctx, property, invoker);
        }
      };

      invoker = function() {
        SC.run.once(observer);
      };

      // Observes the given property on the context and
      // tells the SC._BindableSpan to re-render.
      SC.addObserver(ctx, property, invoker);

      var buffer = bindView.renderToBuffer();
      return new Handlebars.SafeString(buffer.string());
    } else {
      // The object is not observable, so just render it out and
      // be done with it.
      return getPath(this, property);
    }
  };

  /**
    `bind` can be used to display a value, then update that value if it
    changes. For example, if you wanted to print the `title` property of
    `content`:

        {{bind "content.title"}}

    This will return the `title` property as a string, then create a new
    observer at the specified path. If it changes, it will update the value in
    DOM. Note that if you need to support IE7 and IE8 you must modify the
    model objects properties using SC.get() and SC.set() for this to work as
    it relies on SC's KVO system.  For all other browsers this will be handled
    for you automatically.

    @name Handlebars.helpers.bind
    @param {String} property Property to bind
    @param {Function} fn Context to provide for rendering
    @returns {String} HTML string
  */
  Handlebars.registerHelper('bind', function(property, fn) {
    return bind.call(this, property, fn, false, function(result) {
      return !SC.none(result);
    });
  });

  /**
    Use the `boundIf` helper to create a conditional that re-evaluates
    whenever the bound value changes.

        {{#boundIf "content.shouldDisplayTitle"}}
          {{content.title}}
        {{/boundIf}}

    @name Handlebars.helpers.boundIf
    @param {String} property Property to bind
    @param {Function} fn Context to provide for rendering
    @returns {String} HTML string
  */
  Handlebars.registerHelper('boundIf', function(property, fn) {
    if(fn) {
      return bind.call(this, property, fn, true, function(result) {
        if (SC.typeOf(result) === 'array') {
          return get(result, 'length') !== 0;
        } else {
          return !!result;
        }
      } );
    } else {
      throw new SC.Error("Cannot use boundIf helper without a block.");
    }
  });
})();

/**
  @name Handlebars.helpers.with
  @param {Function} context
  @param {Hash} options
  @returns {String} HTML string
*/
Handlebars.registerHelper('with', function(context, options) {
  return Handlebars.helpers.bind.call(options.contexts[0], context, options);
});

/**
  @name Handlebars.helpers.if
  @param {Function} context
  @param {Hash} options
  @returns {String} HTML string
*/
Handlebars.registerHelper('if', function(context, options) {
  return Handlebars.helpers.boundIf.call(options.contexts[0], context, options);
});

/**
  @name Handlebars.helpers.unless
  @param {Function} context
  @param {Hash} options
  @returns {String} HTML string
*/
Handlebars.registerHelper('unless', function(context, options) {
  var fn = options.fn, inverse = options.inverse;

  options.fn = inverse;
  options.inverse = fn;

  return Handlebars.helpers.boundIf.call(options.contexts[0], context, options);
});

/**
  `bindAttr` allows you to create a binding between DOM element attributes and
  SproutCore objects. For example:

      <img {{bindAttr src="imageUrl" alt="imageTitle"}}>

  @name Handlebars.helpers.bindAttr
  @param {Hash} options
  @returns {String} HTML string
*/
Handlebars.registerHelper('bindAttr', function(options) {

  var attrs = options.hash;
  var view = options.data.view;
  var ret = [];
  var ctx = this;

  // Generate a unique id for this element. This will be added as a
  // data attribute to the element so it can be looked up when
  // the bound property changes.
  var dataId = jQuery.uuid++;

  // Handle classes differently, as we can bind multiple classes
  var classBindings = attrs['class'];
  if (classBindings !== null && classBindings !== undefined) {
    var classResults = SC.Handlebars.bindClasses(this, classBindings, view, dataId);
    ret.push('class="' + classResults.join(' ') + '"');
    delete attrs['class'];
  }

  var attrKeys = SC.keys(attrs);

  // For each attribute passed, create an observer and emit the
  // current value of the property as an attribute.
  attrKeys.forEach(function(attr) {
    var property = attrs[attr];
    var value = getPath(ctx, property);

    var observer, invoker;

    observer = function observer() {
      var result = getPath(ctx, property);
      var elem = view.$("[data-handlebars-id='" + dataId + "']");

      // If we aren't able to find the element, it means the element
      // to which we were bound has been removed from the view.
      // In that case, we can assume the template has been re-rendered
      // and we need to clean up the observer.
      if (elem.length === 0) {
        SC.removeObserver(ctx, property, invoker);
        return;
      }

      // A false result will remove the attribute from the element. This is
      // to support attributes such as disabled, whose presence is meaningful.
      if (result === NO) {
        elem.removeAttr(attr);

      // Likewise, a true result will set the attribute's name as the value.
      } else if (result === YES) {
        elem.attr(attr, attr);

      } else {
        elem.attr(attr, result);
      }
    };

    invoker = function() {
      SC.run.once(observer);
    };

    // Add an observer to the view for when the property changes.
    // When the observer fires, find the element using the
    // unique data id and update the attribute to the new value.
    SC.addObserver(ctx, property, invoker);

    // Use the attribute's name as the value when it is YES
    if (value === YES) {
      value = attr;
    }

    // Do not add the attribute when the value is false
    if (value !== NO) {
      // Return the current value, in the form src="foo.jpg"
      ret.push(attr + '="' + value + '"');
    }
  }, this);

  // Add the unique identifier
  ret.push('data-handlebars-id="' + dataId + '"');
  return ret.join(' ');
});

/**
  Helper that, given a space-separated string of property paths and a context,
  returns an array of class names. Calling this method also has the side
  effect of setting up observers at those property paths, such that if they
  change, the correct class name will be reapplied to the DOM element.

  For example, if you pass the string "fooBar", it will first look up the
  "fooBar" value of the context. If that value is YES, it will add the
  "foo-bar" class to the current element (i.e., the dasherized form of
  "fooBar"). If the value is a string, it will add that string as the class.
  Otherwise, it will not add any new class name.

  @param {SC.Object} context
    The context from which to lookup properties

  @param {String} classBindings
    A string, space-separated, of class bindings to use

  @param {SC.View} view
    The view in which observers should look for the element to update

  @param {String} id
    Optional id use to lookup elements

  @returns {Array} An array of class names to add
*/
SC.Handlebars.bindClasses = function(context, classBindings, view, id) {
  var ret = [], newClass, value, elem;

  // Helper method to retrieve the property from the context and
  // determine which class string to return, based on whether it is
  // a Boolean or not.
  var classStringForProperty = function(property) {
    var val = getPath(context, property);

    // If value is a Boolean and true, return the dasherized property
    // name.
    if (val === YES) {
      // Normalize property path to be suitable for use
      // as a class name. For exaple, content.foo.barBaz
      // becomes bar-baz.
      return SC.String.dasherize(get(property.split('.'), 'lastObject'));

    // If the value is not NO, undefined, or null, return the current
    // value of the property.
    } else if (val !== NO && val !== undefined && val !== null) {
      return val;

    // Nothing to display. Return null so that the old class is removed
    // but no new class is added.
    } else {
      return null;
    }
  };

  // For each property passed, loop through and setup
  // an observer.
  classBindings.split(' ').forEach(function(property) {

    // Variable in which the old class value is saved. The observer function
    // closes over this variable, so it knows which string to remove when
    // the property changes.
    var oldClass;

    var observer, invoker;

    // Set up an observer on the context. If the property changes, toggle the
    // class name.
    observer = function() {
      // Get the current value of the property
      newClass = classStringForProperty(property);
      elem = id ? view.$("[data-handlebars-id='" + id + "']") : view.$();

      // If we can't find the element anymore, a parent template has been
      // re-rendered and we've been nuked. Remove the observer.
      if (elem.length === 0) {
        SC.removeObserver(context, property, invoker);
      } else {
        // If we had previously added a class to the element, remove it.
        if (oldClass) {
          elem.removeClass(oldClass);
        }

        // If necessary, add a new class. Make sure we keep track of it so
        // it can be removed in the future.
        if (newClass) {
          elem.addClass(newClass);
          oldClass = newClass;
        } else {
          oldClass = null;
        }
      }
    };

    invoker = function() {
      SC.run.once(observer);
    };

    SC.addObserver(context, property, invoker);

    // We've already setup the observer; now we just need to figure out the
    // correct behavior right now on the first pass through.
    value = classStringForProperty(property);

    if (value) {
      ret.push(value);

      // Make sure we save the current value so that it can be removed if the
      // observer fires.
      oldClass = value;
    }
  });

  return ret;
};

<commit_msg>Output of bindAttr should not be escaped.<commit_after>// ==========================================================================
// Project:   SproutCore Handlebar Views
// Copyright: ©2011 Strobe Inc. and contributors.
// License:   Licensed under MIT license (see license.js)
// ==========================================================================
/*globals Handlebars */

require('sproutcore-handlebars/ext');
require('sproutcore-handlebars/views/bindable_span');

var get = SC.get, getPath = SC.getPath;

(function() {
  // Binds a property into the DOM. This will create a hook in DOM that the
  // KVO system will look for and upate if the property changes.
  var bind = function(property, options, preserveContext, shouldDisplay) {
    var data = options.data,
        fn = options.fn,
        inverse = options.inverse,
        view = data.view,
        ctx  = this;

    // Set up observers for observable objects
    if ('object' === typeof this) {
      // Create the view that will wrap the output of this template/property
      // and add it to the nearest view's childViews array.
      // See the documentation of SC._BindableSpanView for more.
      var bindView = view.createChildView(SC._BindableSpanView, {
        preserveContext: preserveContext,
        shouldDisplayFunc: shouldDisplay,
        displayTemplate: fn,
        inverseTemplate: inverse,
        property: property,
        previousContext: ctx,
        isEscaped: options.hash.escaped
      });

      var observer, invoker;

      get(view, 'childViews').pushObject(bindView);

      observer = function() {
        if (get(bindView, 'element')) {
          bindView.rerender();
        } else {
          // If no layer can be found, we can assume somewhere
          // above it has been re-rendered, so remove the
          // observer.
          SC.removeObserver(ctx, property, invoker);
        }
      };

      invoker = function() {
        SC.run.once(observer);
      };

      // Observes the given property on the context and
      // tells the SC._BindableSpan to re-render.
      SC.addObserver(ctx, property, invoker);

      var buffer = bindView.renderToBuffer();
      return new Handlebars.SafeString(buffer.string());
    } else {
      // The object is not observable, so just render it out and
      // be done with it.
      return getPath(this, property);
    }
  };

  /**
    `bind` can be used to display a value, then update that value if it
    changes. For example, if you wanted to print the `title` property of
    `content`:

        {{bind "content.title"}}

    This will return the `title` property as a string, then create a new
    observer at the specified path. If it changes, it will update the value in
    DOM. Note that if you need to support IE7 and IE8 you must modify the
    model objects properties using SC.get() and SC.set() for this to work as
    it relies on SC's KVO system.  For all other browsers this will be handled
    for you automatically.

    @name Handlebars.helpers.bind
    @param {String} property Property to bind
    @param {Function} fn Context to provide for rendering
    @returns {String} HTML string
  */
  Handlebars.registerHelper('bind', function(property, fn) {
    return bind.call(this, property, fn, false, function(result) {
      return !SC.none(result);
    });
  });

  /**
    Use the `boundIf` helper to create a conditional that re-evaluates
    whenever the bound value changes.

        {{#boundIf "content.shouldDisplayTitle"}}
          {{content.title}}
        {{/boundIf}}

    @name Handlebars.helpers.boundIf
    @param {String} property Property to bind
    @param {Function} fn Context to provide for rendering
    @returns {String} HTML string
  */
  Handlebars.registerHelper('boundIf', function(property, fn) {
    if(fn) {
      return bind.call(this, property, fn, true, function(result) {
        if (SC.typeOf(result) === 'array') {
          return get(result, 'length') !== 0;
        } else {
          return !!result;
        }
      } );
    } else {
      throw new SC.Error("Cannot use boundIf helper without a block.");
    }
  });
})();

/**
  @name Handlebars.helpers.with
  @param {Function} context
  @param {Hash} options
  @returns {String} HTML string
*/
Handlebars.registerHelper('with', function(context, options) {
  return Handlebars.helpers.bind.call(options.contexts[0], context, options);
});

/**
  @name Handlebars.helpers.if
  @param {Function} context
  @param {Hash} options
  @returns {String} HTML string
*/
Handlebars.registerHelper('if', function(context, options) {
  return Handlebars.helpers.boundIf.call(options.contexts[0], context, options);
});

/**
  @name Handlebars.helpers.unless
  @param {Function} context
  @param {Hash} options
  @returns {String} HTML string
*/
Handlebars.registerHelper('unless', function(context, options) {
  var fn = options.fn, inverse = options.inverse;

  options.fn = inverse;
  options.inverse = fn;

  return Handlebars.helpers.boundIf.call(options.contexts[0], context, options);
});

/**
  `bindAttr` allows you to create a binding between DOM element attributes and
  SproutCore objects. For example:

      <img {{bindAttr src="imageUrl" alt="imageTitle"}}>

  @name Handlebars.helpers.bindAttr
  @param {Hash} options
  @returns {String} HTML string
*/
Handlebars.registerHelper('bindAttr', function(options) {

  var attrs = options.hash;
  var view = options.data.view;
  var ret = [];
  var ctx = this;

  // Generate a unique id for this element. This will be added as a
  // data attribute to the element so it can be looked up when
  // the bound property changes.
  var dataId = jQuery.uuid++;

  // Handle classes differently, as we can bind multiple classes
  var classBindings = attrs['class'];
  if (classBindings !== null && classBindings !== undefined) {
    var classResults = SC.Handlebars.bindClasses(this, classBindings, view, dataId);
    ret.push('class="' + classResults.join(' ') + '"');
    delete attrs['class'];
  }

  var attrKeys = SC.keys(attrs);

  // For each attribute passed, create an observer and emit the
  // current value of the property as an attribute.
  attrKeys.forEach(function(attr) {
    var property = attrs[attr];
    var value = getPath(ctx, property);

    var observer, invoker;

    observer = function observer() {
      var result = getPath(ctx, property);
      var elem = view.$("[data-handlebars-id='" + dataId + "']");

      // If we aren't able to find the element, it means the element
      // to which we were bound has been removed from the view.
      // In that case, we can assume the template has been re-rendered
      // and we need to clean up the observer.
      if (elem.length === 0) {
        SC.removeObserver(ctx, property, invoker);
        return;
      }

      // A false result will remove the attribute from the element. This is
      // to support attributes such as disabled, whose presence is meaningful.
      if (result === NO) {
        elem.removeAttr(attr);

      // Likewise, a true result will set the attribute's name as the value.
      } else if (result === YES) {
        elem.attr(attr, attr);

      } else {
        elem.attr(attr, result);
      }
    };

    invoker = function() {
      SC.run.once(observer);
    };

    // Add an observer to the view for when the property changes.
    // When the observer fires, find the element using the
    // unique data id and update the attribute to the new value.
    SC.addObserver(ctx, property, invoker);

    // Use the attribute's name as the value when it is YES
    if (value === YES) {
      value = attr;
    }

    // Do not add the attribute when the value is false
    if (value !== NO) {
      // Return the current value, in the form src="foo.jpg"
      ret.push(attr + '="' + value + '"');
    }
  }, this);

  // Add the unique identifier
  ret.push('data-handlebars-id="' + dataId + '"');
  return new Handlebars.SafeString(ret.join(' '));
});

/**
  Helper that, given a space-separated string of property paths and a context,
  returns an array of class names. Calling this method also has the side
  effect of setting up observers at those property paths, such that if they
  change, the correct class name will be reapplied to the DOM element.

  For example, if you pass the string "fooBar", it will first look up the
  "fooBar" value of the context. If that value is YES, it will add the
  "foo-bar" class to the current element (i.e., the dasherized form of
  "fooBar"). If the value is a string, it will add that string as the class.
  Otherwise, it will not add any new class name.

  @param {SC.Object} context
    The context from which to lookup properties

  @param {String} classBindings
    A string, space-separated, of class bindings to use

  @param {SC.View} view
    The view in which observers should look for the element to update

  @param {String} id
    Optional id use to lookup elements

  @returns {Array} An array of class names to add
*/
SC.Handlebars.bindClasses = function(context, classBindings, view, id) {
  var ret = [], newClass, value, elem;

  // Helper method to retrieve the property from the context and
  // determine which class string to return, based on whether it is
  // a Boolean or not.
  var classStringForProperty = function(property) {
    var val = getPath(context, property);

    // If value is a Boolean and true, return the dasherized property
    // name.
    if (val === YES) {
      // Normalize property path to be suitable for use
      // as a class name. For exaple, content.foo.barBaz
      // becomes bar-baz.
      return SC.String.dasherize(get(property.split('.'), 'lastObject'));

    // If the value is not NO, undefined, or null, return the current
    // value of the property.
    } else if (val !== NO && val !== undefined && val !== null) {
      return val;

    // Nothing to display. Return null so that the old class is removed
    // but no new class is added.
    } else {
      return null;
    }
  };

  // For each property passed, loop through and setup
  // an observer.
  classBindings.split(' ').forEach(function(property) {

    // Variable in which the old class value is saved. The observer function
    // closes over this variable, so it knows which string to remove when
    // the property changes.
    var oldClass;

    var observer, invoker;

    // Set up an observer on the context. If the property changes, toggle the
    // class name.
    observer = function() {
      // Get the current value of the property
      newClass = classStringForProperty(property);
      elem = id ? view.$("[data-handlebars-id='" + id + "']") : view.$();

      // If we can't find the element anymore, a parent template has been
      // re-rendered and we've been nuked. Remove the observer.
      if (elem.length === 0) {
        SC.removeObserver(context, property, invoker);
      } else {
        // If we had previously added a class to the element, remove it.
        if (oldClass) {
          elem.removeClass(oldClass);
        }

        // If necessary, add a new class. Make sure we keep track of it so
        // it can be removed in the future.
        if (newClass) {
          elem.addClass(newClass);
          oldClass = newClass;
        } else {
          oldClass = null;
        }
      }
    };

    invoker = function() {
      SC.run.once(observer);
    };

    SC.addObserver(context, property, invoker);

    // We've already setup the observer; now we just need to figure out the
    // correct behavior right now on the first pass through.
    value = classStringForProperty(property);

    if (value) {
      ret.push(value);

      // Make sure we save the current value so that it can be removed if the
      // observer fires.
      oldClass = value;
    }
  });

  return ret;
};

<|endoftext|>


<commit_before>var Project = require('../db').models.Project;
var protect = require('../util/protect');

module.exports = function(app) {
  app.get('/api/projects', function(req, res) {
    var offset  = req.query.offset || 0;
    var limit   = req.query.limit || 20;
    var tag     = req.query.tag;

    var query;
    if (tag) {
      query = {};
      query.tags = { '$in': [ tag ] };
    }

    Project.find(query)
      .skip(offset)
      .limit(limit)
      .then(function(results) {
        return res.status(200).json(results);
      })
      .catch(function(err) {
        return res.status(500).send(err);
      });
  });

  app.get('/api/projects/:id', function(req, res) {
    var offset  = req.query.offset || 0;
    var limit   = req.query.limit || 20;
    var tag     = req.query.tag;

    var query = { '_id': req.params['id'] };

    Project.findOne(query)
      .then(function(result) {
        return res.status(200).json(result);
      })
      .catch(function(err) {
        return res.status(500).send(err);
      });
  });

  app.get('/api/myprojects', function(req, res) {
    var query;
    if (req && req.session && req.session.user) {
      query = {
        'creator.liveId': req.session.user
      };
    } else {
      query = {
        'creator.name': { $regex: /.*Mcc.*/ }
      };
    }

    Project.find(query)
      .then(function(results) {
        return res.status(200).json(results);
      })
      .catch(function(err) {
        return res.status(500).send(err);
      });
  });
};
<commit_msg>added the back route<commit_after>var Project = require('../db').models.Project;
var protect = require('../util/protect');

module.exports = function(app) {
  app.get('/api/projects', function(req, res) {
    var offset  = req.query.offset || 0;
    var limit   = req.query.limit || 20;
    var tag     = req.query.tag;

    var query;
    if (tag) {
      query = {};
      query.tags = { '$in': [ tag ] };
    }

    Project.find(query)
      .skip(offset)
      .limit(limit)
      .then(function(results) {
        return res.status(200).json(results);
      })
      .catch(function(err) {
        return res.status(500).send(err);
      });
  });

  app.get('/api/projects/:id', function(req, res) {
    var offset  = req.query.offset || 0;
    var limit   = req.query.limit || 20;
    var tag     = req.query.tag;

    var query = { '_id': req.params['id'] };

    Project.findOne(query)
      .then(function(result) {
        return res.status(200).json(result);
      })
      .catch(function(err) {
        return res.status(500).send(err);
      });
  });

  app.get('/api/myprojects', function(req, res) {
    var query;
    if (req && req.session && req.session.user) {
      query = {
        'creator.liveId': req.session.user
      };
    } else {
      query = {
        'creator.name': { $regex: /.*Mcc.*/ }
      };
    }

    Project.find(query)
      .then(function(results) {
        return res.status(200).json(results);
      })
      .catch(function(err) {
        return res.status(500).send(err);
      });
  });

  app.post('/api/projects/:id/back', function(req, res) {
    return res.status(200).send();
  });
};
<|endoftext|>


<commit_before>// ==UserScript==
// @id             iitc-plugin-keys@xelio
// @name           IITC plugin: Keys
// @category       Keys
// @version        0.2.0.@@DATETIMEVERSION@@
// @namespace      https://github.com/jonatkins/ingress-intel-total-conversion
// @updateURL      @@UPDATEURL@@
// @downloadURL    @@DOWNLOADURL@@
// @description    [@@BUILDNAME@@-@@BUILDDATE@@] Allow manual entry of key counts for each portal. Use 'keys-on-map' to show the numbers on the map, and 'sync' to share between multiple browsers or desktop/mobile.
// @include        https://www.ingress.com/intel*
// @include        http://www.ingress.com/intel*
// @match          https://www.ingress.com/intel*
// @match          http://www.ingress.com/intel*
// @grant          none
// ==/UserScript==

@@PLUGINSTART@@

// PLUGIN START ////////////////////////////////////////////////////////

// use own namespace for plugin
window.plugin.keys = function() {};

// delay in ms
window.plugin.keys.SYNC_DELAY = 10000;

window.plugin.keys.LOCAL_STORAGE_KEY = 'plugin-keys-data';

window.plugin.keys.KEY = {key: 'plugin-keys-data', field: 'keys'};
window.plugin.keys.UPDATE_QUEUE = {key: 'plugin-keys-data-queue', field: 'updateQueue'};
window.plugin.keys.UPDATING_QUEUE = {key: 'plugin-keys-data-updating-queue', field: 'updatingQueue'};

window.plugin.keys.keys = {};
window.plugin.keys.updateQueue = {};
window.plugin.keys.updatingQueue = {};

window.plugin.keys.enableSync = false;

window.plugin.keys.disabledMessage = null;
window.plugin.keys.contentHTML = null;

window.plugin.keys.addToSidebar = function() {
  if(typeof(Storage) === "undefined") {
    $('#portaldetails > .imgpreview').after(plugin.keys.disabledMessage);
    return;
  }

  $('#portaldetails > .imgpreview').after(plugin.keys.contentHTML);
  plugin.keys.updateDisplayCount();
}

window.plugin.keys.updateDisplayCount = function() {
  var guid = window.selectedPortal;
  var count = plugin.keys.keys[guid] || 0;
  $('#keys-count').html(count);
}

window.plugin.keys.addKey = function(addCount) {
  var guid = window.selectedPortal;
  var oldCount = plugin.keys.keys[guid];
  var newCount = Math.max((oldCount || 0) + addCount, 0);
  if(oldCount !== newCount) {
    if(newCount === 0) {
      delete plugin.keys.keys[guid];
      plugin.keys.updateQueue[guid] = null;
    } else {
      plugin.keys.keys[guid] = newCount;
      plugin.keys.updateQueue[guid] = newCount;
    }

    plugin.keys.storeLocal(plugin.keys.KEY);
    plugin.keys.storeLocal(plugin.keys.UPDATE_QUEUE);
    plugin.keys.updateDisplayCount();
    window.runHooks('pluginKeysUpdateKey', {guid: guid, count: newCount});
    plugin.keys.delaySync();
  }
}

// Delay the syncing to group a few updates in a single request
window.plugin.keys.delaySync = function() {
  if(!plugin.keys.enableSync) return;
  clearTimeout(plugin.keys.delaySync.timer);
  plugin.keys.delaySync.timer = setTimeout(function() {
      plugin.keys.delaySync.timer = null;
      window.plugin.keys.syncNow();
    }, plugin.keys.SYNC_DELAY);
}

// Store the upadteQueue in updatingQueue and upload
window.plugin.keys.syncNow = function() {
  if(!plugin.keys.enableSync) return;
  $.extend(plugin.keys.updatingQueue, plugin.keys.updateQueue);
  plugin.keys.updateQueue = {};
  plugin.keys.storeLocal(plugin.keys.UPDATING_QUEUE);
  plugin.keys.storeLocal(plugin.keys.UPDATE_QUEUE);

  plugin.sync.updateMap('keys', 'keys', Object.keys(plugin.keys.updatingQueue));
}

// Call after IITC and all plugin loaded
window.plugin.keys.registerFieldForSyncing = function() {
  if(!window.plugin.sync) return;
  window.plugin.sync.registerMapForSync('keys', 'keys', window.plugin.keys.syncCallback, window.plugin.keys.syncInitialed);
}

// Call after local or remote change uploaded
window.plugin.keys.syncCallback = function(pluginName, fieldName, e, fullUpdated) {
  if(fieldName === 'keys') {
    plugin.keys.storeLocal(plugin.keys.KEY);
    // All data is replaced if other client update the data duing this client offline,
    // fire 'pluginKeysRefreshAll' to notify a full update
    if(fullUpdated) {
      plugin.keys.updateDisplayCount();
      window.runHooks('pluginKeysRefreshAll');
      return;
    }

    if(!e) return;
    if(e.isLocal) {
      // Update pushed successfully, remove it from updatingQueue
      delete plugin.keys.updatingQueue[e.property];
    } else {
      // Remote update
      delete plugin.keys.updateQueue[e.property];
      plugin.keys.storeLocal(plugin.keys.UPDATE_QUEUE);
      plugin.keys.updateDisplayCount();
      window.runHooks('pluginKeysUpdateKey', {guid: e.property, count: plugin.keys.keys[e.property]});
    }
  }
}

// syncing of the field is initialed, upload all queued update
window.plugin.keys.syncInitialed = function(pluginName, fieldName) {
  if(fieldName === 'keys') {
    plugin.keys.enableSync = true;
    if(Object.keys(plugin.keys.updateQueue).length > 0) {
      plugin.keys.delaySync();
    }
  }
}

window.plugin.keys.storeLocal = function(mapping) {
  if(typeof(plugin.keys[mapping.field]) !== 'undefined' && plugin.keys[mapping.field] !== null) {
    localStorage[mapping.key] = JSON.stringify(plugin.keys[mapping.field]);
  } else {
    localStorage.removeItem(mapping.key);
  }
}

window.plugin.keys.loadLocal = function(mapping) {
  var objectJSON = localStorage[mapping.key];
  if(!objectJSON) return;
  plugin.keys[mapping.field] = mapping.convertFunc
                          ? mapping.convertFunc(JSON.parse(objectJSON))
                          : JSON.parse(objectJSON);
}

// For backward compitable, will change to use loadLocal after a few version
window.plugin.keys.loadKeys = function() {
  var keysObjectJSON = localStorage[plugin.keys.KEY.key];
  if(!keysObjectJSON) return;
  var keysObject = JSON.parse(keysObjectJSON);
  // Move keys data up one level, it was {keys: keys_data} in localstorage in previous version
  plugin.keys.keys = keysObject.keys ? keysObject.keys : keysObject;
  if(keysObject.keys) plugin.keys.storeLocal(plugin.keys.KEY);
}

window.plugin.keys.setupCSS = function() {
  $("


<commit_before>import Ember from 'ember';
import Converter from 'pd-converter';
import getSanitizingConverter from 'pd-get-sanitizing-converter';
import Editor from 'pd-editor';
import HookCollection from 'pd-hook-collection';

export default Ember.Object.extend({

  /* singletones */

  converter: new Converter(),

  sanitizedConverter: getSanitizingConverter(),

  /* create your own */

  Converter: Converter,

  Editor: Editor,

  HookCollection: HookCollection

});
<commit_msg>use Ember.Service<commit_after>import Ember from 'ember';
import Converter from 'pd-converter';
import getSanitizingConverter from 'pd-get-sanitizing-converter';
import Editor from 'pd-editor';
import HookCollection from 'pd-hook-collection';

// TODO: remove compatibility for Ember < 1.13.0-beta.2
var serviceBaseObject = Ember.Service ? Ember.Service : Ember.Object;

export default serviceBaseObject.extend({

  /* singletones */

  converter: new Converter(),

  sanitizedConverter: getSanitizingConverter(),

  /* create your own */

  Converter: Converter,

  Editor: Editor,

  HookCollection: HookCollection

});
<|endoftext|>


<commit_before>/*
 * gfw.js - resource.js
 * Copyright(c) 2011 Aaron Hedges <aaron@dashron.com>
 * MIT Licensed
 */

"use strict";
//var fs_module = require('fs');
var url_module = require('url');
var mongoose_module = require('mongoose');
var http_module = require('http');
// todo replace this with a chunked renderer like mu?
//var hogan_module = require('hogan.js');

//var static_component = require('./static');
//var request_component = require('./request_wrapper');
//var response_component = require('./response_wrapper');
var accept_header_component = require('./accept_header');
var view_component = require('./view');
var View = view_component.View;
var Firebug = require('./firenode/firenode').Firebug;
var Cookie = require('./cookie').Cookie;
var Router = require('./router').RegexRouter;

var _resources = {};

/**
 * Build a single resource by name, and cache it
 *
 * @param {String}
 *            name
 * @param {Object}
 *            config
 * @return {Resource}
 */
var get_resource = exports.get = function (name, config) {
	if (typeof _resources[name] == "undefined" || _resources[name] == null) {
		console.log("Loading Resource:" + name);
		_resources[name] = build(name, require(__dirname.replace('components', 'resources/')
			+ name + '/' + name + '.desc.js'));
	}

	return _resources[name];
};

/**
 * Removes a single resource from the cache list
 *
 * @param  {[type]} name [description]
 * @return {[type]}
 */
var remove_resource = exports.remove = function (name) {
	_resources[name] = null;
};

/**
 * Free up the memory of all resources built within this module
 */
var clear = exports.clear = function () {
	_resources = {};
};

/**
 * [build description]
 * @param  {[type]} description [description]
 * @return {[type]}
 */
var build = exports.build = function (name, description) {
	var i = 0, j = 0;
	var key = null;
	var route = null;
	var resource = new Resource(name);

	resource.uri = description.uri;
	resource.directory = __dirname.replace("components", '') + 'resources/' + name;
	resource.template_dir = resource.directory + '/templates/';
	resource.template = description.template;

	resource.router.unmatched_route = description.unmatched_route;
	resource.config = description.config;

	for (i = 0; i < description.routes.length; i++) {
		route = description.routes[i];
		if (typeof route.options != "object") {
			route.options = {};
		}
		resource.addRoute(route.match, route, route.options.keys);
	}

	for (i = 0; i < description.dependencies.length; i++) {
		resource.addChild(get_resource(description.dependencies[i]));
	}

	if (typeof resource.config.db === "object" && typeof resource.config.db.connection === "string") {
		resource.db = mongoose_module.createConnection(resource.config.db.connection);
		populate_child_connections(resource.children, resource.db);
	}

	for (key in description.models) {
		resource.addModel(key, description.models[key]);
	}

	return resource;
};

/**
 * Iterates through all dependent resources and applies a datbase connection if not pre-configured for one
 * @param  {[type]} children   [description]
 * @param  {[type]} connection [description]
 * @return {[type]}
 */
var populate_child_connections = function (children, connection) {
	var key = null;
	for (key in children) {
		if (typeof children[key].db === "undefined" || children[key].db === null) {
			children[key].db = connection;
			// we want to fill all empty children with the provided default connection
			// this might not  be the right path
			populate_child_connections(children[key].children, connection);
		}
	}
};

/**
 * [Resource description]
 * @param {[type]} name [description]
 */
var Resource = exports.Resource = function Resource (name) {
	this.name = name;
	this.config = {};
	this.router = new Router();
	this.models = {};
	this.resources = {};
	this.db = null;
	this.unmatched_route = null;
};

Resource.prototype.name = '';
Resource.prototype.uri = '';
Resource.prototype.config = null;
Resource.prototype.directory = '';
Resource.prototype.template_dir = '';
Resource.prototype.template = null;
Resource.prototype.router = null;
Resource.prototype.resources = null;
Resource.prototype.models = {};
Resource.prototype.db = null;

/**
 * [getResource description]
 * @param  {[type]} name [description]
 * @return {[type]}      [description]
 */
Resource.prototype.getResource = function (name) {
	return get_resource(name);
};

/**
 * [addRoutes description]
 * @param {[type]} match   [description]
 * @param {Object} routes  Mapping of Method => Function
 * @param {[type]} options [description]
 */
Resource.prototype.addRoute = function (match, route, keys) {
	this.router.addRoute(match, route, keys);
};

/**
 * [addChild description]
 * @param {[type]} resource [description]
 */
Resource.prototype.addChild = function (resource) {
	this.resources[resource.name] = resource;
}

/**
 * [addModel description]
 * @param {[type]} key   [description]
 * @param {[type]} model [description]
 */
Resource.prototype.addModel = function (key, model) {
	this.models[key] = model;
}

/**
 * [route description]
 * @param  {[type]} uri_bundle [description]
 * @param  {[type]} view       [description]
 * @return {[type]}
 */
Resource.prototype.request = function (uri_bundle, view) {
	var key = null;
	var template_dir = this.template_dir;

	// Allow direct urls for shorthand. Assume a GET request in this case
	if (typeof uri_bundle === "string") {
		uri_bundle = {
			uri : uri_bundle,
			method : 'GET'
		}
	}

	var route = this.getRoute(uri_bundle);

	if (!route) {
		// attempt each child, see if you can find a proper route
		for (key in this.resources) {
			route = this.resources[key].getRoute(uri_bundle);
			if (route) {
				// ensure that the proper template directory is used within the view
				template_dir = this.resources[key].template_dir;
				break;
			}
		}

		if (!route) {
			route = this.unmatched_route;
		}

		if (!route) {
			// todo: 404
			throw new Error('route not found :' + uri_bundle.uri + ' [' + this.name + ']');
		}
	}

	// If the template provided is actually a server response, we need to build the very first view
	if (view instanceof http_module.ServerResponse) {
		var response = view;

		view = new View();
		view.setRenderMode(accept_header_component.getRenderMode(uri_bundle.headers.accept, route.modes));
		// todo: not sure this will actually be desired due to view template precedence.
		//view.setTemplate(this.default_template);
		view.setResponse(response);
	}

	// If a template is set in the config, apply it to the current view and then provide a child view to the route
	if (typeof this.template === "function") {
		// We don't want to set the route resources directory, we will always create the template from the resource upon which request is called
		view.setDir(this.template_dir);
		var child = view.child('content');
		this.template(view);
		view = child;
	}

	// assume that we want to load templates directly from this route, no matter the data provided
	view.setDir(template_dir);

	// route, allowing this to point to the original resource, and provide some helper utils
	if (typeof route[uri_bundle.method] == "function") {
		process.nextTick(function() {
			route[uri_bundle.method].call(this, uri_bundle, view);
		});
	} else if (typeof route['default'] === "function") {
		process.nextTick(function() {
			route.default.call(this, uri_bundle, view);
		});
	} else {
		var keys = [];
		['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'].forEach(function (option) {
			if (typeof route[option] === "function") {
				keys.push(option)
			}
		});
		view.unsupportedMethod(keys);
	}
};

/**
 * [getRoute description]
 * @param  {[type]} uri_bundle [description]
 * @return {[type]}
 */
Resource.prototype.getRoute = function (uri_bundle) {

	if (uri_bundle.uri.indexOf(this.uri) === 0) {
		// strip the resource uri out of the request uri
		uri_bundle.uri = uri_bundle.uri.substring(this.uri.length);

		// what to do here? how do I route down into the children?
		// We don't have to pass the view, we can assume it's correct.
		// we just need to make sure that the promise returned is bound to the right resource
		var route = this.router.getRoute(uri_bundle);

		if (route) {
			return route;
		}
	}

	// No route was found
	return false;
};

/**
 *
 * @param router
 * @returns
 * @todo allow users to configure their resource to not take default template or
 *       js or css routes
 */
/*var applyTemplateRoutes = function (router, resource) {
	router.add(new RegExp('^/' + resource.name + '/template/(.+)$'), function (request, response, callback) {
		static_component.streamFile(resource.template_dir + request.GET['template'], response, {
			request : request,
			callback : callback
		});
	}, {keys : ['template']});

	router.add(new RegExp('^/' + resource.name + '/template/(.+)$'), function (request, response, callback) {
		static_component.loadFile(resource.template_dir + request.GET['template'], function (contents) {
			// todo replace this with a chunked renderer like mu?
			var template = hogan_module.compile(contents);
			response.ok(template.render(request.POST));
			callback();

		}, function (error) {
			response.error(error);
			callback();
		});
	}, {method : "POST", keys : ['template']});

	router.add(new RegExp('^/' + resource.name + '(\/.+\.js)$'), function (request, response, callback) {
		var filename = request.GET['file'].replace(/\.\./, '');
		static_component.streamFile(resource.directory + '/templates/js' + filename, response, {
			request : request,
			callback : callback
		});
	}, {keys : ['file']});

	router.add(new RegExp('^/' + resource.name + '(\/.+\.css)$'), function (request, response, callback) {
		var filename = request.GET['file'].replace(/\.\./, '');
		static_component.streamFile(resource.directory + '/templates/css' + filename, response, {
			request : request,
			callback : callback
		});
	}, {keys : ['file']});
};

var applyResourceRoutes = function (router, resource) {
	router.add(new RegExp('^/' + resource.name + '/(\d+)$'), function (request, response, callback) {
		resource.models[resource.name].get({id : request.GET['id']});
	}, {method : "GET", keys : ['id']});

	router.add(new RegExp('^/' + resource.name + '/$'), function (request, response, callback) {
		resource.models[resource.name].get({id : request.GET['id']});
	}, {method : "POST", keys : ['id']});

	router.add(new RegExp('^/' + resource.name + '/(\d+)$'), function (request, response, callback) {
		resource.models[resource.name].save({id : request.GET['id']});
	}, {method : "PUT", keys : ['id']});

	router.add(new RegExp('^/' + resource.name + '/(\d+)$'), function (request, response, callback) {
		resource.models[resource.name].delete({id : request.GET['id']});
	}, {method : "DELETE", keys : ['id']});
};*/
<commit_msg>fix bug causing this to be incorrect in a route<commit_after>/*
 * gfw.js - resource.js
 * Copyright(c) 2011 Aaron Hedges <aaron@dashron.com>
 * MIT Licensed
 */

"use strict";
//var fs_module = require('fs');
var url_module = require('url');
var mongoose_module = require('mongoose');
var http_module = require('http');
// todo replace this with a chunked renderer like mu?
//var hogan_module = require('hogan.js');

//var static_component = require('./static');
//var request_component = require('./request_wrapper');
//var response_component = require('./response_wrapper');
var accept_header_component = require('./accept_header');
var view_component = require('./view');
var View = view_component.View;
var Firebug = require('./firenode/firenode').Firebug;
var Cookie = require('./cookie').Cookie;
var Router = require('./router').RegexRouter;

var _resources = {};

/**
 * Build a single resource by name, and cache it
 *
 * @param {String}
 *            name
 * @param {Object}
 *            config
 * @return {Resource}
 */
var get_resource = exports.get = function (name, config) {
	if (typeof _resources[name] == "undefined" || _resources[name] == null) {
		console.log("Loading Resource:" + name);
		_resources[name] = build(name, require(__dirname.replace('components', 'resources/')
			+ name + '/' + name + '.desc.js'));
	}

	return _resources[name];
};

/**
 * Removes a single resource from the cache list
 *
 * @param  {[type]} name [description]
 * @return {[type]}
 */
var remove_resource = exports.remove = function (name) {
	_resources[name] = null;
};

/**
 * Free up the memory of all resources built within this module
 */
var clear = exports.clear = function () {
	_resources = {};
};

/**
 * [build description]
 * @param  {[type]} description [description]
 * @return {[type]}
 */
var build = exports.build = function (name, description) {
	var i = 0, j = 0;
	var key = null;
	var route = null;
	var resource = new Resource(name);

	resource.uri = description.uri;
	resource.directory = __dirname.replace("components", '') + 'resources/' + name;
	resource.template_dir = resource.directory + '/templates/';
	resource.template = description.template;

	resource.router.unmatched_route = description.unmatched_route;
	resource.config = description.config;

	for (i = 0; i < description.routes.length; i++) {
		route = description.routes[i];
		if (typeof route.options != "object") {
			route.options = {};
		}
		resource.addRoute(route.match, route, route.options.keys);
	}

	for (i = 0; i < description.dependencies.length; i++) {
		resource.addChild(get_resource(description.dependencies[i]));
	}

	if (typeof resource.config.db === "object" && typeof resource.config.db.connection === "string") {
		resource.db = mongoose_module.createConnection(resource.config.db.connection);
		populate_child_connections(resource.children, resource.db);
	}

	for (key in description.models) {
		resource.addModel(key, description.models[key]);
	}

	return resource;
};

/**
 * Iterates through all dependent resources and applies a datbase connection if not pre-configured for one
 * @param  {[type]} children   [description]
 * @param  {[type]} connection [description]
 * @return {[type]}
 */
var populate_child_connections = function (children, connection) {
	var key = null;
	for (key in children) {
		if (typeof children[key].db === "undefined" || children[key].db === null) {
			children[key].db = connection;
			// we want to fill all empty children with the provided default connection
			// this might not  be the right path
			populate_child_connections(children[key].children, connection);
		}
	}
};

/**
 * [Resource description]
 * @param {[type]} name [description]
 */
var Resource = exports.Resource = function Resource (name) {
	this.name = name;
	this.config = {};
	this.router = new Router();
	this.models = {};
	this.resources = {};
	this.db = null;
	this.unmatched_route = null;
};

Resource.prototype.name = '';
Resource.prototype.uri = '';
Resource.prototype.config = null;
Resource.prototype.directory = '';
Resource.prototype.template_dir = '';
Resource.prototype.template = null;
Resource.prototype.router = null;
Resource.prototype.resources = null;
Resource.prototype.models = {};
Resource.prototype.db = null;

/**
 * [getResource description]
 * @param  {[type]} name [description]
 * @return {[type]}      [description]
 */
Resource.prototype.getResource = function (name) {
	return get_resource(name);
};

/**
 * [addRoutes description]
 * @param {[type]} match   [description]
 * @param {Object} routes  Mapping of Method => Function
 * @param {[type]} options [description]
 */
Resource.prototype.addRoute = function (match, route, keys) {
	this.router.addRoute(match, route, keys);
};

/**
 * [addChild description]
 * @param {[type]} resource [description]
 */
Resource.prototype.addChild = function (resource) {
	this.resources[resource.name] = resource;
}

/**
 * [addModel description]
 * @param {[type]} key   [description]
 * @param {[type]} model [description]
 */
Resource.prototype.addModel = function (key, model) {
	this.models[key] = model;
}

/**
 * [route description]
 * @param  {[type]} uri_bundle [description]
 * @param  {[type]} view       [description]
 * @return {[type]}
 */
Resource.prototype.request = function (uri_bundle, view) {
	var key = null;
	var template_dir = this.template_dir;
	var _self = this;

	// Allow direct urls for shorthand. Assume a GET request in this case
	if (typeof uri_bundle === "string") {
		uri_bundle = {
			uri : uri_bundle,
			method : 'GET'
		}
	}

	var route = this.getRoute(uri_bundle);

	if (!route) {
		// attempt each child, see if you can find a proper route
		for (key in this.resources) {
			route = this.resources[key].getRoute(uri_bundle);
			if (route) {
				// ensure that the proper template directory is used within the view
				template_dir = this.resources[key].template_dir;
				break;
			}
		}

		if (!route) {
			route = this.unmatched_route;
		}

		if (!route) {
			// todo: 404
			throw new Error('route not found :' + uri_bundle.uri + ' [' + this.name + ']');
		}
	}

	// If the template provided is actually a server response, we need to build the very first view
	if (view instanceof http_module.ServerResponse) {
		var response = view;

		view = new View();
		view.setRenderMode(accept_header_component.getRenderMode(uri_bundle.headers.accept, route.modes));
		// todo: not sure this will actually be desired due to view template precedence.
		//view.setTemplate(this.default_template);
		view.setResponse(response);
	}

	// If a template is set in the config, apply it to the current view and then provide a child view to the route
	if (typeof this.template === "function") {
		// We don't want to set the route resources directory, we will always create the template from the resource upon which request is called
		view.setDir(this.template_dir);
		var child = view.child('content');
		this.template(view);
		view = child;
	}

	// assume that we want to load templates directly from this route, no matter the data provided
	view.setDir(template_dir);

	// route, allowing this to point to the original resource, and provide some helper utils
	if (typeof route[uri_bundle.method] == "function") {
		process.nextTick(function() {
			route[uri_bundle.method].call(_self, uri_bundle, view);
		});
	} else if (typeof route['default'] === "function") {
		process.nextTick(function() {
			route.default.call(_self, uri_bundle, view);
		});
	} else {
		var keys = [];
		['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'].forEach(function (option) {
			if (typeof route[option] === "function") {
				keys.push(option)
			}
		});
		view.unsupportedMethod(keys);
	}
};

/**
 * [getRoute description]
 * @param  {[type]} uri_bundle [description]
 * @return {[type]}
 */
Resource.prototype.getRoute = function (uri_bundle) {

	if (uri_bundle.uri.indexOf(this.uri) === 0) {
		// strip the resource uri out of the request uri
		uri_bundle.uri = uri_bundle.uri.substring(this.uri.length);

		// what to do here? how do I route down into the children?
		// We don't have to pass the view, we can assume it's correct.
		// we just need to make sure that the promise returned is bound to the right resource
		var route = this.router.getRoute(uri_bundle);

		if (route) {
			return route;
		}
	}

	// No route was found
	return false;
};

/**
 *
 * @param router
 * @returns
 * @todo allow users to configure their resource to not take default template or
 *       js or css routes
 */
/*var applyTemplateRoutes = function (router, resource) {
	router.add(new RegExp('^/' + resource.name + '/template/(.+)$'), function (request, response, callback) {
		static_component.streamFile(resource.template_dir + request.GET['template'], response, {
			request : request,
			callback : callback
		});
	}, {keys : ['template']});

	router.add(new RegExp('^/' + resource.name + '/template/(.+)$'), function (request, response, callback) {
		static_component.loadFile(resource.template_dir + request.GET['template'], function (contents) {
			// todo replace this with a chunked renderer like mu?
			var template = hogan_module.compile(contents);
			response.ok(template.render(request.POST));
			callback();

		}, function (error) {
			response.error(error);
			callback();
		});
	}, {method : "POST", keys : ['template']});

	router.add(new RegExp('^/' + resource.name + '(\/.+\.js)$'), function (request, response, callback) {
		var filename = request.GET['file'].replace(/\.\./, '');
		static_component.streamFile(resource.directory + '/templates/js' + filename, response, {
			request : request,
			callback : callback
		});
	}, {keys : ['file']});

	router.add(new RegExp('^/' + resource.name + '(\/.+\.css)$'), function (request, response, callback) {
		var filename = request.GET['file'].replace(/\.\./, '');
		static_component.streamFile(resource.directory + '/templates/css' + filename, response, {
			request : request,
			callback : callback
		});
	}, {keys : ['file']});
};

var applyResourceRoutes = function (router, resource) {
	router.add(new RegExp('^/' + resource.name + '/(\d+)$'), function (request, response, callback) {
		resource.models[resource.name].get({id : request.GET['id']});
	}, {method : "GET", keys : ['id']});

	router.add(new RegExp('^/' + resource.name + '/$'), function (request, response, callback) {
		resource.models[resource.name].get({id : request.GET['id']});
	}, {method : "POST", keys : ['id']});

	router.add(new RegExp('^/' + resource.name + '/(\d+)$'), function (request, response, callback) {
		resource.models[resource.name].save({id : request.GET['id']});
	}, {method : "PUT", keys : ['id']});

	router.add(new RegExp('^/' + resource.name + '/(\d+)$'), function (request, response, callback) {
		resource.models[resource.name].delete({id : request.GET['id']});
	}, {method : "DELETE", keys : ['id']});
};*/
<|endoftext|>


<commit_before>/* AIDA Source Code */
/* Contributors located at: github.com/2nd47/CSC309-A4 */

var express = require('express');
var fs = require('fs');

var VIEWPATH = __dirname + '/views';

var walk = function(path, viewList, prepend) {
  if (!prepend) {
    prepend = "";
  }

  var views = fs.readdirSync(path);
  for (var i = 0; i < views.length; i++) {
    var file = views[i];
    var newPath = path + '/' + file;
    var stat = fs.statSync(newPath);

    if (stat.isFile()) {
      if (/(.*)\.(html$|ejs$)/.test(file)) {
        viewList.push(prepend + file.replace(/\.(html$|ejs$)/, ''));
      }
    } else if (stat.isDirectory() && file != 'partials') {
      walk(newPath, viewList, prepend + file + "/");
    }
  }

  return viewList;
};

var serveStaticPagesOnRequest = function(app, pageNames) {
  for (var i = 0; i < pageNames.length; i++) {
    var page = pageNames[i];
    app.get('/' + page, function(req, res) {
      res.sendFile(page, { root: './' });
    });
  }
};

module.exports = function(app, auth, user, project, job, search) {

  app.set('view engine', 'html');
  app.set('views', __dirname + '/views');

  // middleware that is specific to this router
  app.use(function timeLog(req, res, next) {
    console.log('Time: ', Date.now());
    next();
  });

  // change the request methods as required
  // refer to express documentation for more details

  // if logged in: feed; else: landing page
  app.get('/', auth.ensureAuthenticated, function (req, res, next) {
    res.sendFile('landing.html', { root: "./views" });
  });

  serveStaticPagesOnRequest(app, walk(VIEWPATH, []));

  app.post('/signup', auth.signup);
  app.post('/login', auth.login);
  app.get('/logout', auth.logout);

  // create a new job
  app.post('/job/new', job.createJob);

  app.get('/job/:job_id', function(req, res) {
    res.sendFile('contract', { root: "./views" });
  });

  // details of job with job_id
  app.get('/api/job/:job_id', job.getJob);

  // prompt to sign a job with job_id
  app.get('/job/:job_id/sign', job.signJob);

  // list of profiles of top ten followed users
  // if user is logged in, show their following users at the bottom
  app.get('/profile', user.getPopularUsers);

  app.get('/profile/:username', function(req, res) {
    res.sendFile('profile.html', { root: "./views" });
  });

  // details of people with username
  app.get('/api/profile/:username', user.getUser);

  // list of top ten projects
  // if user has followed projects, list them, too
  app.get('/project', project.getPopularProjects);

  // create a new project
  app.post('/project/new', project.createProject);

  app.get('/project/:username', function(req, res) {
    res.sendFile('profile.html', { root: "./views" });
  });

  // details of project with project_id
  app.get('/api/project/:project_id', project.getProject);

  // message inbox
  app.get('/inbox', user.getMessages);

  // chat message detail
  app.get('/inbox/:chat_id', user.getChat);

  // send message to person_id
  app.post('/inbox/:person_id/new', user.createMessage);

  // load all messages from a chat history
  app.get('/inbox/:chat_id/all', user.getChatHistory);

  // search page
  app.get('/search', search.getSearch);
};
<commit_msg>syntax error<commit_after>/* AIDA Source Code */
/* Contributors located at: github.com/2nd47/CSC309-A4 */

var express = require('express');
var fs = require('fs');

var VIEWPATH = __dirname + '/views';

var walk = function(path, viewList, prepend) {
  if (!prepend) {
    prepend = "";
  }

  var views = fs.readdirSync(path);
  for (var i = 0; i < views.length; i++) {
    var file = views[i];
    var newPath = path + '/' + file;
    var stat = fs.statSync(newPath);

    if (stat.isFile()) {
      if (/(.*)\.(html$|ejs$)/.test(file)) {
        viewList.push(prepend + file.replace(/\.(html$|ejs$)/, ''));
      }
    } else if (stat.isDirectory() && file != 'partials') {
      walk(newPath, viewList, prepend + file + "/");
    }
  }

  return viewList;
};

var serveStaticPagesOnRequest = function(app, pageNames) {
  for (var i = 0; i < pageNames.length; i++) {
    var page = pageNames[i];
    app.get('/' + page, function(req, res) {
      res.sendFile(page, { root: './' });
    });
  }
};

module.exports = function(app, auth, user, project, job, search) {

  app.set('view engine', 'html');
  app.set('views', __dirname + '/views');

  // middleware that is specific to this router
  app.use(function timeLog(req, res, next) {
    console.log('Time: ', Date.now());
    next();
  });

  // change the request methods as required
  // refer to express documentation for more details

  // if logged in: feed; else: landing page
  app.get('/', auth.ensureAuthenticated, function (req, res, next) {
    res.sendFile('landing.html', { root: "./views" });
  });

  //serveStaticPagesOnRequest(app, walk(VIEWPATH, []));

  app.post('/signup', auth.signup);
  app.post('/login', auth.login);
  app.get('/logout', auth.logout);

  // create a new job
  app.post('/job/new', job.createJob);

  app.get('/job/:job_id', function(req, res) {
    res.sendFile('contract.html', { root: "./views" });
  });

  // details of job with job_id
  app.get('/api/job/:job_id', job.getJob);

  // prompt to sign a job with job_id
  app.get('/job/:job_id/sign', job.signJob);

  // list of profiles of top ten followed users
  // if user is logged in, show their following users at the bottom
  app.get('/profile', user.getPopularUsers);

  app.get('/profile/:username', function(req, res) {
    res.sendFile('profile.html', { root: "./views" });
  });

  // details of people with username
  app.get('/api/profile/:username', user.getUser);

  // list of top ten projects
  // if user has followed projects, list them, too
  app.get('/project', project.getPopularProjects);

  // create a new project
  app.post('/project/new', project.createProject);

  app.get('/project/:username', function(req, res) {
    res.sendFile('profile.html', { root: "./views" });
  });

  // details of project with project_id
  app.get('/api/project/:project_id', project.getProject);

  // message inbox
  app.get('/inbox', user.getMessages);

  // chat message detail
  app.get('/inbox/:chat_id', user.getChat);

  // send message to person_id
  app.post('/inbox/:person_id/new', user.createMessage);

  // load all messages from a chat history
  app.get('/inbox/:chat_id/all', user.getChatHistory);

  // search page
  app.get('/search', search.getSearch);
};
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const { pick, pipe, has, prop, isNil, cloneDeep, isArray } = require('lodash/fp');
const {
  isRelationalAttribute,
  getVisibleAttributes,
  isMediaAttribute,
} = require('strapi-utils').contentTypes;
const { getService } = require('../utils');

const isLocalized = modelOrAttribute => {
  return prop('pluginOptions.i18n.localized', modelOrAttribute) === true;
};

const getValidLocale = async locale => {
  const localesService = getService('locales');

  if (isNil(locale)) {
    return localesService.getDefaultLocale();
  }

  const foundLocale = await localesService.findByCode(locale);
  if (!foundLocale) {
    throw new Error('Locale not found');
  }

  return locale;
};

/**
 * Get the related entity used for entity creation
 * @param {Object} relatedEntity related entity
 * @returns {id[]} related entity
 */
const getNewLocalizationsFrom = async relatedEntity => {
  if (relatedEntity) {
    return [relatedEntity.id, ...relatedEntity.localizations.map(prop('id'))];
  }

  return [];
};

/**
 * Get the related entity used for entity creation
 * @param {id} relatedEntityId related entity id
 * @param {string} model corresponding model
 * @param {string} locale locale of the entity to create
 * @returns {Object} related entity
 */
const getAndValidateRelatedEntity = async (relatedEntityId, model, locale) => {
  const { kind } = strapi.getModel(model);
  let relatedEntity;

  if (kind === 'singleType') {
    relatedEntity = await strapi.query(model).findOne({});
  } else if (relatedEntityId) {
    relatedEntity = await strapi.query(model).findOne({ id: relatedEntityId });
  }

  if (relatedEntityId && !relatedEntity) {
    throw new Error("The related entity doesn't exist");
  }

  if (
    relatedEntity &&
    (relatedEntity.locale === locale ||
      relatedEntity.localizations.map(prop('locale')).includes(locale))
  ) {
    throw new Error('The entity already exists in this locale');
  }

  return relatedEntity;
};

/**
 * Returns whether an attribute is localized or not
 * @param {*} attribute
 * @returns
 */
const isLocalizedAttribute = (model, attributeName) => {
  const attribute = model.attributes[attributeName];

  return (
    isLocalized(attribute) || (isRelationalAttribute(attribute) && !isMediaAttribute(attribute))
  );
};

/**
 * Returns the list of attribute names that are not localized
 * @param {object} model
 * @returns {string[]}
 */
const getNonLocalizedAttributes = model => {
  return getVisibleAttributes(model).filter(
    attributeName => !isLocalizedAttribute(model, attributeName)
  );
};

const removeId = value => {
  if (typeof value === 'object' && has('id', value)) {
    delete value.id;
  }
};

const removeIds = model => entry => removeIdsMut(model, cloneDeep(entry));

const removeIdsMut = (model, entry) => {
  if (isNil(entry)) {
    return entry;
  }

  removeId(entry);

  _.forEach(model.attributes, (attr, attrName) => {
    const value = entry[attrName];
    if (attr.type === 'dynamiczone' && isArray(value)) {
      value.forEach(compo => {
        if (has('__component', compo)) {
          const model = strapi.components[compo.__component];
          removeIdsMut(model, compo);
        }
      });
    } else if (attr.type === 'component') {
      const [model] = strapi.db.getModelsByAttribute(attr);
      if (isArray(value)) {
        value.forEach(compo => removeIdsMut(model, compo));
      } else {
        removeIdsMut(model, value);
      }
    }
  });

  return entry;
};

/**
 * Returns a copy of an entry picking only its non localized attributes
 * @param {object} model
 * @param {object} entry
 * @returns {object}
 */
const copyNonLocalizedAttributes = (model, entry) => {
  const nonLocalizedAttributes = getNonLocalizedAttributes(model);

  const res = pipe(pick(nonLocalizedAttributes), removeIds(model))(entry);
  return res;
};

/**
 * Returns the list of attribute names that are localized
 * @param {object} model
 * @returns {string[]}
 */
const getLocalizedAttributes = model => {
  return getVisibleAttributes(model).filter(attributeName =>
    isLocalizedAttribute(model, attributeName)
  );
};

/**
 * Fill non localized fields of an entry if there are nil
 * @param {Object} entry entry to fill
 * @param {Object} relatedEntry values used to fill
 * @param {Object} options
 * @param {Object} options.model corresponding model
 */
const fillNonLocalizedAttributes = (entry, relatedEntry, { model }) => {
  if (isNil(relatedEntry)) {
    return;
  }

  const modelDef = strapi.getModel(model);
  const relatedEntryCopy = copyNonLocalizedAttributes(modelDef, relatedEntry);

  _.forEach(relatedEntryCopy, (value, field) => {
    if (isNil(entry[field])) {
      entry[field] = value;
    }
  });
};

module.exports = {
  isLocalized,
  getValidLocale,
  getNewLocalizationsFrom,
  getLocalizedAttributes,
  getNonLocalizedAttributes,
  copyNonLocalizedAttributes,
  getAndValidateRelatedEntity,
  fillNonLocalizedAttributes,
};
<commit_msg>Update content-types.js<commit_after>'use strict';

const _ = require('lodash');
const { pick, pipe, has, prop, isNil, cloneDeep, isArray } = require('lodash/fp');
const {
  isRelationalAttribute,
  getVisibleAttributes,
  isMediaAttribute,
} = require('strapi-utils').contentTypes;
const { getService } = require('../utils');

const isLocalized = modelOrAttribute => {
  return prop('pluginOptions.i18n.localized', modelOrAttribute) === true;
};

const getValidLocale = async locale => {
  const localesService = getService('locales');

  if (isNil(locale)) {
    return localesService.getDefaultLocale();
  }

  const foundLocale = await localesService.findByCode(locale);
  if (!foundLocale) {
    throw new Error('Locale not found');
  }

  return locale;
};

/**
 * Get the related entity used for entity creation
 * @param {Object} relatedEntity related entity
 * @returns {id[]} related entity
 */
const getNewLocalizationsFrom = async relatedEntity => {
  if (relatedEntity) {
    return [relatedEntity.id, ...relatedEntity.localizations.map(prop('id'))];
  }

  return [];
};

/**
 * Get the related entity used for entity creation
 * @param {id} relatedEntityId related entity id
 * @param {string} model corresponding model
 * @param {string} locale locale of the entity to create
 * @returns {Object} related entity
 */
const getAndValidateRelatedEntity = async (relatedEntityId, model, locale) => {
  const { kind } = strapi.getModel(model);
  let relatedEntity;

  if (kind === 'singleType') {
    relatedEntity = await strapi.query(model).findOne({});
  } else if (relatedEntityId) {
    relatedEntity = await strapi.query(model).findOne({ id: relatedEntityId });
  }

  if (relatedEntityId && !relatedEntity) {
    throw new Error("The related entity doesn't exist");
  }

  if (
    relatedEntity &&
    (relatedEntity.locale === locale ||
      relatedEntity.localizations.map(prop('locale')).includes(locale))
  ) {
    throw new Error('The entity already exists in this locale');
  }

  return relatedEntity;
};

/**
 * Returns whether an attribute is localized or not
 * @param {*} attribute
 * @returns
 */
const isLocalizedAttribute = (model, attributeName) => {
  const attribute = model.attributes[attributeName];

  return (
    isLocalized(attribute) || (isRelationalAttribute(attribute) && !isMediaAttribute(attribute))
  );
};

/**
 * Returns the list of attribute names that are not localized
 * @param {object} model
 * @returns {string[]}
 */
const getNonLocalizedAttributes = model => {
  return getVisibleAttributes(model).filter(
    attributeName => !isLocalizedAttribute(model, attributeName)
  );
};

const removeId = value => {
  if (typeof value === 'object' && has('id', value)) {
    delete value.id;
  }
};

const removeIds = model => entry => removeIdsMut(model, cloneDeep(entry));

const removeIdsMut = (model, entry) => {
  if (isNil(entry)) {
    return entry;
  }

  removeId(entry);

  _.forEach(model.attributes, (attr, attrName) => {
    const value = entry[attrName];
    if (attr.type === 'dynamiczone' && isArray(value)) {
      value.forEach(compo => {
        if (has('__component', compo)) {
          const model = strapi.components[compo.__component];
          removeIdsMut(model, compo);
        }
      });
    } else if (attr.type === 'component') {
      const [model] = strapi.db.getModelsByAttribute(attr);
      if (isArray(value)) {
        value.forEach(compo => removeIdsMut(model, compo));
      } else {
        removeIdsMut(model, value);
      }
    }
  });

  return entry;
};

/**
 * Returns a copy of an entry picking only its non localized attributes
 * @param {object} model
 * @param {object} entry
 * @returns {object}
 */
const copyNonLocalizedAttributes = (model, entry) => {
  const nonLocalizedAttributes = getNonLocalizedAttributes(model);

  return pipe(pick(nonLocalizedAttributes), removeIds(model))(entry);
};

/**
 * Returns the list of attribute names that are localized
 * @param {object} model
 * @returns {string[]}
 */
const getLocalizedAttributes = model => {
  return getVisibleAttributes(model).filter(attributeName =>
    isLocalizedAttribute(model, attributeName)
  );
};

/**
 * Fill non localized fields of an entry if there are nil
 * @param {Object} entry entry to fill
 * @param {Object} relatedEntry values used to fill
 * @param {Object} options
 * @param {Object} options.model corresponding model
 */
const fillNonLocalizedAttributes = (entry, relatedEntry, { model }) => {
  if (isNil(relatedEntry)) {
    return;
  }

  const modelDef = strapi.getModel(model);
  const relatedEntryCopy = copyNonLocalizedAttributes(modelDef, relatedEntry);

  _.forEach(relatedEntryCopy, (value, field) => {
    if (isNil(entry[field])) {
      entry[field] = value;
    }
  });
};

module.exports = {
  isLocalized,
  getValidLocale,
  getNewLocalizationsFrom,
  getLocalizedAttributes,
  getNonLocalizedAttributes,
  copyNonLocalizedAttributes,
  getAndValidateRelatedEntity,
  fillNonLocalizedAttributes,
};
<|endoftext|>