<commit_before>APP_KEY = "j2q6r6vvd5g8ulm"

document.addEventListener('DOMContentLoaded', function () {
  $('#friend_link').on('keyup', function(e) {
    if (e.keyCode === 13) {
    	clickHandler();
    }
  });
});

// Send message to background script
function clickHandler() {
  var linkaddress = document.getElementById("friend_link")
  var linkcontent = linkaddress.value
  // Read contents as string from given link.
  getContent(linkcontent);
}

function getContent(theUrl){
  $.getJSON(
    theUrl,
    function(data) {
      setKey(data["uid"],data["public"]);
    }
  );
}

function setKey(uid,publickey){
  var person = {"uid": uid, "public": publickey};

  var new_friendlist = {'data':[]};
  var prev_friendlist = localStorage.getItem("friendlist");

  if (prev_friendlist == null){
    // if prev freindlist is null, then we add a new array.
  	new_friendlist.data.push(JSON.stringify(person));
    localStorage.setItem("friendlist", JSON.stringify(new_friendlist));

  }else {
    // if there's list already, then we get the localstorage item and add to it, store the new list.
    console.log(typeof prev_friendlist);
    //new_friendlist = prev_friendlist.push(JSON.stringify(person));

  }
  //console.log(new_friendlist.data.length);
  console.log(new_friendlist.data);
}<commit_msg>setKey implemented but don't know if it's working<commit_after>APP_KEY = "j2q6r6vvd5g8ulm"

document.addEventListener('DOMContentLoaded', function () {
  $('#friend_link').on('keyup', function(e) {
    if (e.keyCode === 13) {
    	clickHandler();
    }
  });
});

// Send message to background script
function clickHandler() {
  var linkaddress = document.getElementById("friend_link")
  var linkcontent = linkaddress.value
  // Read contents as string from given link.
  getContent(linkcontent);
}

function getContent(theUrl){
  $.getJSON(
    theUrl,
    function(data) {
      setKey(data["uid"],data["public"]);
    }
  );
}

function setKey(uid,publickey){

  var friendList;
  var person = {"uid": uid, "public": publickey};
  var uidExists = false;

  // Create object representing friendList in localStorage
  if (localStorage.getItem("friendList") == null) {
    friendList = [];
  }
  else {
    friendList = JSON.parse(localStorage.getItem("friendList"));
  }

  // First if uid exists in localStorage
  for (var i in friendList) {
    if (friendList[i]["uid"] == uid) {
      uidExists == true;
      break;
    }
  }

  if (! uidExists) {
    friendList.push(person);
  }

  // Now synchronize
  localStorage.setItem("friendList", JSON.stringify(friendList));

  // if (prev_friendlist == null){
  //   // if prev freindlist is null, then we add a new array.
  // 	new_friendlist.data.push(JSON.stringify(person));
  //   localStorage.setItem("friendlist", JSON.stringify(new_friendlist));

  // }else {
  //   // if there's list already, then we get the localstorage item and add to it, store the new list.
  //   console.log(typeof prev_friendlist);
  //   //new_friendlist = prev_friendlist.push(JSON.stringify(person));

  // }
  // //console.log(new_friendlist.data.length);
  // console.log(new_friendlist.data);
}<|endoftext|>


<commit_before>var http = require("http");
var https = require("https");
var util = require("util");
var cheerio = require("cheerio");
var async = require("async");

var contestid;

var usercollection;
var collection;

function parseUserContest(code, funcproblem, callback, trycount)
{
	if (trycount < 0)
	{
		callback(Error("Error in parsing problems from contest, Exceeded try counts " + code));
		return;
	}

	var url = "https://www.codechef.com/api/contests/" + code;
	execHttps(url, function(source)
	{
		if (source.indexOf('"status":"success"') == -1)
		{
			parseUserContest(code, funcproblem, callback, trycount-1);
			return;
		}

		var obj = JSON.parse(source);

		async.each(obj.problems, funcproblem, function(err)
		{
			callback(err);
		});

	}, 3);
}

function parseStatusPage(contestid, problemid, pageno, callback, trycount)
{
	/*
	if (trycount < 0)
	{
		callback(Error("Error in parsing status page, Exceeded try counts " + contestid + " " + problemid + " " + pageno));
		return;
	}
	*/

	var url = util.format('https://www.codechef.com/%s/status/%s?page=%s&sort_by=Date%2FTime&sorting_order=asc', contestid, problemid, pageno);;
	execHttps(url, function(source)
	{
		if (source.indexOf("pageinfo") == -1)
		{
			parseStatusPage(contestid, problemid, pageno, callback, trycount-1);
			return;
		}

		var $ = cheerio.load(source);
		var lastpage = parseInt($('.pageinfo').text().split(' ')[2]) - 1;

		$('table[class="dataTable"]>tbody>tr>td>a').each(function(i, data)
		{
			var username = $(data).attr('title');
			try
			{
				usercollection.insert({contestid: contestid, user: username});
			}
			catch (ex)
			{
			}
		});

		collection.update({problemid: problemid}, {$set: {pagedone: pageno}}, {upsert: true});

		console.log("Current page", pageno, lastpage, url, $('.pageinfo').text());

		if (pageno < lastpage)
		{
			parseStatusPage(contestid, problemid, pageno+1, callback, 2);
		}
		else
		{
			callback();
		}
	}, 4);
}

require("./helper.js")();

module.exports = function(nextcall)
{

	var contestIDS = [];

	MongoClient.connect(mongourl, function(err, db)
	{
		if (err)
		{
			throw err;
		}

		collection = db.collection("status");
		usercollection = db.collection("user");

		usercollection.createIndex({contestid: 1, user: 1}, {unique: true });

		/*
		bad way
		if (process.argv.length >= 4 && process.argv[3] == 'delete')
		{
			//reset before use
			usercollection.deleteMany({});
			collection.deleteMany({});
		}
		*/

		var processContests = function()
		{
			async.eachSeries(contestIDS, function(ciid, callback)
			{
				contestid = ciid;

				parseUserContest(contestid, function(problem, callback)
				{
					var problemid = problem.code;
					collection.findOne({problemid: problemid}, function(err, obj)
					{
						var lastpage = (obj !== null ? obj.pagedone : 0);
						console.log(problemid, lastpage);
						parseStatusPage(contestid, problemid, lastpage, callback, 9);
					});
				},
				function(err)
				{
					if (err)
					{
						console.log("Error in parsing", contestid, err);
					}
					else
					{
						console.log("Completed parsing", contestid);
					}

					callback(err);
				}, 9);
			},
			function (err)
			{
				if (err)
				{
					console.log("Error", err);
					throw err;
				}

				db.close();
				console.log("Completed ALL");

				setImmediate(nextcall);
			});
		};

		db.collection("checklist").find({}).toArray(function(err, cdatas)
		{
			cdatas.forEach(function(x)
			{
				contestIDS.push(x.contest);
			});

			processContests();
		});
	});

};<commit_msg>removed retry<commit_after>var http = require("http");
var https = require("https");
var util = require("util");
var cheerio = require("cheerio");
var async = require("async");

var contestid;

var usercollection;
var collection;

function parseUserContest(code, funcproblem, callback, trycount)
{
	if (trycount < 0)
	{
		callback(Error("Error in parsing problems from contest, Exceeded try counts " + code));
		return;
	}

	var url = "https://www.codechef.com/api/contests/" + code;
	execHttps(url, function(source)
	{
		if (source.indexOf('"status":"success"') == -1)
		{
			parseUserContest(code, funcproblem, callback, trycount-1);
			return;
		}

		var obj = JSON.parse(source);

		async.each(obj.problems, funcproblem, function(err)
		{
			callback(err);
		});

	}, 3);
}

function parseStatusPage(contestid, problemid, pageno, callback, trycount)
{
	/*
	if (trycount < 0)
	{
		callback(Error("Error in parsing status page, Exceeded try counts " + contestid + " " + problemid + " " + pageno));
		return;
	}
	*/

	var url = util.format('https://www.codechef.com/%s/status/%s?page=%s&sort_by=Date%2FTime&sorting_order=asc', contestid, problemid, pageno);;
	execHttps(url, function(source)
	{
		var $ = cheerio.load(source);
		var lastpage = parseInt($('.pageinfo').text().split(' ')[2]) - 1;

		if (source.indexOf("pageinfo") == -1)
		{
			//parseStatusPage(contestid, problemid, pageno, callback, trycount-1);
			//return;
			lastpage = pageno;
		}

		$('table[class="dataTable"]>tbody>tr>td>a').each(function(i, data)
		{
			var username = $(data).attr('title');
			try
			{
				usercollection.insert({contestid: contestid, user: username});
			}
			catch (ex)
			{
			}
		});

		collection.update({problemid: problemid}, {$set: {pagedone: pageno}}, {upsert: true});

		console.log("Current page", pageno, lastpage, url, $('.pageinfo').text());

		if (pageno < lastpage)
		{
			parseStatusPage(contestid, problemid, pageno+1, callback, 2);
		}
		else
		{
			callback();
		}
	}, 4);
}

require("./helper.js")();

module.exports = function(nextcall)
{

	var contestIDS = [];

	MongoClient.connect(mongourl, function(err, db)
	{
		if (err)
		{
			throw err;
		}

		collection = db.collection("status");
		usercollection = db.collection("user");

		usercollection.createIndex({contestid: 1, user: 1}, {unique: true });

		/*
		bad way
		if (process.argv.length >= 4 && process.argv[3] == 'delete')
		{
			//reset before use
			usercollection.deleteMany({});
			collection.deleteMany({});
		}
		*/

		var processContests = function()
		{
			async.eachSeries(contestIDS, function(ciid, callback)
			{
				contestid = ciid;

				parseUserContest(contestid, function(problem, callback)
				{
					var problemid = problem.code;
					collection.findOne({problemid: problemid}, function(err, obj)
					{
						var lastpage = (obj !== null ? obj.pagedone : 0);
						console.log(problemid, lastpage);
						parseStatusPage(contestid, problemid, lastpage, callback, 9);
					});
				},
				function(err)
				{
					if (err)
					{
						console.log("Error in parsing", contestid, err);
					}
					else
					{
						console.log("Completed parsing", contestid);
					}

					callback(err);
				}, 9);
			},
			function (err)
			{
				if (err)
				{
					console.log("Error", err);
					throw err;
				}

				db.close();
				console.log("Completed ALL");

				setImmediate(nextcall);
			});
		};

		db.collection("checklist").find({}).toArray(function(err, cdatas)
		{
			cdatas.forEach(function(x)
			{
				contestIDS.push(x.contest);
			});

			processContests();
		});
	});

};<|endoftext|>


<commit_before>import _ from 'underscore';
import async from 'async';
import Field from '../Field';
import React from 'react';
import Select from 'react-select';
import xhr from 'xhr';
import { Button, FormInput } from 'elemental';

module.exports = Field.create({

	displayName: 'RelationshipField',

	shouldCollapse () {
		if (this.props.many) {
			// many:true relationships have an Array for a value
			return this.props.collapse && !this.props.value.length;
		}
		return this.props.collapse && !this.props.value;
	},

	/*
	buildFilters () {
		var filters = {};

		_.each(this.props.filters, function(value, key) {
			if(_.isString(value) && value[0] == ':') {//eslint-disable-line eqeqeq
				var fieldName = value.slice(1);

				var val = this.props.values[fieldName];
				if (val) {
					filters[key] = val;
					return;
				}

				// check if filtering by id and item was already saved
				if (fieldName === ':_id' && Keystone.item) {
					filters[key] = Keystone.item.id;
					return;
				}
			} else {
				filters[key] = value;
			}
		}, this);

		var parts = [];

		_.each(filters, function (val, key) {
			parts.push('filters[' + key + ']=' + encodeURIComponent(val));
		});

		return parts.join('&');
	},
	*/

	getOptions (input, callback) {
		// TODO: Implement filters
		console.log('INPUT:', input);
		xhr({
			url: '/keystone/api/' + this.props.refList.path + '?search=' + input,
			responseType: 'json',
		}, (err, resp, body) => {
			console.log(body);
			// done(err, body);
		});
		/*
		async.map(values, (id, done) => {
			xhr({
				url: '/keystone/api/' + this.props.refList.path + '/' + id + '?basic',
				responseType: 'json',
			}, (err, resp, body) => {
				console.log(body);
				done(err, body);
			});
		}, (err, results) => {
			// TODO: handle err
			this.setState({
				ready: true,
				expandedValues: results,
			});
		});
		*/
		/*
		superagent
			.get('/keystone/api/' + this.props.refList.path + '/autocomplete?' + this.buildOptionQuery(input))
			.set('Accept', 'application/json')
			.end(function (err, res) {
				if (err) throw err;

				var data = res.body;

				callback(null, {
					options: data.items.map(function (item) {
						return {
							value: item.id,
							label: item.name
						};
					}),
					complete: data.total === data.items.length
				});
			});
		*/
	},

	updateValue (value) {

	},

	renderSelect (noedit) {
		return (
			<Select
				multi={this.props.many}
				disabled={noedit}
				onChange={this.updateValue}
				name={this.props.path}
				value={this.props.value}
			/>
		);
	},

	renderValue () {
		return this.renderSelect(true);
	},

	renderField () {

		// let button = (!this.props.many && this.props.value) ? (
		// 	<Button key="relational-button" type="link" href={'/keystone/' + this.props.refList.path + '/' + this.props.value} className="keystone-relational-button" title={'Go to "' + this.state.expandedValues[0].label + '"'}>
		//
		// 	</Button>
		// ) : null;

		return this.renderSelect();
	}

});
<commit_msg>Relationship fields loading initial values correctly<commit_after>import async from 'async';
import Field from '../Field';
import React from 'react';
import Select from 'react-select';
import xhr from 'xhr';
import { Button, FormInput } from 'elemental';

module.exports = Field.create({

	displayName: 'RelationshipField',

	getInitialState () {
		return {
			value: null,
		};
	},

	componentDidMount () {
		this._valuesCache = {};
		this.onValueChanged();
	},

	shouldCollapse () {
		if (this.props.many) {
			// many:true relationships have an Array for a value
			return this.props.collapse && !this.props.value.length;
		}
		return this.props.collapse && !this.props.value;
	},

	buildFilters () {
		var filters = {};

		_.each(this.props.filters, function(value, key) {
			if(_.isString(value) && value[0] == ':') {//eslint-disable-line eqeqeq
				var fieldName = value.slice(1);

				var val = this.props.values[fieldName];
				if (val) {
					filters[key] = val;
					return;
				}

				// check if filtering by id and item was already saved
				if (fieldName === ':_id' && Keystone.item) {
					filters[key] = Keystone.item.id;
					return;
				}
			} else {
				filters[key] = value;
			}
		}, this);

		var parts = [];

		_.each(filters, function (val, key) {
			parts.push('filters[' + key + ']=' + encodeURIComponent(val));
		});

		return parts.join('&');
	},

	onValueChanged () {
		let values = this.props.many ? this.props.value : [this.props.value];
		this.setState({
			loading: true,
		});
		async.map(values, (value, done) => {
			if (this._valuesCache[value]) return done(null, this._valuesCache[value]);
			xhr({
				url: '/keystone/api/' + this.props.refList.path + '/' + value + '?basic',
				responseType: 'json',
			}, (err, resp, data) => {
				if (err || !data) return done(err);
				this._valuesCache[value] = data;
				done(err, data);
			});
		}, (err, expanded) => {
			this.setState({
				loading: false,
				value: this.props.many ? expanded : expanded[0],
			});
		});
	},

	getOptions (input, callback) {
		// TODO: Implement filters
		console.log('INPUT:', input);
		xhr({
			url: '/keystone/api/' + this.props.refList.path + '?search=' + input,
			responseType: 'json',
		}, (err, resp, body) => {
			console.log(body);
			return callback(null, []);
			// async.map(values, (id, done) => {
			// 	xhr({
			// 		url: '/keystone/api/' + this.props.refList.path + '/' + id + '?basic',
			// 		responseType: 'json',
			// 	}, (err, resp, body) => {
			// 		console.log(body);
			// 		done(err, body);
			// 	});
			// }, (err, results) => {
			//
			// });
		});
	},

	renderSelect (noedit) {
		return (
			<Select
				multi={this.props.many}
				disabled={noedit}
				labelKey="name"
				name={this.props.path}
				onChange={this.valueChanged}
				value={this.state.value}
				valueKey="id"
			/>
		);
	},

	renderValue () {
		return this.renderSelect(true);
	},

	renderField () {

		// let button = (!this.props.many && this.props.value) ? (
		// 	<Button key="relational-button" type="link" href={'/keystone/' + this.props.refList.path + '/' + this.props.value} className="keystone-relational-button" title={'Go to "' + this.state.expandedValues[0].label + '"'}>
		//
		// 	</Button>
		// ) : null;

		return this.renderSelect();
	}

});
<|endoftext|>


<commit_before>$(document).ready(init);

function init() {
	$("a.image").click(loadImage);
	$("a.next").click(loadImage);
	$("a.previous").click(loadImage);
}

function loadImage() {
	var url = $(this).attr("href");

	$.ajax({
		url:		url,
		data:		{ ajax: true },
		success:	renderResponse,
	});

	return false;
}

function renderResponse(response) {
	var request = this.url.replace(/^.*\/|#[^#]*|\?[^\?]*$/g, '');

	$("div.content").html(response);
	$("div.image").each(function() {
		$(this).removeClass("selected");

		var url = $(this).children("a").attr("href").replace(/^.*\/|#[^#]*|\?[^\?]*$/g, '');
		if(request == url) {
			$(this).addClass("selected");
			var scroll = $(this).parents("div.sidebar");
			var height = scroll.height()/2;
			var pos = $(this).position().top;

			if(scroll != undefined && pos > height) scroll.animate({scrollTop: scroll.scrollTop() + (pos - height)}, 500);
			if(scroll != undefined && pos < height) scroll.animate({scrollTop: scroll.scrollTop() - (height - pos)}, 500);
		}
	});

	$("a.next").click(loadImage);
	$("a.previous").click(loadImage);
}<commit_msg>Better positioning.<commit_after>$(document).ready(init);

function init() {
	$("a.image").click(loadImage);
	$("a.next").click(loadImage);
	$("a.previous").click(loadImage);
}

function loadImage() {
	var url = $(this).attr("href");

	$.ajax({
		url:		url,
		data:		{ ajax: true },
		success:	renderResponse,
	});

	return false;
}

function renderResponse(response) {
	var request = this.url.replace(/^.*\/|#[^#]*|\?[^\?]*$/g, '');

	$("div.content").html(response);
	$("div.image").each(function() {
		$(this).removeClass("selected");

		var url = $(this).children("a").attr("href").replace(/^.*\/|#[^#]*|\?[^\?]*$/g, '');
		if(request == url) {
			$(this).addClass("selected");
			var scroll = $(this).parents("div.sidebar");
			var height = scroll.height()/2;
			var pos = $(this).position().top + $(this).height()/2;

			if(scroll != undefined && pos > height) scroll.animate({scrollTop: scroll.scrollTop() + (pos - height)}, 500);
			if(scroll != undefined && pos < height) scroll.animate({scrollTop: scroll.scrollTop() - (height - pos)}, 500);
		}
	});

	$("a.next").click(loadImage);
	$("a.previous").click(loadImage);
}<|endoftext|>


<commit_before>import parse from 'parse-import-es6'
import parsePackageName from 'parse-package-name'

export default (code, scripts) => {
  if (!/\bimport\b/.test(code)) return code

  for (const [index, item] of parse(code).entries()) {
    const moduleName = `__npm_module_${index}`
    const pkg = parsePackageName(item.moduleSpecifier)
    scripts.push({
      module: pkg.name,
      path: pkg.path ? `/${pkg.path}` : '',
      version: pkg.version || 'latest',
      name: moduleName
    })
    let replacement = '\n'
    if (item.importedDefaultBinding) {
      replacement += `var ${item.importedDefaultBinding} = ${moduleName}.default || ${moduleName};\n`
    }
    if (item.namedImports && item.namedImports.length > 0) {
      for (const namedImport of item.namedImports) {
        const names = namedImport.split(/\s*as\s*/)
        const name = names[1] || names[0]
        replacement += `var ${name} = ${moduleName}.${name};\n`
      }
    }
    if (item.nameSpaceImport) {
      const name = /as\s+(.+)/.exec(item.nameSpaceImport)[1]
      replacement += `var ${name} = ${moduleName};\n`
    }
    code = code.slice(0, item.range.start) + replacement + code.slice(item.range.end)
  }

  return code
}
<commit_msg>fix code replacement<commit_after>import parse from 'parse-import-es6'
import parsePackageName from 'parse-package-name'

export default (code, scripts) => {
  if (!/\bimport\b/.test(code)) return code

  const replacements = []
  for (const [index, item] of parse(code).entries()) {
    const moduleName = `__npm_module_${index}`
    const pkg = parsePackageName(item.moduleSpecifier)
    scripts.push({
      module: pkg.name,
      path: pkg.path ? `/${pkg.path}` : '',
      version: pkg.version || 'latest',
      name: moduleName
    })
    let replacement = '\n'
    if (item.importedDefaultBinding) {
      replacement += `var ${item.importedDefaultBinding} = ${moduleName}.default || ${moduleName};\n`
    }
    if (item.namedImports && item.namedImports.length > 0) {
      for (const namedImport of item.namedImports) {
        const names = namedImport.split(/\s*as\s*/)
        const name = names[1] || names[0]
        replacement += `var ${name} = ${moduleName}.${name};\n`
      }
    }
    if (item.nameSpaceImport) {
      const name = /as\s+(.+)/.exec(item.nameSpaceImport)[1]
      replacement += `var ${name} = ${moduleName};\n`
    }
    if (replacement) {
      replacements.push(replacement)
    }
    code = code.slice(0, item.range.start) + ' '.repeat(item.range.end - item.range.start) + code.slice(item.range.end)
  }

  if (replacements.length > 0) {
    code = replacements.join('\n') + code
  }

  return code
}
<|endoftext|>


<commit_before>var QueryCommand = require('./commands/query_command').QueryCommand,
  GetMoreCommand = require('./commands/get_more_command').GetMoreCommand,
  KillCursorCommand = require('./commands/kill_cursor_command').KillCursorCommand,
  Integer = require('./goog/math/integer').Integer,
  Long = require('./goog/math/long').Long;

/**
  Handles all the operations on query result using find
**/
var Cursor = exports.Cursor = function(db, collection, selector, fields, skip, limit, sort, hint, explain, snapshot, timeout) {
  this.db = db;
  this.collection = collection;
  this.selector = selector;
  this.fields = fields;
  this.skipValue = skip == null ? 0 : skip;
  this.limitValue = limit == null ? 0 : limit;
  this.sortValue = sort;
  this.hint = hint;
  this.explainValue = explain;
  this.snapshot = snapshot;
  this.timeout = timeout;
  this.numberOfReturned = 0;
  this.totalNumberOfRecords = 0;
  this.items = [];
  this.cursorId = Long.fromInt(0);
  // Keeps track of location of the cursor
  this.index = 0;
  // State variables for the cursor
  this.state = Cursor.INIT;
  // Kepp track of the current query run
  this.queryRun = false;
};

  // Return an array of documents
Cursor.prototype.toArray = function(callback) {
  var self = this;

  try {
    if(self.state != Cursor.CLOSED) {
      self.fetchAllRecords(function(err, items) {
        self.state = Cursor.CLOSED;
        // Save object in internal cache that can be used for iterating and set index
        // to first object
        self.index = 0;
        self.items = items;
        callback(null, items);
      });
    } else {
      callback(new Error("Cursor is closed"), null);
    }
  } catch(err) {
    callback(new Error(err.toString()), null);
  }
};

// For Each materialized the objects at need
Cursor.prototype.each = function(callback) {
  var self = this;

  if(this.state != Cursor.CLOSED || (this.index <= this.items.length)) {
    //FIX: stack overflow (on deep callback) (cred: https://github.com/limp/node-mongodb-native/commit/27da7e4b2af02035847f262b29837a94bbbf6ce2)
    process.nextTick(function(){
        // Fetch the next object until there is no more objects
        self.nextObject(function(err, item) {
          if(item != null) {
            callback(null, item);
            self.each(callback);
          } else {
            self.state = Cursor.CLOSED;
            callback(null, null);
          }
        });
    });
  } else {
    callback(new Error("Cursor is closed"), null);
  }
};

Cursor.prototype.count = function(callback) {
  this.collection.count(this.selector, callback);
};

Cursor.prototype.sort = function(keyOrList, direction, callback) {
  callback = callback || function(){};
  if(typeof direction === "function") { callback = direction; direction = null; }
  if(this.queryRun == true || this.state == Cursor.CLOSED) {
    callback(new Error("Cursor is closed"), null);
  } else {
    var order = keyOrList;

    if(direction != null) {
      order = [[keyOrList, direction]];
    }
    this.sortValue = order;
    callback(null, this);
  }
  return this;
};

Cursor.prototype.limit = function(limit, callback) {
  callback = callback || function(){};
  if(this.queryRun == true || this.state == Cursor.CLOSED) {
    callback(new Error("Cursor is closed"), null);
  } else {
    if(limit != null && limit.constructor != Number) {
      callback(new Error("limit requires an integer"), null);
    } else {
      this.limitValue = limit;
      callback(null, this);
    }
  }
  return this;
};

Cursor.prototype.skip = function(skip, callback) {
  callback = callback || function(){};
  if(this.queryRun == true || this.state == Cursor.CLOSED) {
    callback(new Error("Cursor is closed"), null);
  } else {
    if(skip != null && skip.constructor != Number) {
      callback(new Error("skip requires an integer"), null);
    } else {
      this.skipValue = skip;
      callback(null, this);
    }
  }
  return this;
};

Cursor.prototype.generateQueryCommand = function() {
  // Unpack the options
  var timeout  = this.timeout != null ? this.timeout : QueryCommand.OPTS_NONE;
  var queryOptions = timeout;
  // Check if we need a special selector
  if(this.sortValue != null || this.explainValue != null || this.hint != null || this.snapshot != null) {
    // Build special selector
    var specialSelector = {'query':this.selector};
    if(this.sortValue != null) specialSelector['orderby'] = this.formattedOrderClause();
    if(this.hint != null && this.hint.constructor == Object) specialSelector['$hint'] = this.hint;
    if(this.explainValue != null) specialSelector['$explain'] = true;
    if(this.snapshot != null) specialSelector['$snapshot'] = true;

    return new QueryCommand(this.db, this.db.databaseName + "." + this.collection.collectionName, queryOptions, this.skipValue, this.limitValue, specialSelector, this.fields);
  } else {
    return new QueryCommand(this.db, this.db.databaseName + "." + this.collection.collectionName, queryOptions, this.skipValue, this.limitValue, this.selector, this.fields);
  }
};

Cursor.prototype.formattedOrderClause = function() {
  var orderBy = {};
  var self = this;

  if(this.sortValue instanceof Array) {
    this.sortValue.forEach(function(sortElement) {
      if(sortElement.constructor == String) {
        orderBy[sortElement] = 1;
      } else {
        orderBy[sortElement[0]] = self.formatSortValue(sortElement[1]);
      }
    });
  } else if(Object.prototype.toString.call(this.sortValue) === '[object Object]') {
    throw new Error("Invalid sort argument was supplied");
  } else if(this.sortValue.constructor == String) {
    orderBy[this.sortValue] = 1
  } else {
    throw Error("Illegal sort clause, must be of the form " +
      "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]");
  }
  return orderBy;
};

Cursor.prototype.formatSortValue = function(sortDirection) {
  var value = ("" + sortDirection).toLowerCase();
  if(value == 'ascending' || value == 'asc' || value == 1) return 1;
  if(value == 'descending' || value == 'desc' || value == -1 ) return -1;
  throw Error("Illegal sort clause, must be of the form " +
    "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]");
};

Cursor.prototype.fetchAllRecords = function(callback) {
  var self = this;

  if(self.state == Cursor.INIT) {
    var queryCommand = self.generateQueryCommand();
    self.db.executeCommand(queryCommand, function(err, result) {
      var numberReturned = result.numberReturned;
      // Check if we need to fetch the count
      if(self.limitValue > 0 && self.limitValue > numberReturned) {
        self.totalNumberOfRecords = numberReturned;
        self.fetchFirstResults(result, callback);
      } else if(self.limitValue > 0 && self.limitValue <= numberReturned) {
        self.totalNumberOfRecords = self.limitValue;
        self.fetchFirstResults(result, callback);
      } else {
        self.totalNumberOfRecords = numberReturned;
        self.fetchFirstResults(result, callback);
      }
    });
  } else if(self.state == Cursor.OPEN) {
    if(self.cursorId.greaterThan(self.db.bson_serializer.Long.fromInt(0))) {
      // Build get more command
      var getMoreCommand = new GetMoreCommand(self.db, self.db.databaseName + "." + self.collection.collectionName, self.limitValue, self.cursorId);
      // Execute the command
      self.db.executeCommand(getMoreCommand, function(err, result) {
        self.numberOfReturned = result.numberReturned;
        self.cursorId = result.cursorId;
        self.totalNumberOfRecords = self.totalNumberOfRecords + self.numberOfReturned;
        // Determine if there's more documents to fetch
        if(self.numberOfReturned > 0 && (self.limitValue == 0 || self.totalNumberOfRecords < self.limitValue)) {
          result.documents.forEach(function(item) { self.items.push(item);});
          self.fetchAllRecords(callback);
        } else {
          // Close the cursor if we still have one
          if(self.cursorId.greaterThan(self.db.bson_serializer.Long.fromInt(0))) self.close(function(cursor) {});
          // Return all the items fetched
          callback(null, self.items);
        }
      });
    } else {
      // Close the cursor as all results have been read
      callback(null, self.items);
      self.state = Cursor.CLOSED;
    }
  }
};

Cursor.prototype.fetchFirstResults = function(result, callback) {
  var self = this;
  this.cursorId = result.cursorId;
  this.queryRun = true;
  this.numberOfReturned = result.numberReturned;
  this.totalNumberOfRecords = this.numberOfReturned;

  // Add the new documents to the list of items
  result.documents.forEach(function(item) { self.items.push(item);});
  // Adjust the state of the cursor
  this.state = Cursor.OPEN;
  // Fetch more records
  this.fetchAllRecords(callback);
};

Cursor.prototype.nextObject = function(callback) {
  var self = this;

  // Fetch the first batch of records if none are available
  if(self.state == Cursor.INIT) {
    // Fetch the total count of object
    try {
      // Execute the first query
      this.fetchAllRecords(function(err, items) {
        self.items = items;

        if(self.index < items.length) {
          callback(null, items[self.index++]);
        } else {
          callback(null, null);
        }
      });
    } catch(err) {
      callback(new Error(err.toString()), null);
    }
  } else {
    if(self.items.length > self.index) {
      callback(null, self.items[self.index++]);
    } else {
      callback(null, null);
    }
  }
};

Cursor.prototype.explain = function(callback) {
  var limit = (-1)*Math.abs(this.limitValue);
  // Create a new cursor and fetch the plan
  var cursor = new Cursor(this.db, this.collection, this.selector, this.fields, this.skipValue, limit,
      this.sortValue, this.hint, true, this.snapshot, this.timeout);
  cursor.nextObject(function(err, item) {
    // close the cursor
    cursor.close(function(err, result) {
      callback(null, item);
    });
  });
};

Cursor.prototype.streamRecords = function(options) {
  var args = Array.prototype.slice.call(arguments, 0);
  options = args.length ? args.shift() : {};

  var
    self = this,
    stream = new process.EventEmitter(),
    recordLimitValue = this.limitValue || 0,
    emittedRecordCount = 0,
    queryCommand = this.generateQueryCommand();

  // see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol
  queryCommand.numberToReturn = options.fetchSize ? options.fetchSize : 500;

  execute(queryCommand);

  function execute(command) {
    self.db.executeCommand(command, function(err,result) {
      if (!self.queryRun && result) {
        self.queryRun = true;
        self.cursorId = result.cursorId;
        self.state = Cursor.OPEN;
        self.getMoreCommand = new GetMoreCommand(self.db, self.db.databaseName + "." + self.collection.collectionName, queryCommand.numberToReturn, result.cursorId);
      }
      if (result.documents && result.documents.length) {
        try {
          result.documents.forEach(function(doc){
            if (recordLimitValue && emittedRecordCount>=recordLimitValue) {
              throw("done");
            }
            emittedRecordCount++;
            stream.emit('data', doc);
          });
        } catch(err) {
          if (err != "done") { throw err; }
          else {
            stream.emit('end', recordLimitValue);
            self.close(function(){});
            return(null);
          }
        }
        // rinse & repeat
        execute(self.getMoreCommand);
      } else {
        self.close(function(){
          stream.emit('end', recordLimitValue);
        });
      }
    });
  }
  return stream;
};

Cursor.prototype.close = function(callback) {
  var self = this;

  // Close the cursor if not needed
  if(self.cursorId instanceof Long && self.cursorId.greaterThan(new self.db.bson_serializer.Long.fromInt(0))) {
    var command = new KillCursorCommand(self.db, [self.cursorId]);
    self.db.executeCommand(command, function(err, result) {});
  }

  self.cursorId = Long.fromInt(0);
  self.state = Cursor.CLOSED;
  callback(null, self);
};

Cursor.prototype.isClosed = function() {
  return this.state == Cursor.CLOSED ? true : false;
};

// Static variables
Cursor.INIT = 0;
Cursor.OPEN = 1;
Cursor.CLOSED = 2;
<commit_msg>Unrolling the process.nexttick cursor patch<commit_after>var QueryCommand = require('./commands/query_command').QueryCommand,
  GetMoreCommand = require('./commands/get_more_command').GetMoreCommand,
  KillCursorCommand = require('./commands/kill_cursor_command').KillCursorCommand,
  Integer = require('./goog/math/integer').Integer,
  Long = require('./goog/math/long').Long;

/**
  Handles all the operations on query result using find
**/
var Cursor = exports.Cursor = function(db, collection, selector, fields, skip, limit, sort, hint, explain, snapshot, timeout) {
  this.db = db;
  this.collection = collection;
  this.selector = selector;
  this.fields = fields;
  this.skipValue = skip == null ? 0 : skip;
  this.limitValue = limit == null ? 0 : limit;
  this.sortValue = sort;
  this.hint = hint;
  this.explainValue = explain;
  this.snapshot = snapshot;
  this.timeout = timeout;
  this.numberOfReturned = 0;
  this.totalNumberOfRecords = 0;
  this.items = [];
  this.cursorId = Long.fromInt(0);
  // Keeps track of location of the cursor
  this.index = 0;
  // State variables for the cursor
  this.state = Cursor.INIT;
  // Kepp track of the current query run
  this.queryRun = false;
};

  // Return an array of documents
Cursor.prototype.toArray = function(callback) {
  var self = this;

  try {
    if(self.state != Cursor.CLOSED) {
      self.fetchAllRecords(function(err, items) {
        self.state = Cursor.CLOSED;
        // Save object in internal cache that can be used for iterating and set index
        // to first object
        self.index = 0;
        self.items = items;
        callback(null, items);
      });
    } else {
      callback(new Error("Cursor is closed"), null);
    }
  } catch(err) {
    callback(new Error(err.toString()), null);
  }
};

// For Each materialized the objects at need
Cursor.prototype.each = function(callback) {
  var self = this;

  if(this.state != Cursor.CLOSED || (this.index <= this.items.length)) {
    // Fetch the next object until there is no more objects
    self.nextObject(function(err, item) {
      if(item != null) {
        callback(null, item);
        self.each(callback);
      } else {
        self.state = Cursor.CLOSED;
        callback(null, null);
      }
    });
  } else {
    callback(new Error("Cursor is closed"), null);
  }
};

Cursor.prototype.count = function(callback) {
  this.collection.count(this.selector, callback);
};

Cursor.prototype.sort = function(keyOrList, direction, callback) {
  callback = callback || function(){};
  if(typeof direction === "function") { callback = direction; direction = null; }
  if(this.queryRun == true || this.state == Cursor.CLOSED) {
    callback(new Error("Cursor is closed"), null);
  } else {
    var order = keyOrList;

    if(direction != null) {
      order = [[keyOrList, direction]];
    }
    this.sortValue = order;
    callback(null, this);
  }
  return this;
};

Cursor.prototype.limit = function(limit, callback) {
  callback = callback || function(){};
  if(this.queryRun == true || this.state == Cursor.CLOSED) {
    callback(new Error("Cursor is closed"), null);
  } else {
    if(limit != null && limit.constructor != Number) {
      callback(new Error("limit requires an integer"), null);
    } else {
      this.limitValue = limit;
      callback(null, this);
    }
  }
  return this;
};

Cursor.prototype.skip = function(skip, callback) {
  callback = callback || function(){};
  if(this.queryRun == true || this.state == Cursor.CLOSED) {
    callback(new Error("Cursor is closed"), null);
  } else {
    if(skip != null && skip.constructor != Number) {
      callback(new Error("skip requires an integer"), null);
    } else {
      this.skipValue = skip;
      callback(null, this);
    }
  }
  return this;
};

Cursor.prototype.generateQueryCommand = function() {
  // Unpack the options
  var timeout  = this.timeout != null ? this.timeout : QueryCommand.OPTS_NONE;
  var queryOptions = timeout;
  // Check if we need a special selector
  if(this.sortValue != null || this.explainValue != null || this.hint != null || this.snapshot != null) {
    // Build special selector
    var specialSelector = {'query':this.selector};
    if(this.sortValue != null) specialSelector['orderby'] = this.formattedOrderClause();
    if(this.hint != null && this.hint.constructor == Object) specialSelector['$hint'] = this.hint;
    if(this.explainValue != null) specialSelector['$explain'] = true;
    if(this.snapshot != null) specialSelector['$snapshot'] = true;

    return new QueryCommand(this.db, this.db.databaseName + "." + this.collection.collectionName, queryOptions, this.skipValue, this.limitValue, specialSelector, this.fields);
  } else {
    return new QueryCommand(this.db, this.db.databaseName + "." + this.collection.collectionName, queryOptions, this.skipValue, this.limitValue, this.selector, this.fields);
  }
};

Cursor.prototype.formattedOrderClause = function() {
  var orderBy = {};
  var self = this;

  if(this.sortValue instanceof Array) {
    this.sortValue.forEach(function(sortElement) {
      if(sortElement.constructor == String) {
        orderBy[sortElement] = 1;
      } else {
        orderBy[sortElement[0]] = self.formatSortValue(sortElement[1]);
      }
    });
  } else if(Object.prototype.toString.call(this.sortValue) === '[object Object]') {
    throw new Error("Invalid sort argument was supplied");
  } else if(this.sortValue.constructor == String) {
    orderBy[this.sortValue] = 1
  } else {
    throw Error("Illegal sort clause, must be of the form " +
      "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]");
  }
  return orderBy;
};

Cursor.prototype.formatSortValue = function(sortDirection) {
  var value = ("" + sortDirection).toLowerCase();
  if(value == 'ascending' || value == 'asc' || value == 1) return 1;
  if(value == 'descending' || value == 'desc' || value == -1 ) return -1;
  throw Error("Illegal sort clause, must be of the form " +
    "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]");
};

Cursor.prototype.fetchAllRecords = function(callback) {
  var self = this;

  if(self.state == Cursor.INIT) {
    var queryCommand = self.generateQueryCommand();
    self.db.executeCommand(queryCommand, function(err, result) {
      var numberReturned = result.numberReturned;
      // Check if we need to fetch the count
      if(self.limitValue > 0 && self.limitValue > numberReturned) {
        self.totalNumberOfRecords = numberReturned;
        self.fetchFirstResults(result, callback);
      } else if(self.limitValue > 0 && self.limitValue <= numberReturned) {
        self.totalNumberOfRecords = self.limitValue;
        self.fetchFirstResults(result, callback);
      } else {
        self.totalNumberOfRecords = numberReturned;
        self.fetchFirstResults(result, callback);
      }
    });
  } else if(self.state == Cursor.OPEN) {
    if(self.cursorId.greaterThan(self.db.bson_serializer.Long.fromInt(0))) {
      // Build get more command
      var getMoreCommand = new GetMoreCommand(self.db, self.db.databaseName + "." + self.collection.collectionName, self.limitValue, self.cursorId);
      // Execute the command
      self.db.executeCommand(getMoreCommand, function(err, result) {
        self.numberOfReturned = result.numberReturned;
        self.cursorId = result.cursorId;
        self.totalNumberOfRecords = self.totalNumberOfRecords + self.numberOfReturned;
        // Determine if there's more documents to fetch
        if(self.numberOfReturned > 0 && (self.limitValue == 0 || self.totalNumberOfRecords < self.limitValue)) {
          result.documents.forEach(function(item) { self.items.push(item);});
          self.fetchAllRecords(callback);
        } else {
          // Close the cursor if we still have one
          if(self.cursorId.greaterThan(self.db.bson_serializer.Long.fromInt(0))) self.close(function(cursor) {});
          // Return all the items fetched
          callback(null, self.items);
        }
      });
    } else {
      // Close the cursor as all results have been read
      callback(null, self.items);
      self.state = Cursor.CLOSED;
    }
  }
};

Cursor.prototype.fetchFirstResults = function(result, callback) {
  var self = this;
  this.cursorId = result.cursorId;
  this.queryRun = true;
  this.numberOfReturned = result.numberReturned;
  this.totalNumberOfRecords = this.numberOfReturned;

  // Add the new documents to the list of items
  result.documents.forEach(function(item) { self.items.push(item);});
  // Adjust the state of the cursor
  this.state = Cursor.OPEN;
  // Fetch more records
  this.fetchAllRecords(callback);
};

Cursor.prototype.nextObject = function(callback) {
  var self = this;

  // Fetch the first batch of records if none are available
  if(self.state == Cursor.INIT) {
    // Fetch the total count of object
    try {
      // Execute the first query
      this.fetchAllRecords(function(err, items) {
        self.items = items;

        if(self.index < items.length) {
          callback(null, items[self.index++]);
        } else {
          callback(null, null);
        }
      });
    } catch(err) {
      callback(new Error(err.toString()), null);
    }
  } else {
    if(self.items.length > self.index) {
      callback(null, self.items[self.index++]);
    } else {
      callback(null, null);
    }
  }
};

Cursor.prototype.explain = function(callback) {
  var limit = (-1)*Math.abs(this.limitValue);
  // Create a new cursor and fetch the plan
  var cursor = new Cursor(this.db, this.collection, this.selector, this.fields, this.skipValue, limit,
      this.sortValue, this.hint, true, this.snapshot, this.timeout);
  cursor.nextObject(function(err, item) {
    // close the cursor
    cursor.close(function(err, result) {
      callback(null, item);
    });
  });
};

Cursor.prototype.streamRecords = function(options) {
  var args = Array.prototype.slice.call(arguments, 0);
  options = args.length ? args.shift() : {};

  var
    self = this,
    stream = new process.EventEmitter(),
    recordLimitValue = this.limitValue || 0,
    emittedRecordCount = 0,
    queryCommand = this.generateQueryCommand();

  // see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol
  queryCommand.numberToReturn = options.fetchSize ? options.fetchSize : 500;

  execute(queryCommand);

  function execute(command) {
    self.db.executeCommand(command, function(err,result) {
      if (!self.queryRun && result) {
        self.queryRun = true;
        self.cursorId = result.cursorId;
        self.state = Cursor.OPEN;
        self.getMoreCommand = new GetMoreCommand(self.db, self.db.databaseName + "." + self.collection.collectionName, queryCommand.numberToReturn, result.cursorId);
      }
      if (result.documents && result.documents.length) {
        try {
          result.documents.forEach(function(doc){
            if (recordLimitValue && emittedRecordCount>=recordLimitValue) {
              throw("done");
            }
            emittedRecordCount++;
            stream.emit('data', doc);
          });
        } catch(err) {
          if (err != "done") { throw err; }
          else {
            stream.emit('end', recordLimitValue);
            self.close(function(){});
            return(null);
          }
        }
        // rinse & repeat
        execute(self.getMoreCommand);
      } else {
        self.close(function(){
          stream.emit('end', recordLimitValue);
        });
      }
    });
  }
  return stream;
};

Cursor.prototype.close = function(callback) {
  var self = this;

  // Close the cursor if not needed
  if(self.cursorId instanceof Long && self.cursorId.greaterThan(new self.db.bson_serializer.Long.fromInt(0))) {
    var command = new KillCursorCommand(self.db, [self.cursorId]);
    self.db.executeCommand(command, function(err, result) {});
  }

  self.cursorId = Long.fromInt(0);
  self.state = Cursor.CLOSED;
  callback(null, self);
};

Cursor.prototype.isClosed = function() {
  return this.state == Cursor.CLOSED ? true : false;
};

// Static variables
Cursor.INIT = 0;
Cursor.OPEN = 1;
Cursor.CLOSED = 2;
<|endoftext|>


<commit_before>define([
    'models/proto/paged-collection', 'models/quest'
], function (Parent, Quest) {
    return Parent.extend({
        defaultCgi: ['comment_count=1'],
        baseUrl: '/api/quest',
        cgi: ['user', 'status', 'limit', 'offset', 'sort', 'order', 'unclaimed', 'tags', 'watchers', 'realm'],
        model: Quest,

        initialize: function () {
            Parent.prototype.initialize.apply(this, arguments);

            var order_flag = 1;
            if (this.options.order && this.options.order == 'desc') {
                order_flag = -1;
            }

            if (this.options.sort) {
                if (this.options.sort == 'leaderboard') {
                    // duplicates server-side sorting logic!
                    this.comparator = function(m1, m2) {
                        if (m1.like_count() > m2.like_count()) return -order_flag;
                        if (m2.like_count() > m1.like_count()) return order_flag;

                        if (m1.comment_count() > m2.comment_count()) return -order_flag;
                        if (m2.comment_count() > m1.comment_count()) return order_flag;
                        return 0;
                    };
                }
                else if (this.options.sort == 'manual') {
                    // duplicates server-side sorting logic!
                    this.comparator = function(m1, m2) {
                        var o1 = m1.get('order');
                        var o2 = m2.get('order');
                        var t1 = m1.get('ts');
                        var t2 = m2.get('ts');
                        if (o1 && o2) {
                            if (o1 < o2) { return -1 } else if (o1 > o2) { return 1 } else { return 0 }
                        }
                        else if (o1) {
                            return 1;
                        }
                        else if (o2) {
                            return -1;
                        }
                        else {
                            if (t1 < t2) { return 1 } else if (t1 > t2) { return -1 } else { return 0 }
                        }
                    };
                }
                else {
                    console.log("oops, unknown sort option " + this.options.sort);
                }
            }
            else {
                this.comparator = function(m1, m2) {
                    if (m1.id > m2.id) return order_flag;
                    if (m2.id > m1.id) return -order_flag;
                    return 0;
                };
            }
        },

        saveManualOrder: function (ids) {
            return $.post('/api/quest/set_manual_order', {
                'quest_ids[]': ids
            });
            mixpanel.track('set order');
        }
    });
});
<commit_msg>fix set_manual_order calls tracking<commit_after>define([
    'models/proto/paged-collection', 'models/quest'
], function (Parent, Quest) {
    return Parent.extend({
        defaultCgi: ['comment_count=1'],
        baseUrl: '/api/quest',
        cgi: ['user', 'status', 'limit', 'offset', 'sort', 'order', 'unclaimed', 'tags', 'watchers', 'realm'],
        model: Quest,

        initialize: function () {
            Parent.prototype.initialize.apply(this, arguments);

            var order_flag = 1;
            if (this.options.order && this.options.order == 'desc') {
                order_flag = -1;
            }

            if (this.options.sort) {
                if (this.options.sort == 'leaderboard') {
                    // duplicates server-side sorting logic!
                    this.comparator = function(m1, m2) {
                        if (m1.like_count() > m2.like_count()) return -order_flag;
                        if (m2.like_count() > m1.like_count()) return order_flag;

                        if (m1.comment_count() > m2.comment_count()) return -order_flag;
                        if (m2.comment_count() > m1.comment_count()) return order_flag;
                        return 0;
                    };
                }
                else if (this.options.sort == 'manual') {
                    // duplicates server-side sorting logic!
                    this.comparator = function(m1, m2) {
                        var o1 = m1.get('order');
                        var o2 = m2.get('order');
                        var t1 = m1.get('ts');
                        var t2 = m2.get('ts');
                        if (o1 && o2) {
                            if (o1 < o2) { return -1 } else if (o1 > o2) { return 1 } else { return 0 }
                        }
                        else if (o1) {
                            return 1;
                        }
                        else if (o2) {
                            return -1;
                        }
                        else {
                            if (t1 < t2) { return 1 } else if (t1 > t2) { return -1 } else { return 0 }
                        }
                    };
                }
                else {
                    console.log("oops, unknown sort option " + this.options.sort);
                }
            }
            else {
                this.comparator = function(m1, m2) {
                    if (m1.id > m2.id) return order_flag;
                    if (m2.id > m1.id) return -order_flag;
                    return 0;
                };
            }
        },

        saveManualOrder: function (ids) {
            mixpanel.track('set order');
            return $.post('/api/quest/set_manual_order', {
                'quest_ids[]': ids
            });
        }
    });
});
<|endoftext|>


<commit_before>/**
 * Created by nuintun on 2015/4/27.
 */

'use strict';

var is = require('is');
var css = require('css');
var path = require('path');
var join = path.join;
var extname = path.extname;
var rename = require('./rename');
var udeps = require('umd-deps');
var cdeps = require('./css-deps');
var util = require('./util');
var debug = util.debug;
var colors = util.colors;

/**
 * Transport vinyl id
 * - vinyl: vinyl object
 * - required options: idleading, rename
 */

function transportId(vinyl, options){
  var id;
  var pkg = util.parsePackage(vinyl, options.wwwroot);
  var idleading = resolveIdleading(options.idleading, vinyl.path, pkg);

  // get id
  id = util.template(idleading, pkg);
  // rename id
  id = util.addExt(id);
  id = rename(id, options.rename);
  id = util.hideExt(id);

  // seajs has hacked css before 3.0.0
  // https://github.com/seajs/seajs/blob/2.2.1/src/util-path.js#L49
  // demo https://github.com/popomore/seajs-test/tree/master/css-deps
  if (extname(id) === '.css') {
    id += '.js';
  }

  // normalize id
  id = util.normalize(id);

  // debug
  debug('transport id: %s', colors.data(id));

  // rewrite vinyl path
  if (path.isAbsolute(id)) {
    vinyl.path = join(options.wwwroot, util.addExt(id));
  } else {
    vinyl.path = join(vinyl.cwd, vinyl.base, util.addExt(id));
  }

  // set vinyl package info
  vinyl.package = util.extend(pkg, { id: id });

  return id;
}

/**
 * Transport cmd dependencies.
 * - vinyl: vinyl object
 * - required options: idleading
 */

function transportDeps(vinyl, options){
  var deps = [];
  var include = [];
  var vars = options.vars;
  var alias = options.alias;
  var paths = options.paths;
  var pkg = vinyl.package || {};

  // replace require and collect dependencies
  vinyl.contents = new Buffer(udeps(vinyl.contents.toString(), function (id, flag){
    // parse id form alias, paths, vars
    id = util.parseAlias(id, alias);
    id = util.parsePaths(id, paths);
    id = util.parseAlias(id, alias);
    id = util.parseVars(id, vars);
    id = util.parseAlias(id, alias);

    // id is not a local file
    if (!util.isLocal(id)) {
      // cache dependencie id
      deps.push(id);

      return id;
    }

    var path;

    // normalize id
    id = util.normalize(id);

    // if end with /, find index file
    if (id.substring(id.length - 1) === '/') {
      id += 'index';
    }

    // debug
    debug('transport%s deps: %s', flag ? ' ' + flag : '', colors.data(id));

    // add extname
    path = util.addExt(id);
    // rename id
    id = rename(path, options.rename);
    // hide extname
    id = util.hideExt(id);

    // seajs has hacked css before 3.0.0
    // https://github.com/seajs/seajs/blob/2.2.1/src/util-path.js#L49
    // demo https://github.com/popomore/seajs-test/tree/master/css-deps
    if (extname(id) === '.css') {
      id += '.js';
    }

    // normalize id
    id = util.normalize(id);

    // cache dependencie id and path
    if (flag === null) {
      // cache dependencie id
      deps.push(id);

      // get absolute path
      path = util.resolve(path, vinyl, options.wwwroot);

      // cache dependencie absolute path
      include.push({
        id: id,
        path: path
      });
    }

    return id;
  }, true));

  // Cache file dependencies
  vinyl.package = util.extend(pkg, {
    include: include,
    dependencies: deps
  });

  return deps;
}

/**
 * Transport css dependencies.
 * - vinyl: vinyl object
 */

function transportCssDeps(vinyl, options){
  var deps = [];
  var include = [];
  var prefix = options.prefix;
  var pkg = vinyl.package || {};

  // compute prefix
  prefix = is.fn(prefix) ? prefix(pkg.id || null, vinyl.path) : prefix;

  // replace imports and collect dependencies
  vinyl.contents = new Buffer(cdeps(vinyl.contents, function (id){
    var path;

    // id is not a local file
    if (!util.isLocal(id)) {
      // cache dependencie id
      deps.push(id);

      // keep import
      return id;
    }

    // normalize id
    id = util.normalize(id);

    var isRelative = util.isRelative(id);
    var isAbsolute = util.isAbsolute(id);

    // css import parsing rules using html
    if (!isAbsolute && !isRelative) {
      id = './' + id;
    }

    // if end with /, find index file
    if (id.substring(id.length - 1) === '/') {
      id += 'index.css';
    }

    // add extname
    path = util.addExt(id);
    // rename id
    id = rename(path, options.rename);
    // hide extname
    id = util.hideExt(id);

    // seajs has hacked css before 3.0.0
    // https://github.com/seajs/seajs/blob/2.2.1/src/util-path.js#L49
    // demo https://github.com/popomore/seajs-test/tree/master/css-deps
    if (extname(id) === '.css') {
      id += '.js';
    }

    // normalize id
    id = util.normalize(id);

    // debug
    debug('transport deps: %s', colors.data(id));

    // get absolute path
    path = util.resolve(path, vinyl, options.wwwroot);

    // cache dependencie id
    deps.push(id);
    // cache dependencie absolute path
    include.push({
      id: id,
      path: path
    });

    // delete all import
    return false;
  }, options.oncsspath, prefix));

  // Cache file dependencies
  vinyl.package = util.extend(pkg, {
    include: include,
    dependencies: deps
  });

  return deps;
}

/*
 Create a parser in lib/parser
 */

function plugin(type, transport){
  return function (vinyl, options){
    // debug
    debug('read %s: %s', type, colors.data(util.pathFromCwd(vinyl.path)));

    vinyl = transport(vinyl, options);

    return vinyl;
  };
}

/**
 * resolve id leading
 * @param idleading
 * @param path
 * @param pkg
 * @returns {*}
 */

function resolveIdleading(idleading, path, pkg){
  return is.fn(idleading) ? idleading(path, pkg) : idleading;
}

/**
 * Exports module.
 */

exports.plugin = plugin;
exports.transportId = transportId;
exports.transportDeps = transportDeps;
exports.resolveIdleading = resolveIdleading;
exports.transportCssDeps = transportCssDeps;
<commit_msg>add source files<commit_after>/**
 * Created by nuintun on 2015/4/27.
 */

'use strict';

var is = require('is');
var css = require('css');
var path = require('path');
var join = path.join;
var extname = path.extname;
var rename = require('./rename');
var udeps = require('umd-deps');
var cdeps = require('./css-deps');
var util = require('./util');
var debug = util.debug;
var colors = util.colors;

/**
 * Transport vinyl id
 * - vinyl: vinyl object
 * - required options: idleading, rename
 */

function transportId(vinyl, options){
  var id;
  var pkg = util.parsePackage(vinyl, options.wwwroot);
  var idleading = resolveIdleading(options.idleading, vinyl.path, pkg);

  // get id
  id = util.template(idleading, pkg);
  // rename id
  id = util.addExt(id);
  id = rename(id, options.rename);
  id = util.hideExt(id);

  // seajs has hacked css before 3.0.0
  // https://github.com/seajs/seajs/blob/2.2.1/src/util-path.js#L49
  // demo https://github.com/popomore/seajs-test/tree/master/css-deps
  if (extname(id) === '.css') {
    id += '.js';
  }

  // normalize id
  id = util.normalize(id);

  // debug
  debug('transport id: %s', colors.data(id));

  // rewrite vinyl path
  if (path.isAbsolute(id)) {
    vinyl.path = join(options.wwwroot, util.addExt(id));
  } else {
    vinyl.path = join(vinyl.cwd, vinyl.base, util.addExt(id));
  }

  // set vinyl package info
  vinyl.package = util.extend(pkg, { id: id });

  return id;
}

/**
 * Transport cmd dependencies.
 * - vinyl: vinyl object
 * - required options: idleading
 */

function transportDeps(vinyl, options){
  var deps = [];
  var include = [];
  var vars = options.vars;
  var alias = options.alias;
  var paths = options.paths;
  var pkg = vinyl.package || {};

  // replace require and collect dependencies
  vinyl.contents = new Buffer(udeps(vinyl.contents.toString(), function (id, flag){
    // parse id form alias, paths, vars
    id = util.parseAlias(id, alias);
    id = util.parsePaths(id, paths);
    id = util.parseAlias(id, alias);
    id = util.parseVars(id, vars);
    id = util.parseAlias(id, alias);

    // id is not a local file
    if (!util.isLocal(id)) {
      // cache dependencie id
      deps.push(id);

      return id;
    }

    var path;

    // normalize id
    id = util.normalize(id);

    // if end with /, find index file
    if (id.substring(id.length - 1) === '/') {
      id += 'index';
    }

    // debug
    debug('transport%s deps: %s', flag ? ' ' + flag : '', colors.data(id));

    // add extname
    path = util.addExt(id);
    // rename id
    id = rename(path, options.rename);
    // hide extname
    id = util.hideExt(id);

    // seajs has hacked css before 3.0.0
    // https://github.com/seajs/seajs/blob/2.2.1/src/util-path.js#L49
    // demo https://github.com/popomore/seajs-test/tree/master/css-deps
    if (extname(id) === '.css') {
      id += '.js';
    }

    // normalize id
    id = util.normalize(id);

    // cache dependencie id and path
    if (flag === null) {
      // cache dependencie id
      deps.push(id);

      // get absolute path
      path = util.resolve(path, vinyl, options.wwwroot);

      // cache dependencie absolute path
      include.push({
        id: id,
        path: path
      });
    }

    return id;
  }, true));

  // Cache file dependencies
  vinyl.package = util.extend(pkg, {
    include: include,
    dependencies: deps
  });

  return deps;
}

/**
 * Transport css dependencies.
 * - vinyl: vinyl object
 */

function transportCssDeps(vinyl, options){
  var deps = [];
  var include = [];
  var prefix = options.prefix;
  var pkg = vinyl.package || {};

  // compute prefix
  prefix = is.fn(prefix) ? prefix(pkg.id || null, vinyl.path) : prefix;

  // replace imports and collect dependencies
  vinyl.contents = new Buffer(cdeps(vinyl.contents, function (id){
    var path;

    // id is not a local file
    if (!util.isLocal(id)) {
      // cache dependencie id
      deps.push(id);

      // keep import
      return id;
    }

    // normalize id
    id = util.normalize(id);

    var isRelative = util.isRelative(id);
    var isAbsolute = util.isAbsolute(id);

    // css import parsing rules using html
    if (!isAbsolute && !isRelative) {
      id = './' + id;
    }

    // if end with /, find index file
    if (id.substring(id.length - 1) === '/') {
      id += 'index.css';
    }

    // add extname
    path = util.addExt(id);
    // rename id
    id = rename(path, options.rename);
    // hide extname
    id = util.hideExt(id);

    // seajs has hacked css before 3.0.0
    // https://github.com/seajs/seajs/blob/2.2.1/src/util-path.js#L49
    // demo https://github.com/popomore/seajs-test/tree/master/css-deps
    if (extname(id) === '.css') {
      id += '.js';
    }

    // normalize id
    id = util.normalize(id);

    // debug
    debug('transport deps: %s', colors.data(id));

    // get absolute path
    path = util.resolve(path, vinyl, options.wwwroot);

    // cache dependencie id
    deps.push(id);
    // cache dependencie absolute path
    include.push({
      id: id,
      path: path
    });

    // delete all import
    return false;
  }, options.oncsspath, prefix));

  // Cache file dependencies
  vinyl.package = util.extend(pkg, {
    include: include,
    dependencies: deps
  });

  return deps;
}

/*
 Create a parser in lib/parser
 */

function plugin(type, transport){
  return function (vinyl, options){
    // debug
    debug('read %s: %s', type, colors.data(util.pathFromCwd(vinyl.path)));

    vinyl = transport(vinyl, options);

    return vinyl;
  };
}

/**
 * resolve id leading
 * @param idleading
 * @param path
 * @param pkg
 * @returns {*}
 */

function resolveIdleading(idleading, path, pkg){
  return is.fn(idleading) ? idleading(path, pkg) : idleading;
}

/**
 * Exports module.
 */

exports.plugin = plugin;
exports.transportId = transportId;
exports.transportDeps = transportDeps;
exports.transportCssDeps = transportCssDeps;
exports.resolveIdleading = resolveIdleading;
<|endoftext|>


<commit_before>import React, {useState, useEffect, useRef} from 'react'
import {connect} from 'react-redux'
import styled from 'styled-components'
import {Creatable} from "react-select"
import {translate} from "react-i18next"
import isEqual from "lodash/isEqual"

const SearchInput = (props) => {

  const {t} = props

  const [allOptions, setAllOptions] = useState([])
  const [options, setOptions] = useState([])
  const prevAllTags = useRef([])

  useEffect(() => {
    if (!isEqual(prevAllTags.current, props.allTags)) {
      prevAllTags.current = props.allTags

      const options = props.allTags.map(tag => ({label: `#${tag}`, value: `#${tag}`}))

      setAllOptions(options)
      setOptions(options.slice(0, 5))
    }
  }, [props.allTags])

  const filterOptions = (input) => {
    const searchString = input.toLowerCase().trim();
    const res = allOptions.filter(function(d) {
      return d.label.match( searchString );
    });

    const newOptions = res.slice(0, 5)

    setOptions(newOptions)
  }

  const onInputChange = (input, actionMeta) => {
    const {action} = actionMeta
    if (action === "input-blur" || action === "menu-close") {
      return
    }

    const tokens = input.split(' ')
    const lastToken = tokens[tokens.length-1].trim()
    onChange(input)
    filterOptions(lastToken)
  }

  const handleChange = (newValue, actionMeta) => {
    // TODO: dispatch on change
    if (!!newValue && newValue.length) {
      let newInputValue = props.searchText
      if (newInputValue.length && newInputValue.slice(-1) === ' ') {
        newInputValue = `${newInputValue} ${newValue[0].label}`
      } else {
        const tokens = props.searchText.trim().split(" ").filter(t => t.length)
        tokens.pop()
        tokens.push(newValue[0].label)
        newInputValue = tokens.join(' ')
      }

      onChange(newInputValue)
    }
  };

  const onChange = (newInputValue) => {
    props.onChange(newInputValue)
  }

  return (
    <Recommender
      delimeter=" "
      formatCreateLabel={(inputValue) => (inputValue)}
      filterOption={(option) => true}
      isMulti
      styles={{
        control: (provided, state) => ({
          ...provided,
          border: 'none',
          boxShadow: 'none'
        }),
        menu: (provided, state) => ({
          ...provided,
          width: '85%'
        })
      }}
      onBlur={() => {}}
      onInputChange={onInputChange}
      onChange={handleChange}
      onKeyDown={props.onKeyDown}
      options={options}
      placeholder={t("searchbar.placeholder")}
      inputValue={props.searchText}
      value={[]}
    />
  )
}

const mapStateToProps = ({search}) => {
  return {
    allTags: search.tags.map(tagPair => tagPair[0])
  }
}

export default translate('translation')(connect(mapStateToProps)(SearchInput))

const Recommender = styled(Creatable)`
  font-size: 1em !important;
  line-height: 1em !important;
  font-weight: 100;
  padding-left: 2.7em !important;
  padding-right: 0.5em !important;
  z-index: 999 !important;
  margin-top: 2px;
`<commit_msg>feat: add the prominent tags to the SearchInput.js<commit_after>import React, {useState, useEffect, useRef} from 'react'
import {connect} from 'react-redux'
import styled from 'styled-components'
import {Creatable} from "react-select"
import {translate} from "react-i18next"
import isEqual from "lodash/isEqual"

const SearchInput = (props) => {

  const {t} = props

  const [allOptions, setAllOptions] = useState([])
  const [options, setOptions] = useState([])
  const [prominentOptions, setProminentOptions] = useState([])
  const prevAllTags = useRef([])

  useEffect(() => {
    const prominentOptions = props.prominentTags.map(tag => ({label: `#${tag}`, value: `#${tag}`}))

    setProminentOptions(prominentOptions)
    setOptions(prominentOptions)
  }, [props.prominentTags])

  useEffect(() => {
    if (!isEqual(prevAllTags.current, props.allTags)) {
      prevAllTags.current = props.allTags
      const options = props.allTags.map(tag => ({label: `#${tag}`, value: `#${tag}`}))

      setAllOptions(options)
    }
  }, [props.allTags])

  const filterOptions = (input) => {
    const searchString = input.toLowerCase().trim();
    if (searchString.length === 0) {
      setOptions(prominentOptions)
      return
    }

    const res = allOptions.filter(d => d.label.match(searchString));

    const newOptions = res.slice(0, 5)

    setOptions(newOptions)
  }

  const onInputChange = (input, actionMeta) => {
    const {action} = actionMeta
    if (action === "input-blur" || action === "menu-close") {
      return
    }

    const tokens = input.split(' ')
    const lastToken = tokens[tokens.length-1].trim()
    onChange(input)
    filterOptions(lastToken)
  }

  const handleChange = (newValue, actionMeta) => {
    // TODO: dispatch on change
    if (!!newValue && newValue.length) {
      let newInputValue = props.searchText
      if (newInputValue.length && newInputValue.slice(-1) === ' ') {
        newInputValue = `${newInputValue} ${newValue[0].label}`
      } else {
        const tokens = props.searchText.trim().split(" ").filter(t => t.length)
        tokens.pop()
        tokens.push(newValue[0].label)
        newInputValue = tokens.join(' ')
      }

      onChange(newInputValue)
    }
  };

  const onChange = (newInputValue) => {
    props.onChange(newInputValue)
  }

  return (
    <Recommender
      delimeter=" "
      formatCreateLabel={(inputValue) => (inputValue)}
      filterOption={(option) => true}
      isMulti
      styles={{
        control: (provided, state) => ({
          ...provided,
          border: 'none',
          boxShadow: 'none'
        }),
        menu: (provided, state) => ({
          ...provided,
          width: '85%'
        })
      }}
      onBlur={() => {}}
      onInputChange={onInputChange}
      onChange={handleChange}
      onKeyDown={props.onKeyDown}
      options={options}
      placeholder={t("searchbar.placeholder")}
      inputValue={props.searchText}
      value={[]}
    />
  )
}

const mapStateToProps = ({search}) => {
  return {
    allTags: search.tags.map(tagPair => tagPair[0]),
    prominentTags: search.prominentTags
  }
}

export default translate('translation')(connect(mapStateToProps)(SearchInput))

const Recommender = styled(Creatable)`
  font-size: 1em !important;
  line-height: 1em !important;
  font-weight: 100;
  padding-left: 2.7em !important;
  padding-right: 0.5em !important;
  z-index: 999 !important;
  margin-top: 2px;
`<|endoftext|>


<commit_before>/**
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var conn = require('../common/connection.js'),
    entity = require('./entity.js'),
    Query = require('./query.js').Query,
    util = require('../common/util.js');

var DATASTORE_BASE_URL = 'https://www.googleapis.com/datastore/v1beta2/datasets',
    SCOPES = [
      'https://www.googleapis.com/auth/datastore',
      'https://www.googleapis.com/auth/userinfo.email'],
    MODE_NON_TRANSACTIONAL = 'NON_TRANSACTIONAL',
    MODE_TRANSACTIONAL = 'TRANSACTIONAL';

/**
 * Transaction represents a Datastore transaction.
 */
function Transaction(conn, datasetId) {
  this.conn = conn;
  this.datasetId = datasetId;
  // the default transaction has no id.
  // if id is not set, run operations non-transactional.
  this.id = null;
  this.isFinalized = false;
}

/**
 * Begins starts a remote transaction and identifies the current
 * transaction instance with the remote transaction's ID.
 */
Transaction.prototype.begin = function(callback) {
  var that = this;
  this.makeReq('beginTransaction', null, function(err, resp) {
    if (!err) {
      that.id = resp;
    }
    callback && callback(err);
  });
};

/**
 * Rollback rollsback a transaction remotely and finalizes
 * the current transaction instance.
 */
Transaction.prototype.rollback = function(callback) {
  var that = this;
  var req = { transaction: this.id };
  this.makeReq('rollback', req, function(err, resp) {
    if (!err) {
      that.isFinalized = true;
    }
    callback && callback(err);
  });
};

/**
 * Commit commits the remote transaction and finalizes the
 * current transaction insance.
 */
Transaction.prototype.commit = function(callback) {
  var that = this;
  var req = { transaction: this.id };
  this.makeReq('commit', req, function(err, resp) {
    if (!err) {
      that.isFinalized = true;
    }
    callback && callback(err);
  });
};

/**
 * Finalize commits a transction if it's not finalized yet.
 */
Transaction.prototype.finalize = function(callback) {
  if (!this.isFinalized) {
    return this.commit(callback);
  }
  callback();
};

/**
 * Get retrieves the objects identified with the specified key in the
 * current transaction.
 * @param  {Array}    key
 * @param  {Function} callback
 */
Transaction.prototype.get = function(key, callback) {
  this.getAll([key], function(err, keys, objs) {
    if (err || objs.length < 0) {
      return callback(err);
    }
    return callback(null, keys[0], objs[0]);
  });
};

/**
 * Gets all objects identified with the specified list of keys
 * in the current transaction.
 * @param  {Array<Array>}   keys
 * @param  {Function}       callback
 */
Transaction.prototype.getAll = function(keys, callback) {
  keysProto = [];
  keys.forEach(function(k) {
    keysProto.push(entity.keyToKeyProto(this.id, k));
  });
  var req = { keys: keysProto };
  if (this.id) {
    req.transaction = this.id;
  }
  this.makeReq(
      'lookup', req, function(err, resp) {
    if (err) {
      return callback(err);
    }
    var results = [], keys = [];
    resp.found.forEach(function(f) {
      keys.push(entity.keyFromKeyProto(f.entity.key));
      results.push(entity.entityFromEntityProto(f.entity));
    });
    callback && callback(null, keys, results);
  });
};

/**
 * Inserts or updates the specified the object in the current
 * transaction. If the provided key is incomplete, inserts the object
 * and returns the generated identifier.
 * @param  {Array}    key
 * @param  {Object}   obj
 * @param  {Function} callback
 */
Transaction.prototype.save = function(key, obj, callback) {
  this.saveAll([key], [obj], function(err, keys) {
    if (err || !keys) {
      return callback(err);
    }
    if (keys[0]) {
      return callback(err, keys[0]);
    }
    callback();
  });
};

/**
 * Inserts or upates the specified objects in the current transaction.
 * If a key is incomplete, its associated object is inserted and
 * generated identifier is returned.
 * @param  {Array<Array>}   keys
 * @param  {Array<Object>}  objs
 * @param  {Function} callback
 */
Transaction.prototype.saveAll = function(keys, objs, callback) {
  if (keys.length != objs.length) {
    throw new Error('The length of the keys don\'t match the length of the objects');
  }
  var insertIndexes = [];
  var req = {
    mode: MODE_NON_TRANSACTIONAL,
    mutation: {
      update: [],
      insertAutoId: []
    }
  };
  if (this.id) {
    req.transaction = this.id;
    req.mode = MODE_TRANSACTIONAL;
  }
  for (var i = 0; i < keys.length; i++) {
    var e = entity.entityToEntityProto(objs[i]);
    e.key = entity.keyToKeyProto(this.id, keys[i]);
    if (entity.isKeyComplete(keys[i])) {
      req.mutation.update.push(e);
    } else {
      insertIndexes.push(i);
      req.mutation.insertAutoId.push(e);
    }
  }
  this.makeReq(
      'commit', req, function(err, resp) {
    if (err || !resp) {
      return callback(err);
    }
    resp.mutationResult.insertAutoIdKeys = resp.mutationResult.insertAutoIdKeys || [];
    var i = 0;
    resp.mutationResult.insertAutoIdKeys.forEach(function(item) {
      keys[insertIndexes[i++]] = entity.keyFromKeyProto(item);
    });
    callback(null, keys);
  });
};

/**
 * Deletes the entitiy identified with the specified key in the
 * current transaction.
 * @param  {Array}    key
 * @param  {Function} callback
 */
Transaction.prototype.del = function(key, callback) {
  this.delAll([key], callback);
};

/**
 * Deletes all entities identified with the specified list of keys
 * in the current transaction.
 * @param  {Array<Array>}  keys
 * @param  {Function}      callback
 */
Transaction.prototype.delAll = function(keys, callback) {
  keysProto = [];
  keys.forEach(function(k) {
    keysProto.push(entity.keyToKeyProto(this.id, k));
  });
  var req = {
    mode: MODE_NON_TRANSACTIONAL,
    mutation: {
      'delete': keysProto
    }
  };
  if (this.id) {
    req.transaction = this.id;
    req.mode = MODE_TRANSACTIONAL;
  }
  this.makeReq('commit', req, function(err, resp) {
    callback && callback(err);
  });
};

/**
 * Runs a query in the current transaction. If more results are
 * available, a query to retrieve the next page is provided to
 * the callback function. Example:
 *
 * t.runQuery(q, function(err, keys, objs, nextQuery) {
 *   if (err) return;
 *   // if next page is available, retrieve more results
 *   if (nextQuery) {
 *     t.runQuery(nextQuery, ...);
 *   }
 * });
 * @param  {Query}    q
 * @param  {Function} opt_callback
 */
Transaction.prototype.runQuery = function(q, opt_callback) {
  var req = {
    readOptions: {
      transaction: this.id
    },
    query: entity.queryToQueryProto(q)
  };
  if (q.namespace) {
    req.partitionId = {
      namespace: q.namespace
    };
  }
  this.makeReq('runQuery', req, function(err, resp) {
    if (err || !resp.batch || !resp.batch.entityResults) {
      return opt_callback && opt_callback(err);
    }
    var results = resp.batch.entityResults,
        keys = [], objs = [];
    results.forEach(function(r) {
      keys.push(entity.keyFromKeyProto(r.entity.key));
      objs.push(entity.entityFromEntityProto(r.entity));
    });
    var nextQuery = null;
    if (resp.batch.endCursor != q.startVal) {
      nextQuery = q.start(resp.batch.endCursor);
    }
    opt_callback && opt_callback(err, keys, objs, nextQuery);
  });
};

Transaction.prototype.mapQuery = function(q, mapperFn, opt_callback) {
  throw new Error('not yet implemented');
};

/**
 * Makes a request to the API endpoint.
 */
Transaction.prototype.makeReq = function(method, req, callback) {
  // TODO(jbd): Switch to protobuf API.
  this.conn.req({
    method: 'POST',
    uri: DATASTORE_BASE_URL + '/' + this.datasetId + '/' + method,
    json: req
  }, function(err, res, body) {
    if (body && body.error) {
      return callback(body.error, null);
    }
    callback(err, body);
  });
};

/**
 * Creates a new dataset with the provided options.
 * @param {object} opts             Dataset identifier options.
 * @param {string} opts.id          Dataset ID, this is your project ID
 *                                  from Google Developer Console.
 * @param {string} opts.email       Client email of the service account.
 * @param {string} opts.pemFilepath The path to the pem file.
 */
function Dataset(opts) {
  opts = opts || {};
  var id = opts.projectId || '';

  if (id.indexOf("s~") < 0 && id.indexOf("e~") < 0) {
    id = "s~" + id;
  }

  this.id = id;
  this.transaction = new Transaction(new conn.Connection({
    email: opts.email,
    privateKey: opts.pemFilePath,
    scopes: SCOPES
  }), this.id);
}

/**
 * Creates a query from the current dataset to query the specified
 * kinds.
 * @param  {Array<String>} kinds A list of kinds to query.
 * @return {Query}
 */
Dataset.prototype.createQuery = function(kinds) {
  kinds = util.arrayize(kinds);
  return new Query('', kinds);
};

/**
 * Creates a query for collections identified with the specified
 * namespace and kinds.
 *
 * Example usage:
 *   ds.createQueryNS('zoo', ['Lion', 'Chimp'])
 * @param  {string} ns      Namespace to query entities from.
 * @param  {Array<object>}  A list of kinds to query.
 * @return {Query}
 */
Dataset.prototype.createQueryNS = function(ns, kinds) {
  kinds = util.arrayize(kinds);
  return new Query(ns, kinds);
};

/**
 * Gets the object identified with the provided key.
 * @param  {Key}        key       A non-incomplete key.
 * @param  {Function}   callback  The callback function.
 */
Dataset.prototype.get = function(key, callback) {
  this.transaction.get(key, callback);
};

Dataset.prototype.getAll = function(keys, callback) {
  this.transaction.getAll(keys, callback);
};

Dataset.prototype.save = function(key, obj, callback) {
  this.transaction.save(key, obj, callback);
};

Dataset.prototype.saveAll = function(keys, objs, callback) {
  this.transaction.saveAll(keys, objs, callback);
};

Dataset.prototype.del = function(key, callback) {
  this.transaction.del(key, callback);
};

Dataset.prototype.delAll = function(keys, callback) {
  this.transaction.delAll(keys, callback);
};

Dataset.prototype.runQuery = function(q, callback) {
  this.transaction.runQuery(q, callback);
};

Dataset.prototype.runInTransaction = function(fn, callback) {
  var t = new Transaction(this.transaction.conn, this.id);
  var done = function() {
    t.finalize(callback);
  };
  t.begin(function(err, id) {
    if (err) {
      return callback(err);
    }
    fn(t, done);
  });
};

Dataset.prototype.allocateIds = function(incompleteKey, n, callback) {
  if (entity.isKeyComplete(incompleteKey)) {
    throw new Error('An incomplete key should be provided.')
  }
  var incompleteKeys = [];
  for (var i = 0; i < n; i++) {
    incompleteKeys.push(entity.keyToKeyProto(this.id, incompleteKey));
  }
  this.transaction.makeReq(
      'allocateIds', { keys: incompleteKeys }, function(err, resp) {
    if (err) {
      return callback(err);
    }
    var keys = [];
    resp.keys.forEach(function(k) {
      keys.push(entity.keyFromKeyProto(k));
    });
    callback(null ,keys);
  });
};

/**
 * Exports Dataset.
 * @type {Dataset}
 */
module.exports.Dataset = Dataset;

/**
 * Exports Transaction.
 * @type {Transaction}
 */
module.exports.Transaction = Transaction;
<commit_msg>datastore: if no end cursor, there are no new pages.<commit_after>/**
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var conn = require('../common/connection.js'),
    entity = require('./entity.js'),
    Query = require('./query.js').Query,
    util = require('../common/util.js');

var DATASTORE_BASE_URL = 'https://www.googleapis.com/datastore/v1beta2/datasets',
    SCOPES = [
      'https://www.googleapis.com/auth/datastore',
      'https://www.googleapis.com/auth/userinfo.email'],
    MODE_NON_TRANSACTIONAL = 'NON_TRANSACTIONAL',
    MODE_TRANSACTIONAL = 'TRANSACTIONAL';

/**
 * Transaction represents a Datastore transaction.
 */
function Transaction(conn, datasetId) {
  this.conn = conn;
  this.datasetId = datasetId;
  // the default transaction has no id.
  // if id is not set, run operations non-transactional.
  this.id = null;
  this.isFinalized = false;
}

/**
 * Begins starts a remote transaction and identifies the current
 * transaction instance with the remote transaction's ID.
 */
Transaction.prototype.begin = function(callback) {
  var that = this;
  this.makeReq('beginTransaction', null, function(err, resp) {
    if (!err) {
      that.id = resp;
    }
    callback && callback(err);
  });
};

/**
 * Rollback rollsback a transaction remotely and finalizes
 * the current transaction instance.
 */
Transaction.prototype.rollback = function(callback) {
  var that = this;
  var req = { transaction: this.id };
  this.makeReq('rollback', req, function(err, resp) {
    if (!err) {
      that.isFinalized = true;
    }
    callback && callback(err);
  });
};

/**
 * Commit commits the remote transaction and finalizes the
 * current transaction insance.
 */
Transaction.prototype.commit = function(callback) {
  var that = this;
  var req = { transaction: this.id };
  this.makeReq('commit', req, function(err, resp) {
    if (!err) {
      that.isFinalized = true;
    }
    callback && callback(err);
  });
};

/**
 * Finalize commits a transction if it's not finalized yet.
 */
Transaction.prototype.finalize = function(callback) {
  if (!this.isFinalized) {
    return this.commit(callback);
  }
  callback();
};

/**
 * Get retrieves the objects identified with the specified key in the
 * current transaction.
 * @param  {Array}    key
 * @param  {Function} callback
 */
Transaction.prototype.get = function(key, callback) {
  this.getAll([key], function(err, keys, objs) {
    if (err || objs.length < 0) {
      return callback(err);
    }
    return callback(null, keys[0], objs[0]);
  });
};

/**
 * Gets all objects identified with the specified list of keys
 * in the current transaction.
 * @param  {Array<Array>}   keys
 * @param  {Function}       callback
 */
Transaction.prototype.getAll = function(keys, callback) {
  keysProto = [];
  keys.forEach(function(k) {
    keysProto.push(entity.keyToKeyProto(this.id, k));
  });
  var req = { keys: keysProto };
  if (this.id) {
    req.transaction = this.id;
  }
  this.makeReq(
      'lookup', req, function(err, resp) {
    if (err) {
      return callback(err);
    }
    var results = [], keys = [];
    resp.found.forEach(function(f) {
      keys.push(entity.keyFromKeyProto(f.entity.key));
      results.push(entity.entityFromEntityProto(f.entity));
    });
    callback && callback(null, keys, results);
  });
};

/**
 * Inserts or updates the specified the object in the current
 * transaction. If the provided key is incomplete, inserts the object
 * and returns the generated identifier.
 * @param  {Array}    key
 * @param  {Object}   obj
 * @param  {Function} callback
 */
Transaction.prototype.save = function(key, obj, callback) {
  this.saveAll([key], [obj], function(err, keys) {
    if (err || !keys) {
      return callback(err);
    }
    if (keys[0]) {
      return callback(err, keys[0]);
    }
    callback();
  });
};

/**
 * Inserts or upates the specified objects in the current transaction.
 * If a key is incomplete, its associated object is inserted and
 * generated identifier is returned.
 * @param  {Array<Array>}   keys
 * @param  {Array<Object>}  objs
 * @param  {Function} callback
 */
Transaction.prototype.saveAll = function(keys, objs, callback) {
  if (keys.length != objs.length) {
    throw new Error('The length of the keys don\'t match the length of the objects');
  }
  var insertIndexes = [];
  var req = {
    mode: MODE_NON_TRANSACTIONAL,
    mutation: {
      update: [],
      insertAutoId: []
    }
  };
  if (this.id) {
    req.transaction = this.id;
    req.mode = MODE_TRANSACTIONAL;
  }
  for (var i = 0; i < keys.length; i++) {
    var e = entity.entityToEntityProto(objs[i]);
    e.key = entity.keyToKeyProto(this.id, keys[i]);
    if (entity.isKeyComplete(keys[i])) {
      req.mutation.update.push(e);
    } else {
      insertIndexes.push(i);
      req.mutation.insertAutoId.push(e);
    }
  }
  this.makeReq(
      'commit', req, function(err, resp) {
    if (err || !resp) {
      return callback(err);
    }
    resp.mutationResult.insertAutoIdKeys = resp.mutationResult.insertAutoIdKeys || [];
    var i = 0;
    resp.mutationResult.insertAutoIdKeys.forEach(function(item) {
      keys[insertIndexes[i++]] = entity.keyFromKeyProto(item);
    });
    callback(null, keys);
  });
};

/**
 * Deletes the entitiy identified with the specified key in the
 * current transaction.
 * @param  {Array}    key
 * @param  {Function} callback
 */
Transaction.prototype.del = function(key, callback) {
  this.delAll([key], callback);
};

/**
 * Deletes all entities identified with the specified list of keys
 * in the current transaction.
 * @param  {Array<Array>}  keys
 * @param  {Function}      callback
 */
Transaction.prototype.delAll = function(keys, callback) {
  keysProto = [];
  keys.forEach(function(k) {
    keysProto.push(entity.keyToKeyProto(this.id, k));
  });
  var req = {
    mode: MODE_NON_TRANSACTIONAL,
    mutation: {
      'delete': keysProto
    }
  };
  if (this.id) {
    req.transaction = this.id;
    req.mode = MODE_TRANSACTIONAL;
  }
  this.makeReq('commit', req, function(err, resp) {
    callback && callback(err);
  });
};

/**
 * Runs a query in the current transaction. If more results are
 * available, a query to retrieve the next page is provided to
 * the callback function. Example:
 *
 * t.runQuery(q, function(err, keys, objs, nextQuery) {
 *   if (err) return;
 *   // if next page is available, retrieve more results
 *   if (nextQuery) {
 *     t.runQuery(nextQuery, ...);
 *   }
 * });
 * @param  {Query}    q
 * @param  {Function} opt_callback
 */
Transaction.prototype.runQuery = function(q, opt_callback) {
  var req = {
    readOptions: {
      transaction: this.id
    },
    query: entity.queryToQueryProto(q)
  };
  if (q.namespace) {
    req.partitionId = {
      namespace: q.namespace
    };
  }
  this.makeReq('runQuery', req, function(err, resp) {
    if (err || !resp.batch || !resp.batch.entityResults) {
      return opt_callback && opt_callback(err);
    }
    var results = resp.batch.entityResults,
        keys = [], objs = [];
    results.forEach(function(r) {
      keys.push(entity.keyFromKeyProto(r.entity.key));
      objs.push(entity.entityFromEntityProto(r.entity));
    });
    var nextQuery = null;
    if (resp.batch.endCursor && resp.batch.endCursor != q.startVal) {
      nextQuery = q.start(resp.batch.endCursor);
    }
    opt_callback && opt_callback(err, keys, objs, nextQuery);
  });
};

Transaction.prototype.mapQuery = function(q, mapperFn, opt_callback) {
  throw new Error('not yet implemented');
};

/**
 * Makes a request to the API endpoint.
 */
Transaction.prototype.makeReq = function(method, req, callback) {
  // TODO(jbd): Switch to protobuf API.
  this.conn.req({
    method: 'POST',
    uri: DATASTORE_BASE_URL + '/' + this.datasetId + '/' + method,
    json: req
  }, function(err, res, body) {
    if (body && body.error) {
      return callback(body.error, null);
    }
    callback(err, body);
  });
};

/**
 * Creates a new dataset with the provided options.
 * @param {object} opts             Dataset identifier options.
 * @param {string} opts.id          Dataset ID, this is your project ID
 *                                  from Google Developer Console.
 * @param {string} opts.email       Client email of the service account.
 * @param {string} opts.pemFilepath The path to the pem file.
 */
function Dataset(opts) {
  opts = opts || {};
  var id = opts.projectId || '';

  if (id.indexOf("s~") < 0 && id.indexOf("e~") < 0) {
    id = "s~" + id;
  }

  this.id = id;
  this.transaction = new Transaction(new conn.Connection({
    email: opts.email,
    privateKey: opts.pemFilePath,
    scopes: SCOPES
  }), this.id);
}

/**
 * Creates a query from the current dataset to query the specified
 * kinds.
 * @param  {Array<String>} kinds A list of kinds to query.
 * @return {Query}
 */
Dataset.prototype.createQuery = function(kinds) {
  kinds = util.arrayize(kinds);
  return new Query('', kinds);
};

/**
 * Creates a query for collections identified with the specified
 * namespace and kinds.
 *
 * Example usage:
 *   ds.createQueryNS('zoo', ['Lion', 'Chimp'])
 * @param  {string} ns      Namespace to query entities from.
 * @param  {Array<object>}  A list of kinds to query.
 * @return {Query}
 */
Dataset.prototype.createQueryNS = function(ns, kinds) {
  kinds = util.arrayize(kinds);
  return new Query(ns, kinds);
};

/**
 * Gets the object identified with the provided key.
 * @param  {Key}        key       A non-incomplete key.
 * @param  {Function}   callback  The callback function.
 */
Dataset.prototype.get = function(key, callback) {
  this.transaction.get(key, callback);
};

Dataset.prototype.getAll = function(keys, callback) {
  this.transaction.getAll(keys, callback);
};

Dataset.prototype.save = function(key, obj, callback) {
  this.transaction.save(key, obj, callback);
};

Dataset.prototype.saveAll = function(keys, objs, callback) {
  this.transaction.saveAll(keys, objs, callback);
};

Dataset.prototype.del = function(key, callback) {
  this.transaction.del(key, callback);
};

Dataset.prototype.delAll = function(keys, callback) {
  this.transaction.delAll(keys, callback);
};

Dataset.prototype.runQuery = function(q, callback) {
  this.transaction.runQuery(q, callback);
};

Dataset.prototype.runInTransaction = function(fn, callback) {
  var t = new Transaction(this.transaction.conn, this.id);
  var done = function() {
    t.finalize(callback);
  };
  t.begin(function(err, id) {
    if (err) {
      return callback(err);
    }
    fn(t, done);
  });
};

Dataset.prototype.allocateIds = function(incompleteKey, n, callback) {
  if (entity.isKeyComplete(incompleteKey)) {
    throw new Error('An incomplete key should be provided.')
  }
  var incompleteKeys = [];
  for (var i = 0; i < n; i++) {
    incompleteKeys.push(entity.keyToKeyProto(this.id, incompleteKey));
  }
  this.transaction.makeReq(
      'allocateIds', { keys: incompleteKeys }, function(err, resp) {
    if (err) {
      return callback(err);
    }
    var keys = [];
    resp.keys.forEach(function(k) {
      keys.push(entity.keyFromKeyProto(k));
    });
    callback(null ,keys);
  });
};

/**
 * Exports Dataset.
 * @type {Dataset}
 */
module.exports.Dataset = Dataset;

/**
 * Exports Transaction.
 * @type {Transaction}
 */
module.exports.Transaction = Transaction;
<|endoftext|>