<commit_before>"use strict";

var neo4j = require("neo4j");
var q = require("q");
var util = require("util");

//var server = new neo4j.GraphDatabase("http://localhost:7474");

/**
 * Directory
 */

var Directory = function (server) {
	this.server = server;
}

Directory.prototype.root_area = function () {
	var dir = this;

	return promise_query(this.server,
		[
			"MATCH (root:Area)",
			"WHERE NOT ()-[:PARENT_OF]->(root)",
			"RETURN root"
		],
		{},
		function (results) {
			var node = results[0]["root"]; // should only be one result
			return new Area(dir, node.id, node.data);
		}
	);
}

Directory.prototype.area = function (area_id) {
	var dir = this;
	var deferred = q.defer();
	this.server.getNodeById(area_id, function (err, node) {
		if (err) {
			deferred.reject(err);
			throw err;
		}
		var data = node.data;
		var area = new Area(dir, node.id, data);
		deferred.resolve(area);
		return;
	});
	return deferred.promise;
}

exports.Directory = Directory;

/**
 * Area
 */

var Area = function (dir, area_id, area_info) {
	this.directory = dir;
	this.area_id = area_id;
	this.name = area_info.name;
	if (area_info.note !== undefined) this.note = area_info.note;
}

Area.prototype.descend_along_path = function (path) {
	var area = this;

	// construct query
	var query = ["START n=node({root_area_id}) MATCH (n)"];
	path.forEach(function (area_name, depth) {
		query.push(util.format(
			"-[:PARENT_OF]->(x%d:Area { name: \"%s\"})",
			depth, area_name
		));
	});
	var result_node_name = util.format("x%d", path.length - 1);
	query.push(util.format("RETURN %s", result_node_name));

	return promise_query(this.directory.server,
		query, {"root_area_id" : this.get_area_id()},
		function (target_node) {
			var target = target_node[0][result_node_name];
			return new Area(
				area.directory, target.id, target.data
			);
		}
	);
}

Area.prototype.parent = function () {
	var area = this;

	return promise_query(
		this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (parent:Area)-[:PARENT_OF]->(n)",
			"RETURN parent"
		],
		{"area_id" : this.area_id},
		function (parents) {
			if (!parents.length) return null;
			var parent = parents[0]["parent"];
			return new Area(area.directory, parent.id, parent.data);
		}
	);
}

Area.prototype.children = function () {
	var area = this;

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (n)-[:PARENT_OF]->(child:Area)",
			"RETURN child"
		],
		{"area_id" : this.area_id},
		function (child_nodes) {
			return child_nodes.map(function (child_node) {
				var child_area = child_node["child"];
				return new Area(
					area.directory, child_area.id, child_area.data
				);
			});
		}
	);
}

Area.prototype.descendents = function () {
	var area = this;

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (n)-[depth:PARENT_OF*]->(dp:Area)",
			"OPTIONAL MATCH (dp)-[:PARENT_OF]->(d:Area)",
			"RETURN d, depth, dp"
		],
		{"area_id" : this.area_id},
		function (descendents) {
			var tree_dict = {};
			tree_dict[area.area_id] = [];
			var area_dict = {};
			area_dict[area.area_id] = area;

			descendents.forEach(function (d) {
				var depth = d.depth.length;
				if (!d.d) {
					if (depth == 1) {
						tree_dict[area.area_id].push(d.dp.id);
						area_dict[d.dp.id] = new Area(area.directory, d.dp.id, d.dp.data);
					}
				} else {
					var depth = d.depth.length;

					if (depth == 1 && tree_dict[area.area_id].indexOf(d.dp.id) == -1) {
						tree_dict[area.area_id].push(d.dp.id);
						area_dict[d.dp.id] = new Area(area.directory, d.dp.id, d.dp.data);
					}

					if (!(d.dp.id in tree_dict)) {
						tree_dict[d.dp.id] = [];
					}
					tree_dict[d.dp.id].push(d.d.id);
					area_dict[d.d.id] = new Area(area.directory, d.d.id, d.d.data);
				}
			});
			var queue = [];
			var attach_descendents = function (node) {
				node.children = [];
				if (node.area.area_id in tree_dict) {
					tree_dict[node.area.area_id].forEach(function (child) {
						var new_area = {area: area_dict[child]};
						attach_descendents(new_area);
						node.children.push(new_area);
					});
				}
			}

			var root_node = {"area" : area};
			attach_descendents(root_node);

			return root_node;
		}
	);
}

Area.prototype.path = function (base_area_id) {
	var area = this;

	if (base_area_id) base_area_id = parseInt(base_area_id);

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			base_area_id ? ", base=node({base_area})" : "",
			"MATCH (m:Area)-[link:PARENT_OF*]->(n)",
			base_area_id
				? "WHERE (base)-[:PARENT_OF*]->(m) OR (base) = (m)"
				: ""
			,
			"RETURN link, m, n"
		],
		{"area_id" : this.area_id, "base_area" : base_area_id},
		function (results) {
			var path = [];
			if (results.length) {
				path[0] = new Area(area.directory, results[0].n.id, results[0].n.data);
				results.forEach(function (result) {
					var pos = result.link.length;
					path[pos] = new Area(area.directory, result.m.id, result.m.data);
				});
			}
			return path.reverse().slice(1);
		}
	);
}

Area.prototype.search = function (search_str) {
	var area = this;
	search_str = search_str.toLowerCase().trim().replace(/ +/g, " ").split(" ");
	var search_regex = util.format("(%s)", search_str.join("|"));
	console.log(search_regex);

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (m:Area)-[link:PARENT_OF*]->(target)",
			util.format("WHERE target.name =~ \"(?i).*%s.*\"", search_regex),
			"AND ((n)-[:PARENT_OF*]->(m:Area) OR (n) = (m))",
			"RETURN target, link, m"
		],
		{"area_id" : this.area_id},
		function (results) {
			if (!results) return [];
			var paths = {};
			results.forEach(function (result) {
				var a = result.target;
				paths[a.id] =[];
				paths[a.id][0] = new Area(area.directory, a.id, a.data);
			});
			results.forEach(function (result) {
				var distance = result.link.length;
				var target = result.target;
				var m = result.m;

				// delete if part of path
				if (m.id in paths) {
					console.log(m.id);
					delete paths[m.id];
				}

				if (target.id in paths) {
					paths[target.id][distance] = new Area(
						area.directory, m.id, m.data
					);
				}
			});
			var result_list = [];
			Object.keys(paths).forEach(function (target_id) {
				result_list.push(paths[target_id].reverse().slice(1));
			});
			// further filtering
			var reduced = result_list.filter(function (path) {
				var pathstr = path.map(function (path_item) {
					return path_item.name;
				}).join(" ").toLowerCase();
				return search_str.every(function (term) {
					return pathstr.indexOf(term) > -1;
				});
			});

			return reduced;
		}
	);
}

Area.prototype.collections = function () {
	var area = this;

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (collection:Collection)-[:RESPONSIBLE_FOR*]->(n)",
			"WHERE NOT ()-[:COMES_BEFORE]->(collection)",
			"RETURN collection"
		],
		{"area_id" : this.area_id},
		function (results) {
			return results.map(function (result) {
				var collection = result["collection"];
				return new Collection(
					area.directory, collection.id
				);
			});
		}
	);
}

Area.prototype.all_contacts = function () {
	var area = this;

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (n)<-[:RESPONSIBLE_FOR]-(c:Collection)",
			"MATCH (contact:Contact)-[:IN_COLLECTION]->(c)",
			"OPTIONAL MATCH (contact)-[:HAS_URL]->(url:Url)",
			"OPTIONAL MATCH (c)-[succ:COMES_BEFORE]->(c2:Collection)",
			"RETURN contact,url,c,succ,c2"
		],
		{"area_id" : this.area_id},
		function (results) {

			var contacts = [];
			var all_contacts = {};
			var parent = {};
			results.forEach(function (result) {
				var coll_id = result["c"].id;
				if (!(coll_id in all_contacts)) {
					all_contacts[coll_id] = {
						contacts : [],
						successors : []
					};

					if (result["c2"] != null) {
						parent[result["c2"].id] = {
							"parent" : result["c"].id,
							"note" : result["succ"].data.note
						};
					}
				}
				all_contacts[coll_id].contacts.push(
					new Contact(
						area.directory,
						result["contact"].id,
						result["contact"].data,
						result["url"] ? result["url"].data : null
					)
				);

			});

			Object.keys(all_contacts).forEach(function (coll) {
				if (!(coll in parent)) {
					contacts.push(all_contacts[coll]);
				} else {
					all_contacts[parent[coll].parent].successors.push({
						"collection" : all_contacts[coll],
						"note" : parent[coll].note
					});
				}
			});

			return contacts;
		}
	);
}

Area.prototype.descendent_contact_count = function () {
	var area = this;

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (n)-[:PARENT_OF*]->()<-[*]-(a:Contact)",
			"RETURN COUNT(a)"
		],
		{area_id : this.area_id},
		function (results) {
			return {"contacts" : results[0]["COUNT(a)"]};
		}
	);
}

Area.prototype.get_area_id = function () {
	return this.area_id;
}

Area.prototype.get_name = function () {
	return this.name;
}

Area.prototype.get_notes = function () {
	return this.notes;
}

exports.Area = Area;

/**
 * Collection
 */

var Collection = function (directory, collection_id) {
	this.directory = directory;
	this.collection_id = collection_id;
}

Collection.prototype.contacts = function () {
	var collection = this;

	return promise_query(this.directory.server,
		[
			"START n=node({collection_id})",
			"MATCH (contact:Contact)-[:IN_COLLECTION]->(n)",
			"OPTIONAL MATCH (contact)-[:HAS_URL]->(url:Url)",
			"RETURN contact, url",
			"ORDER BY contact.last_name, contact.first_name"
		],
		{"collection_id" : this.collection_id},
		function (results) {
			return results.map(function (result) {
				var contact = result["contact"];
				var url = result["url"];
				console.log("urL");
				console.log(url);
				return new Contact(
					collection.directory, contact.id, contact.data, url ? url.data : null
				);
			});
		}
	);
}

Collection.prototype.successors = function () {
	var collection = this;

	return promise_query(this.directory.server,
		[
			"START n=node({collection_id})",
			"MATCH (n)-[link:COMES_BEFORE]->(succ:Collection)",
			"RETURN succ, link"
		],
		{"collection_id" : this.collection_id},
		function (results) {
			return results.map(function (result) {
				return new Collection(
					collection.directory, result["succ"].id
				);
			})
		}
	);
}

exports.Collection = Collection;

/**
 * Contact
 */

var Contact = function (directory, contact_id, contact_info, url) {

	this.directory = directory;
	this.contact_id = contact_id;
	this.contact_info = contact_info;
	if (url) this.url = url;
}

/*(Contact.prototype.working_times = function () {

}*/

Contact.prototype.get_contact_id = function () {
	return this.contact_id;
}

Contact.prototype.get_info = function () {
	return this.contact_info;
}

exports.Contact = Contact;

/**
 * Patterns
 */

var promise_query = function (server, query, params, process_results) {
	var query_str = query.join(" ");
	var deferred = q.defer();
	server.query(query_str, params, function (err, results) {
		if (err) {
			deferred.reject(err);
		}
		var processed_results = process_results(results);
		deferred.resolve(processed_results);
	});
	return deferred.promise;
}<commit_msg>Added descendent contact count to search results.<commit_after>"use strict";

var neo4j = require("neo4j");
var q = require("q");
var util = require("util");

//var server = new neo4j.GraphDatabase("http://localhost:7474");

/**
 * Directory
 */

var Directory = function (server) {
	this.server = server;
}

Directory.prototype.root_area = function () {
	var dir = this;

	return promise_query(this.server,
		[
			"MATCH (root:Area)",
			"WHERE NOT ()-[:PARENT_OF]->(root)",
			"RETURN root"
		],
		{},
		function (results) {
			var node = results[0]["root"]; // should only be one result
			return new Area(dir, node.id, node.data);
		}
	);
}

Directory.prototype.area = function (area_id) {
	var dir = this;
	var deferred = q.defer();
	this.server.getNodeById(area_id, function (err, node) {
		if (err) {
			deferred.reject(err);
			throw err;
		}
		var data = node.data;
		var area = new Area(dir, node.id, data);
		deferred.resolve(area);
		return;
	});
	return deferred.promise;
}

exports.Directory = Directory;

/**
 * Area
 */

var Area = function (dir, area_id, area_info) {
	this.directory = dir;
	this.area_id = area_id;
	this.name = area_info.name;
	if (area_info.note !== undefined) this.note = area_info.note;
}

Area.prototype.descend_along_path = function (path) {
	var area = this;

	// construct query
	var query = ["START n=node({root_area_id}) MATCH (n)"];
	path.forEach(function (area_name, depth) {
		query.push(util.format(
			"-[:PARENT_OF]->(x%d:Area { name: \"%s\"})",
			depth, area_name
		));
	});
	var result_node_name = util.format("x%d", path.length - 1);
	query.push(util.format("RETURN %s", result_node_name));

	return promise_query(this.directory.server,
		query, {"root_area_id" : this.get_area_id()},
		function (target_node) {
			var target = target_node[0][result_node_name];
			return new Area(
				area.directory, target.id, target.data
			);
		}
	);
}

Area.prototype.parent = function () {
	var area = this;

	return promise_query(
		this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (parent:Area)-[:PARENT_OF]->(n)",
			"RETURN parent"
		],
		{"area_id" : this.area_id},
		function (parents) {
			if (!parents.length) return null;
			var parent = parents[0]["parent"];
			return new Area(area.directory, parent.id, parent.data);
		}
	);
}

Area.prototype.children = function () {
	var area = this;

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (n)-[:PARENT_OF]->(child:Area)",
			"RETURN child"
		],
		{"area_id" : this.area_id},
		function (child_nodes) {
			return child_nodes.map(function (child_node) {
				var child_area = child_node["child"];
				return new Area(
					area.directory, child_area.id, child_area.data
				);
			});
		}
	);
}

Area.prototype.descendents = function () {
	var area = this;

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (n)-[depth:PARENT_OF*]->(dp:Area)",
			"OPTIONAL MATCH (dp)-[:PARENT_OF]->(d:Area)",
			"RETURN d, depth, dp"
		],
		{"area_id" : this.area_id},
		function (descendents) {
			var tree_dict = {};
			tree_dict[area.area_id] = [];
			var area_dict = {};
			area_dict[area.area_id] = area;

			descendents.forEach(function (d) {
				var depth = d.depth.length;
				if (!d.d) {
					if (depth == 1) {
						tree_dict[area.area_id].push(d.dp.id);
						area_dict[d.dp.id] = new Area(area.directory, d.dp.id, d.dp.data);
					}
				} else {
					var depth = d.depth.length;

					if (depth == 1 && tree_dict[area.area_id].indexOf(d.dp.id) == -1) {
						tree_dict[area.area_id].push(d.dp.id);
						area_dict[d.dp.id] = new Area(area.directory, d.dp.id, d.dp.data);
					}

					if (!(d.dp.id in tree_dict)) {
						tree_dict[d.dp.id] = [];
					}
					tree_dict[d.dp.id].push(d.d.id);
					area_dict[d.d.id] = new Area(area.directory, d.d.id, d.d.data);
				}
			});
			var queue = [];
			var attach_descendents = function (node) {
				node.children = [];
				if (node.area.area_id in tree_dict) {
					tree_dict[node.area.area_id].forEach(function (child) {
						var new_area = {area: area_dict[child]};
						attach_descendents(new_area);
						node.children.push(new_area);
					});
				}
			}

			var root_node = {"area" : area};
			attach_descendents(root_node);

			return root_node;
		}
	);
}

Area.prototype.path = function (base_area_id) {
	var area = this;

	if (base_area_id) base_area_id = parseInt(base_area_id);

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			base_area_id ? ", base=node({base_area})" : "",
			"MATCH (m:Area)-[link:PARENT_OF*]->(n)",
			base_area_id
				? "WHERE (base)-[:PARENT_OF*]->(m) OR (base) = (m)"
				: ""
			,
			"RETURN link, m, n"
		],
		{"area_id" : this.area_id, "base_area" : base_area_id},
		function (results) {
			var path = [];
			if (results.length) {
				path[0] = new Area(area.directory, results[0].n.id, results[0].n.data);
				results.forEach(function (result) {
					var pos = result.link.length;
					path[pos] = new Area(area.directory, result.m.id, result.m.data);
				});
			}
			return path.reverse().slice(1);
		}
	);
}

Area.prototype.search = function (search_str) {
	var area = this;
	search_str = search_str.toLowerCase().trim().replace(/ +/g, " ").split(" ");
	var search_regex = util.format("(%s)", search_str.join("|"));

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (m:Area)-[link:PARENT_OF*]->(target)",
			util.format("WHERE target.name =~ \"(?i).*%s.*\"", search_regex),
			"AND ((n)-[:PARENT_OF*]->(m:Area) OR (n) = (m))",
			"OPTIONAL MATCH (target)-[:PARENT_OF*]->()<-[*]-(a:Contact)",
			"RETURN target, link, m, COUNT(a)"
		],
		{"area_id" : this.area_id},
		function (results) {
			if (!results) return [];
			var paths = {};
			results.forEach(function (result) {
				var a = result.target;
				paths[a.id] = [];
				paths[a.id][0] = new Area(area.directory, a.id, a.data);
				paths[a.id][0].descendent_contact_count = result["COUNT(a)"];
			});
			results.forEach(function (result) {
				var distance = result.link.length;
				var target = result.target;
				var m = result.m;

				// delete if part of path
				if (m.id in paths) {
					console.log(m.id);
					delete paths[m.id];
				}

				if (target.id in paths) {
					paths[target.id][distance] = new Area(
						area.directory, m.id, m.data
					);
				}
			});
			var result_list = [];
			Object.keys(paths).forEach(function (target_id) {
				result_list.push(paths[target_id].reverse().slice(1));
			});
			// further filtering
			var reduced = result_list.filter(function (path) {
				var pathstr = path.map(function (path_item) {
					return path_item.name;
				}).join(" ").toLowerCase();
				return search_str.every(function (term) {
					return pathstr.indexOf(term) > -1;
				});
			});

			return reduced;
		}
	);
}

Area.prototype.collections = function () {
	var area = this;

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (collection:Collection)-[:RESPONSIBLE_FOR*]->(n)",
			"WHERE NOT ()-[:COMES_BEFORE]->(collection)",
			"RETURN collection"
		],
		{"area_id" : this.area_id},
		function (results) {
			return results.map(function (result) {
				var collection = result["collection"];
				return new Collection(
					area.directory, collection.id
				);
			});
		}
	);
}

Area.prototype.all_contacts = function () {
	var area = this;

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (n)<-[:RESPONSIBLE_FOR]-(c:Collection)",
			"MATCH (contact:Contact)-[:IN_COLLECTION]->(c)",
			"OPTIONAL MATCH (contact)-[:HAS_URL]->(url:Url)",
			"OPTIONAL MATCH (c)-[succ:COMES_BEFORE]->(c2:Collection)",
			"RETURN contact,url,c,succ,c2"
		],
		{"area_id" : this.area_id},
		function (results) {

			var contacts = [];
			var all_contacts = {};
			var parent = {};
			results.forEach(function (result) {
				var coll_id = result["c"].id;
				if (!(coll_id in all_contacts)) {
					all_contacts[coll_id] = {
						contacts : [],
						successors : []
					};

					if (result["c2"] != null) {
						parent[result["c2"].id] = {
							"parent" : result["c"].id,
							"note" : result["succ"].data.note
						};
					}
				}
				all_contacts[coll_id].contacts.push(
					new Contact(
						area.directory,
						result["contact"].id,
						result["contact"].data,
						result["url"] ? result["url"].data : null
					)
				);

			});

			Object.keys(all_contacts).forEach(function (coll) {
				if (!(coll in parent)) {
					contacts.push(all_contacts[coll]);
				} else {
					all_contacts[parent[coll].parent].successors.push({
						"collection" : all_contacts[coll],
						"note" : parent[coll].note
					});
				}
			});

			return contacts;
		}
	);
}

Area.prototype.descendent_contact_count = function () {
	var area = this;

	return promise_query(this.directory.server,
		[
			"START n=node({area_id})",
			"MATCH (n)-[:PARENT_OF*]->()<-[*]-(a:Contact)",
			"RETURN COUNT(a)"
		],
		{area_id : this.area_id},
		function (results) {
			return {"contacts" : results[0]["COUNT(a)"]};
		}
	);
}

Area.prototype.get_area_id = function () {
	return this.area_id;
}

Area.prototype.get_name = function () {
	return this.name;
}

Area.prototype.get_notes = function () {
	return this.notes;
}

exports.Area = Area;

/**
 * Collection
 */

var Collection = function (directory, collection_id) {
	this.directory = directory;
	this.collection_id = collection_id;
}

Collection.prototype.contacts = function () {
	var collection = this;

	return promise_query(this.directory.server,
		[
			"START n=node({collection_id})",
			"MATCH (contact:Contact)-[:IN_COLLECTION]->(n)",
			"OPTIONAL MATCH (contact)-[:HAS_URL]->(url:Url)",
			"RETURN contact, url",
			"ORDER BY contact.last_name, contact.first_name"
		],
		{"collection_id" : this.collection_id},
		function (results) {
			return results.map(function (result) {
				var contact = result["contact"];
				var url = result["url"];
				console.log("urL");
				console.log(url);
				return new Contact(
					collection.directory, contact.id, contact.data, url ? url.data : null
				);
			});
		}
	);
}

Collection.prototype.successors = function () {
	var collection = this;

	return promise_query(this.directory.server,
		[
			"START n=node({collection_id})",
			"MATCH (n)-[link:COMES_BEFORE]->(succ:Collection)",
			"RETURN succ, link"
		],
		{"collection_id" : this.collection_id},
		function (results) {
			return results.map(function (result) {
				return new Collection(
					collection.directory, result["succ"].id
				);
			})
		}
	);
}

exports.Collection = Collection;

/**
 * Contact
 */

var Contact = function (directory, contact_id, contact_info, url) {

	this.directory = directory;
	this.contact_id = contact_id;
	this.contact_info = contact_info;
	if (url) this.url = url;
}

/*(Contact.prototype.working_times = function () {

}*/

Contact.prototype.get_contact_id = function () {
	return this.contact_id;
}

Contact.prototype.get_info = function () {
	return this.contact_info;
}

exports.Contact = Contact;

/**
 * Patterns
 */

var promise_query = function (server, query, params, process_results) {
	var query_str = query.join(" ");
	var deferred = q.defer();
	server.query(query_str, params, function (err, results) {
		if (err) {
			deferred.reject(err);
		}
		var processed_results = process_results(results);
		deferred.resolve(processed_results);
	});
	return deferred.promise;
}<|endoftext|>


<commit_before>/**
 *
 * @constructor
 */
var RcmEventManager = function () {

    /**
     * Always refers to this object unlike the 'this' JS variable;
     *
     * @type {RcmEventManager}
     */
    var me = this;

    var eventsUrl = '/rcm-event-calender/events';

    var categoriesUrl = '/rcm-event-calender/categories';

    var categories = [];

    var currentCategoryId;

    var eventList;

    var categorySelectContainer;

    var warning = '
Note: Changes to events happen ' +
        'immediately and cannot be rolled back.
';

    me.showManager = function(){
        var form = $('

').addClass('simple');
        categorySelectContainer =
            $('
Loading...
');
        eventList =
            $('

');
        form.append(categorySelectContainer);
        form.append('
');
        form.append(eventList);
        form.append('
');
        form.append(warning);
        form.dialog({
            title:'Event Manager',
            modal:true,
            width:620,
            buttons:{
                Cancel:function () {
                    $(this).dialog("close");
                },
                Ok:function () {
                    $(this).dialog("close");
                }
            }
        });

        me.renderCategorySelect(me.renderEventList);
    };

    /**
     *
     * @param {Function} [callback]
     */
    me.renderCategorySelect = function(callback){
        me.getCategories(function(){
            categorySelectContainer.empty();
            categorySelectContainer.addSelect(
                'categoryId',
                'Event Category',
                categories,
                currentCategoryId
            );
            categorySelectContainer.find('select').change(function(){
                me.changeSelectedCategory($(this).val());
            });
            categorySelectContainer.find('p').append(me.buildEditButtons());

            if(typeof(callback)=='function'){
                callback();
            }
        });
    };

    /**
     *
     * @param {Integer} categoryId
     */
    me.changeSelectedCategory = function(categoryId){
        currentCategoryId = categoryId;
        me.renderEventList();
    };

    me.buildEditButtons = function(){
        var buttonCont = $('');

        var deleteCategoryButton = $('<button>Delete Category</button>');
        deleteCategoryButton.click(
            function(){
                me.deleteCategory(currentCategoryId);
            }
        );
        buttonCont.append(' ').append(deleteCategoryButton);

        var createCategoryButton = $('<button>Create Category</button>');
        createCategoryButton.click(
            function(){
                me.createCategory();
            }
        );
        buttonCont.append(' ').append(createCategoryButton);

        var createEventButton = $('<button>Create Event</button>');
        createEventButton.click(
            function(){
                me.createEvent();
            }
        );
        buttonCont.append(' ').append(createEventButton);

        return buttonCont;
    };

    me.renderEventList = function(){
        eventList.html('
Loading...
');
        me.getEvents(
            currentCategoryId,
            function(events){

                if(events.length){
                    eventList.html('
Event Title	Start Date
');
                }else{
                    eventList.html('
No events found in this category.
')
                }

                var tdOpen = '';
                $.each(events,function(){
                    var tr=$('

');
                    tr.append(tdOpen + this.title + '	');
                    tr.append(tdOpen + this.startDate + '	');
                    tr.disableSelection();
                    eventList.append(tr);
                });

                eventList.find('tr').dblclick(function(){
                    me.editEvent($(this).attr('data-eventId'));
                });

                rcmEdit.pluginContextMenu(
                    {
                        selector:'#eventManagerList tr',
                        //Here are the right click menu options
                        items:{
                            deleteEvent:{
                                name:'Delete Event',
                                icon:'delete',
                                callback:function(){
                                    me.deleteEvent(
                                        $(this).attr('data-eventId')
                                    );
                                }
                            },
                            editEvent:{
                                name:'Edit Event',
                                icon:'edit',
                                callback:function(){
                                    me.editEvent(
                                        $(this).attr('data-eventId')
                                    );
                                }
                            }
                        }
                    }
                );

                $('body').trigger('rcmEventManagerRender');
            }
        );
    };

    me.createCategory = function(){
        me.showCategoryPropertiesDialog(
            'Create Category',
            {name:''},
            function(category, okButton){
                $.ajax({
                    url: categoriesUrl,
                    type: 'POST',
                    data: category,
                    success: function() {
                        me.renderCategorySelect();
                        okButton.dialog('close');
                    },
                    error:me.handleAjaxError
                });
            }
        );
    };

    /**
     */
    me.createEvent = function(){
        me.showEventPropertiesDialog(
            'Create Event',
            {
                categoryId:currentCategoryId,
                title:'',
                text:'',
                startDate:me.getToday(),
                endDate:me.getToday(),
                mapAddress:''
            },
            function(event, okButton){
                $.ajax({
                    url: eventsUrl,
                    type: 'POST',
                    data: event,
                    success: function() {
                        console.log(1);
                        me.renderEventList();
                        okButton.dialog('close');
                    },
                    error:me.handleAjaxError
                });
            }
        );
    };

    me.getToday = function(){
        var today = new Date();
        return (today.getMonth()+1) + '/'
            + today.getDate() + '/' + today.getFullYear();
    };

    me.handleAjaxError = function(e){
        console.log(e);
        $.confirm(
            'There was a problem. Please make sure you entered valid values ' +
                'and try again.'
        )
    };

    /**
     *
     * @param {Integer} categoryId
     */
    me.deleteCategory = function(categoryId){
        me.getCategory(
            categoryId,
            function(category){
                var message = 'Delete this category AND ALL EVENTS UNDER' +
                    ' IT?

' + category.name +
                    '

' + warning;
                $.confirm(
                    message,
                    function(){
                        $.ajax({
                            url: categoriesUrl + '/' + categoryId,
                            type: 'DELETE',
                            success: function() {
                                me.renderCategorySelect(me.renderEventList);
                            },
                            error:me.handleAjaxError
                        });
                    }
                );
            }
        )
    };

    /**
     *
     * @param {Integer} eventId
     */
    me.deleteEvent = function(eventId){
        me.getEvent(
            eventId,
            function(event){
                var message = 'Delete this event?

' + event.title +
                    '

' + warning;
                $.confirm(
                    message,
                    function(){
                        $.ajax({
                            url: eventsUrl + '/' + eventId,
                            type: 'DELETE',
                            success: function() {
                                me.renderEventList();
                            },
                            error:me.handleAjaxError
                        });
                    }
                );
            }
        )
    };

    /**
     *
     * @param {Integer} eventId
     */
    me.editEvent = function(eventId){
        me.getEvent(
            eventId,
            function(event){
                me.showEventPropertiesDialog(
                    'Edit Event',
                    event,
                    function(event, okButton){
                        me.putEventAjax(eventId, event, okButton);
                    }
                );
            }
        );
    };

    me.putEventAjax = function(eventId, event, okButton){
        $.ajax(
            {
                url: eventsUrl + '/' + eventId,
                type: 'PUT',
                data: event,
                complete: function(xhr) {
                    console.log(xhr.readyState);
                    console.log(xhr.status);
                    if (xhr.readyState == 4 && xhr.status == 204) {
                        me.renderEventList();
                        okButton.dialog('close');
                    } else {
                        me.handleAjaxError(xhr);
                    }
                }
            }
        );
    };

    me.getEvent = function(eventId, callback){
        $.getJSON(
            eventsUrl + '/' + eventId,
                function(response) {
                callback(response)
            }
        ).error(me.handleAjaxError);
    };

    me.getCategory = function(categoryId, callback){
        $.getJSON(
            categoriesUrl + '/' + categoryId,
                function(response) {
                callback(response)
            }
        ).error(me.handleAjaxError);
    };

    me.getEvents = function(categoryId, callback){
        $.getJSON(
            eventsUrl + '?categoryId=' + categoryId,
            function(response) {
                callback(response)
            }
        ).error(me.handleAjaxError);
    };

    /**
     *
     * @param {String} formTitle
     * @param {Object} [event]
     * @param {Function} okCallback
     */
    me.showEventPropertiesDialog = function(formTitle, event, okCallback){

        var inputs = {
            categoryId:$.dialogIn('select', 'Event Category', categories, event.categoryId),
            title:$.dialogIn('text', 'Title', event.title),
            text:$.dialogIn('richEdit', 'Text', event.text),
            startDate:$.dialogIn('date', 'Start Date', event.startDate),
            endDate:$.dialogIn('date' , 'End Date', event.endDate),
            mapAddress:$.dialogIn('text' , 'Map Address', event.mapAddress)
        };

        var form = $('<form></form>')
            .addClass('simple')
            .appendMulti(inputs)
            .append(warning)
            .dialog({
                title:formTitle,
                modal:true,
                width:620,
                buttons:{
                    Cancel:function () {
                        $(this).dialog("close");
                    },
                    Ok:function () {

                        //Get user-entered data from form
                        event.categoryId = inputs.categoryId.val();
                        event.title = inputs.title.val();
                        event.startDate  = inputs.startDate.val();
                        event.endDate  = inputs.endDate.val();
                        event.mapAddress = inputs.mapAddress.val();
                        event.text  = inputs.text.val();

                        okCallback(event,$(this));
                    }
                }
            }
        );
    };

    /**
     *
     * @param {String} formTitle
     * @param {Object} [category]
     * @param {Function} okCallback
     */
    me.showCategoryPropertiesDialog = function(formTitle, category, okCallback){

        var nameInput = $.dialogIn('text', 'Name', category.name);

        var form = $('<form></form>')
            .addClass('simple')
            .append(nameInput)
            .append(warning)
            .dialog({
                title:formTitle,
                modal:true,
                width:620,
                buttons:{
                    Cancel:function () {
                        $(this).dialog("close");
                    },
                    Ok:function () {

                        //Get user-entered data from form
                        category.name= nameInput.val();

                        okCallback(category,$(this));
                    }
                }
            }
        );
    };

    /**
     * updates category array from server
     * @param {Function} [callback]
     */
    me.getCategories = function(callback){
        $.getJSON(
            '/rcm-event-calender/categories',
            function(result) {
                categories=[];
                $.each(result, function(){
                    categories[this.categoryId]=this.name;
                });

                me.setCurrentCategoryToDefault();

                if(typeof(callback)=='function'){
                    callback(categories);
                }
            }
        );
    };

    me.setCurrentCategoryToDefault = function(){
        //Use for-loop to get first key
        for (var firstCategoryId in categories) {
            currentCategoryId = firstCategoryId;
            if(1==1){//Prevent IDE whining
                break;
            }
        }
    };
};<commit_msg>varouis minor js fixes<commit_after>/**
 *
 * @constructor
 */
var RcmEventManager = function () {

    /**
     * Always refers to this object unlike the 'this' JS variable;
     *
     * @type {RcmEventManager}
     */
    var me = this;

    var eventsUrl = '/rcm-event-calender/events';

    var categoriesUrl = '/rcm-event-calender/categories';

    var categories = [];

    var currentCategoryId;

    var eventList;

    var categorySelectContainer;

    var warning = '
Note: Changes to events happen ' +
        'immediately and cannot be rolled back.
';

    me.showManager = function(){
        var form = $('

').addClass('simple');
        categorySelectContainer =
            $('
Loading...
');
        eventList =
            $('

');
        form.append(categorySelectContainer);
        form.append('
');
        form.append(eventList);
        form.append('
');
        form.append(warning);
        form.dialog({
            title:'Event Manager',
            modal:true,
            width:620,
            buttons:{
                Cancel:function () {
                    $(this).dialog("close");
                },
                Ok:function () {
                    $(this).dialog("close");
                }
            }
        });

        me.renderCategorySelect(me.renderEventList);
    };

    /**
     *
     * @param {Function} [callback]
     */
    me.renderCategorySelect = function(callback){
        me.getCategories(function(){
            categorySelectContainer.empty();
            categorySelectContainer.addSelect(
                'categoryId',
                'Event Category',
                categories,
                currentCategoryId
            );
            categorySelectContainer.find('select').change(function(){
                me.changeSelectedCategory($(this).val());
            });
            categorySelectContainer.find('p').append(me.buildEditButtons());

            if(typeof(callback)=='function'){
                callback();
            }
        });
    };

    /**
     *
     * @param {Integer} categoryId
     */
    me.changeSelectedCategory = function(categoryId){
        currentCategoryId = categoryId;
        me.renderEventList();
    };

    me.buildEditButtons = function(){
        var buttonCont = $('');

        var deleteCategoryButton = $('<button>Delete Category</button>');
        deleteCategoryButton.click(
            function(){
                me.deleteCategory(currentCategoryId);
            }
        );
        buttonCont.append(' ').append(deleteCategoryButton);

        var createCategoryButton = $('<button>Create Category</button>');
        createCategoryButton.click(
            function(){
                me.createCategory();
            }
        );
        buttonCont.append(' ').append(createCategoryButton);

        var createEventButton = $('<button>Create Event</button>');
        createEventButton.click(
            function(){
                me.createEvent();
            }
        );
        buttonCont.append(' ').append(createEventButton);

        return buttonCont;
    };

    me.renderEventList = function(){
        eventList.html('
Loading...
');
        me.getEvents(
            currentCategoryId,
            function(events){

                if(events.length){
                    eventList.html('
Event Title	Start Date
');
                }else{
                    eventList.html('
No events found in this category.
')
                }

                var tdOpen = '';
                $.each(events,function(){
                    var tr=$('

');
                    tr.append(tdOpen + this.title + '	');
                    tr.append(tdOpen + this.startDate + '	');
                    tr.disableSelection();
                    eventList.append(tr);
                });

                eventList.find('tr').dblclick(function(){
                    me.editEvent($(this).attr('data-eventId'));
                });

                rcmEdit.pluginContextMenu(
                    {
                        selector:'#eventManagerList tr',
                        //Here are the right click menu options
                        items:{
                            deleteEvent:{
                                name:'Delete Event',
                                icon:'delete',
                                callback:function(){
                                    me.deleteEvent(
                                        $(this).attr('data-eventId')
                                    );
                                }
                            },
                            editEvent:{
                                name:'Edit Event',
                                icon:'edit',
                                callback:function(){
                                    me.editEvent(
                                        $(this).attr('data-eventId')
                                    );
                                }
                            }
                        }
                    }
                );

                $('body').trigger('rcmEventManagerRender');
            }
        );
    };

    me.createCategory = function(){
        me.showCategoryPropertiesDialog(
            'Create Category',
            {name:''},
            function(category, okButton){
                $.ajax({
                    url: categoriesUrl,
                    type: 'POST',
                    data: category,
                    success: function() {
                        me.renderCategorySelect();
                        okButton.dialog('close');
                    },
                    error:me.handleAjaxError
                });
            }
        );
    };

    /**
     */
    me.createEvent = function(){
        me.showEventPropertiesDialog(
            'Create Event',
            {
                categoryId:currentCategoryId,
                title:'',
                text:'',
                startDate:me.getToday(),
                endDate:me.getToday(),
                mapAddress:''
            },
            function(event, okButton){
                $.ajax({
                    url: eventsUrl,
                    type: 'POST',
                    data: event,
                    success: function() {
                        console.log(1);
                        me.renderEventList();
                        okButton.dialog('close');
                    },
                    error:me.handleAjaxError
                });
            }
        );
    };

    me.getToday = function(){
        var today = new Date();
        return (today.getMonth()+1) + '/'
            + today.getDate() + '/' + today.getFullYear();
    };

    me.handleAjaxError = function(e){
        console.log(e);
        $.confirm(
            'There was a problem. Please make sure you entered valid values ' +
                'and try again.'
        )
    };

    /**
     *
     * @param {Integer} categoryId
     */
    me.deleteCategory = function(categoryId){
        me.getCategory(
            categoryId,
            function(category){
                var message = 'Delete this category AND ALL EVENTS UNDER' +
                    ' IT?

' + category.name +
                    '

' + warning;
                $.confirm(
                    message,
                    function(){
                        $.ajax({
                            url: categoriesUrl + '/' + categoryId,
                            type: 'DELETE',
                            complete: function(xhr) {
                                if (xhr.readyState == 4 && xhr.status == 204) {
                                    me.renderCategorySelect(me.renderEventList);
                                } else {
                                    me.handleAjaxError(xhr);
                                }
                            }
                        });
                    }
                );
            }
        )
    };

    /**
     *
     * @param {Integer} eventId
     */
    me.deleteEvent = function(eventId){
        me.getEvent(
            eventId,
            function(event){
                var message = 'Delete this event?

' + event.title +
                    '

' + warning;
                $.confirm(
                    message,
                    function(){
                        $.ajax({
                            url: eventsUrl + '/' + eventId,
                            type: 'DELETE',
                            complete: function(xhr) {
                                if (xhr.readyState == 4 && xhr.status == 204) {
                                    me.renderEventList();
                                } else {
                                    me.handleAjaxError(xhr);
                                }
                            }
                        });
                    }
                );
            }
        )
    };

    /**
     *
     * @param {Integer} eventId
     */
    me.editEvent = function(eventId){
        me.getEvent(
            eventId,
            function(event){
                me.showEventPropertiesDialog(
                    'Edit Event',
                    event,
                    function(event, okButton){
                        me.putEventAjax(eventId, event, okButton);
                    }
                );
            }
        );
    };

    me.putEventAjax = function(eventId, event, okButton){
        $.ajax(
            {
                url: eventsUrl + '/' + eventId,
                type: 'PUT',
                data: event,
                complete: function(xhr) {
                    console.log(xhr.readyState);
                    console.log(xhr.status);
                    if (xhr.readyState == 4 && xhr.status == 204) {
                        me.renderEventList();
                        okButton.dialog('close');
                    } else {
                        me.handleAjaxError(xhr);
                    }
                }
            }
        );
    };

    me.getEvent = function(eventId, callback){
        $.getJSON(
            eventsUrl + '/' + eventId,
                function(response) {
                callback(response)
            }
        ).error(me.handleAjaxError);
    };

    me.getCategory = function(categoryId, callback){
        $.getJSON(
            categoriesUrl + '/' + categoryId,
                function(response) {
                callback(response)
            }
        ).error(me.handleAjaxError);
    };

    me.getEvents = function(categoryId, callback){
        $.getJSON(
            eventsUrl + '?categoryId=' + categoryId,
            function(response) {
                callback(response)
            }
        ).error(me.handleAjaxError);
    };

    /**
     *
     * @param {String} formTitle
     * @param {Object} [event]
     * @param {Function} okCallback
     */
    me.showEventPropertiesDialog = function(formTitle, event, okCallback){

        var inputs = {
            categoryId:$.dialogIn('select', 'Event Category', categories, event.categoryId),
            title:$.dialogIn('text', 'Title', event.title),
            text:$.dialogIn('richEdit', 'Text', event.text),
            startDate:$.dialogIn('date', 'Start Date', event.startDate),
            endDate:$.dialogIn('date' , 'End Date', event.endDate),
            mapAddress:$.dialogIn('text' , 'Map Address', event.mapAddress)
        };

        var form = $('<form></form>')
            .addClass('simple')
            .appendMulti(inputs)
            .append(warning)
            .dialog({
                title:formTitle,
                modal:true,
                width:620,
                buttons:{
                    Cancel:function () {
                        $(this).dialog("close");
                    },
                    Ok:function () {

                        //Get user-entered data from form
                        event.categoryId = inputs.categoryId.val();
                        event.title = inputs.title.val();
                        event.startDate  = inputs.startDate.val();
                        event.endDate  = inputs.endDate.val();
                        event.mapAddress = inputs.mapAddress.val();
                        event.text  = inputs.text.val();

                        okCallback(event,$(this));
                    }
                }
            }
        );
    };

    /**
     *
     * @param {String} formTitle
     * @param {Object} [category]
     * @param {Function} okCallback
     */
    me.showCategoryPropertiesDialog = function(formTitle, category, okCallback){

        var nameInput = $.dialogIn('text', 'Name', category.name);

        var form = $('<form></form>')
            .addClass('simple')
            .append(nameInput)
            .append(warning)
            .dialog({
                title:formTitle,
                modal:true,
                width:620,
                buttons:{
                    Cancel:function () {
                        $(this).dialog("close");
                    },
                    Ok:function () {

                        //Get user-entered data from form
                        category.name= nameInput.val();

                        okCallback(category,$(this));
                    }
                }
            }
        );
    };

    /**
     * updates category array from server
     * @param {Function} [callback]
     */
    me.getCategories = function(callback){
        $.getJSON(
            '/rcm-event-calender/categories',
            function(result) {
                categories=[];
                $.each(result, function(){
                    categories[this.categoryId]=this.name;
                });

                me.setCurrentCategoryToDefault();

                if(typeof(callback)=='function'){
                    callback(categories);
                }
            }
        );
    };

    me.setCurrentCategoryToDefault = function(){
        //Use for-loop to get first key
        for (var firstCategoryId in categories) {
            currentCategoryId = firstCategoryId;
            if(1==1){//Prevent IDE whining
                break;
            }
        }
    };
};<|endoftext|>


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

	window.app = {};

	var initialize = function() {
		var loaders = [];

		for (var i in app.config.autoLoad) {
			(function(key, value) {
				loaders.push(function(callback) {
					if ($.isArray(value)) {
						loadFiles(key, value, callback);
					} else {
						loadFiles(key, value.files, callback, !value.initialize);
					}
				});
			}(i, app.config.autoLoad[i]));
		}

		// Autoload all components
		async.series(loaders, function() {
			// All files have been autoloaded, preload data necessary for the "loaded" hooks
			app.core.hooks.trigger('loaded', function() {
				// All necessary data has been loaded, give libraries opportunity to preload own assets
				app.core.hooks.trigger('preloadAssets', function() {
					// All necessary assets have been preloaded
					app.core.hooks.trigger('ready');
				});
			});
		});
	};

	require(['app/config'], initialize);

	/**
	 * Load the provided files and initialize them
	 * @param String namespace
	 * @param Array files
	 * @param Function callback
	 * @param Boolean dontInitialize
	 */
	var loadFiles = function(namespace, files, callback, dontInitialize) {
		// Create a key to store them
		if (!app[namespace]) app[namespace] = {};

		// Load the files
		require(files, function() {
			// File loaded, init it now and store it
			files.forEach(function(filePath) {
				var fileParts = filePath.split('/');
				var file = fileParts[fileParts.length - 1];

				if (!dontInitialize)
					app[namespace][file] = new app[namespace][file]();
			});

			app.core.log.debug('Loaded ' + namespace + ' (' + files.length + ' files)', 'Zeus/Core');

			// Callback if done loading.
			callback();
		});
	};
});<commit_msg>Support for cache busting by removing hashes from file name#<commit_after>$(function() {
	'use strict';

	window.app = {};

	var initialize = function() {
		var loaders = [];

		for (var i in app.config.autoLoad) {
			(function(key, value) {
				loaders.push(function(callback) {
					if ($.isArray(value)) {
						loadFiles(key, value, callback);
					} else {
						loadFiles(key, value.files, callback, !value.initialize);
					}
				});
			}(i, app.config.autoLoad[i]));
		}

		// Autoload all components
		async.series(loaders, function() {
			// All files have been autoloaded, preload data necessary for the "loaded" hooks
			app.core.hooks.trigger('loaded', function() {
				// All necessary data has been loaded, give libraries opportunity to preload own assets
				app.core.hooks.trigger('preloadAssets', function() {
					// All necessary assets have been preloaded
					app.core.hooks.trigger('ready');
				});
			});
		});
	};

	require(['app/config'], initialize);

	/**
	 * Load the provided files and initialize them
	 * @param String namespace
	 * @param Array files
	 * @param Function callback
	 * @param Boolean dontInitialize
	 */
	var loadFiles = function(namespace, files, callback, dontInitialize) {
		// Create a key to store them
		if (!app[namespace]) app[namespace] = {};

		// Load the files
		require(files, function() {
			// File loaded, init it now and store it
			files.forEach(function(filePath) {
				var fileParts = filePath.split('/');
				var file = fileParts[fileParts.length - 1];

				// Remove cache busting hash, if any
				if (/\./.test(file)) file = file.replace(/\..+$/, '');

				if (!dontInitialize) app[namespace][file] = new app[namespace][file]();
			});

			app.core.log.debug('Loaded ' + namespace + ' (' + files.length + ' files)', 'Zeus/Core');

			// Callback if done loading.
			callback();
		});
	};
});<|endoftext|>


<commit_before>'use strict'

import React, {Component} from 'react'

export default class extends Component {
  render () {
    return (

Profile

    )
  }
}
<commit_msg>Profile<commit_after>'use strict'

import React, {Component} from 'react'

export default class extends Component {
  constructor (props) {
    super(props)
    this.submitSummoner = this.submitSummoner.bind(this)
  }

  submitSummoner (e) {
    e.preventDefault()

    fetch('http://localhost:3001/summoner', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({
        name: this.name.value
      })
    }).then(res => {
      console.log(res)
    }).catch(err => {
      console.log(err)
    })
  }

  render () {
    return (

Profile

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vel lacus vel augue aliquet luctus. Ut urna libero.

          <form className='registration-form' onSubmit={this.submitSummoner}>
            <fieldset className='form-input'>
              Summoner Name
              <input className='input' type='text' name="name" ref={input => {this.name = input}} />
            </fieldset>

            <button className='btn -secondary -large' type='submit'>Add Summoner</button>
          </form>

    )
  }
}
<|endoftext|>


<commit_before>
<SLDSDatepickerSingleSelect />
<commit_msg>datepicker code sample<commit_after>
<SLDSDatepickerSingleSelect
  onDateChange = {(date) => {
    console.log('>>> onDateChange ', date);
  }}
/>
<|endoftext|>


<commit_before>import Constants from 'vtk.js/Sources/Widgets/Widgets3D/LineWidget/Constants';
import macro from 'vtk.js/Sources/macros';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math/';
import {
  calculateTextPosition,
  updateTextPosition,
  getNumberOfPlacedHandles,
  isHandlePlaced,
  getPoint,
} from 'vtk.js/Sources/Widgets/Widgets3D/LineWidget/helpers';

const { ShapeType } = Constants;
// Total number of points to place
const MAX_POINTS = 2;

const handleGetters = ['getHandle1', 'getHandle2', 'getMoveHandle'];

export default function widgetBehavior(publicAPI, model) {
  model.classHierarchy.push('vtkLineWidgetProp');
  model._isDragging = false;

  /**
   * Returns the handle at the handleIndex'th index.
   * @param {number} handleIndex 0, 1 or 2
   */
  publicAPI.getHandle = (handleIndex) =>
    model.widgetState[handleGetters[handleIndex]]();

  publicAPI.isPlaced = () =>
    getNumberOfPlacedHandles(model.widgetState) === MAX_POINTS;

  // --------------------------------------------------------------------------
  // Interactor event
  // --------------------------------------------------------------------------

  function ignoreKey(e) {
    return e.altKey || e.controlKey || e.shiftKey;
  }

  function updateCursor(callData) {
    model._isDragging = true;
    const manipulator =
      model.activeState?.getManipulator?.() ?? model.manipulator;
    model.previousPosition = [
      ...manipulator.handleEvent(callData, model._apiSpecificRenderWindow),
    ];
    model._apiSpecificRenderWindow.setCursor('grabbing');
    model._interactor.requestAnimation(publicAPI);
  }

  // --------------------------------------------------------------------------
  // Text methods
  // --------------------------------------------------------------------------

  /**
   * check for handle 2 position in comparison to handle 1 position
   * and sets text offset to not overlap on the line representation
   */

  function getOffsetDirectionForTextPosition() {
    const pos1 = publicAPI.getHandle(0).getOrigin();
    const pos2 = publicAPI.getHandle(1).getOrigin();

    let dySign = 1;

    if (pos1 && pos2) {
      if (pos1[0] <= pos2[0]) {
        dySign = pos1[1] <= pos2[1] ? 1 : -1;
      } else {
        dySign = pos1[1] <= pos2[1] ? -1 : 1;
      }
    }
    return dySign;
  }

  /**
   * place SVGText on line according to both handle positions
   * which purpose is to never have text representation overlapping
   * on PolyLine representation
   * */
  publicAPI.placeText = () => {
    const dySign = getOffsetDirectionForTextPosition();
    const textPropsCp = { ...model.representations[3].getTextProps() };
    textPropsCp.dy = dySign * Math.abs(textPropsCp.dy);
    model.representations[3].setTextProps(textPropsCp);
    model._interactor.render();
  };

  publicAPI.setText = (text) => {
    model.widgetState.getText().setText(text);
    model._interactor.render();
  };

  // --------------------------------------------------------------------------
  // Handle positioning methods
  // --------------------------------------------------------------------------

  // Handle utilities ---------------------------------------------------------

  function getLineDirection(p1, p2) {
    const dir = vtkMath.subtract(p1, p2, []);
    vtkMath.normalize(dir);
    return dir;
  }

  // Handle orientation & rotation ---------------------------------------------------------

  function computeMousePosition(p1, callData) {
    const displayMousePos = publicAPI.computeWorldToDisplay(
      model._renderer,
      ...p1
    );
    const worldMousePos = publicAPI.computeDisplayToWorld(
      model._renderer,
      callData.position.x,
      callData.position.y,
      displayMousePos[2]
    );
    return worldMousePos;
  }

  /**
   * Returns the  handle orientation to match the direction vector of the polyLine from one tip to another
   * @param {number} handleIndex 0 for handle1, 1 for handle2
   * @param {object} callData if specified, uses mouse position as 2nd point
   */
  function getHandleOrientation(handleIndex, callData = null) {
    const point1 = getPoint(handleIndex, model.widgetState);
    const point2 = callData
      ? computeMousePosition(point1, callData)
      : getPoint(1 - handleIndex, model.widgetState);
    return point1 && point2 ? getLineDirection(point1, point2) : null;
  }

  /**
   * Orient handle
   * @param {number} handleIndex 0, 1 or 2
   * @param {object} callData optional, see getHandleOrientation for details.
   */
  function updateHandleOrientation(handleIndex, callData = null) {
    const orientation = getHandleOrientation(Math.min(1, handleIndex));
    model.representations[handleIndex].setOrientation(orientation);
  }

  publicAPI.updateHandleOrientations = () => {
    updateHandleOrientation(0);
    updateHandleOrientation(1);
    updateHandleOrientation(2);
  };

  publicAPI.rotateHandlesToFaceCamera = () => {
    model.representations[0].setViewMatrix(
      Array.from(model._camera.getViewMatrix())
    );
    model.representations[1].setViewMatrix(
      Array.from(model._camera.getViewMatrix())
    );
  };

  // Handles visibility ---------------------------------------------------------

  publicAPI.setMoveHandleVisibility = (visibility) => {
    model.representations[2].setVisibilityFlagArray([visibility, visibility]);
    model.widgetState.getMoveHandle().setVisible(visibility);
    model.representations[2].updateActorVisibility();
  };

  /**
   * Set actor visibility to true unless it is a NONE handle
   * and uses state visibility variable for the displayActor visibility to
   * allow pickable handles even when they are not displayed on screen
   * @param handle : the handle state object
   * @param handleNb : the handle number according to its label in widget state
   */
  publicAPI.updateHandleVisibility = (handleIndex) => {
    const handle = publicAPI.getHandle(handleIndex);
    const visibility =
      handle.getVisible() && isHandlePlaced(handleIndex, model.widgetState);
    model.representations[handleIndex].setVisibilityFlagArray([
      visibility,
      visibility && handle.getShape() !== ShapeType.NONE,
    ]);
    model.representations[handleIndex].updateActorVisibility();
    model._interactor.render();
  };

  /**
   * Called when placing a point from the first time.
   * @param {number} handleIndex
   */
  publicAPI.placeHandle = (handleIndex) => {
    const handle = publicAPI.getHandle(handleIndex);
    handle.setOrigin(...model.widgetState.getMoveHandle().getOrigin());

    publicAPI.updateHandleOrientations();
    publicAPI.rotateHandlesToFaceCamera();
    model.widgetState.getText().setOrigin(calculateTextPosition(model));
    publicAPI.updateHandleVisibility(handleIndex);

    if (handleIndex === 0) {
      // For the line (handle1, handle2, moveHandle) to be displayed
      // correctly, handle2 origin must be valid.
      publicAPI
        .getHandle(1)
        .setOrigin(...model.widgetState.getMoveHandle().getOrigin());
      // Now that handle2 has a valid origin, hide it
      publicAPI.updateHandleVisibility(1);

      model.widgetState
        .getMoveHandle()
        .setShape(publicAPI.getHandle(1).getShape());
    }
    if (handleIndex === 1) {
      publicAPI.placeText();
      publicAPI.setMoveHandleVisibility(false);
    }
  };

  // --------------------------------------------------------------------------
  // Left press: Select handle to drag
  // --------------------------------------------------------------------------

  publicAPI.handleLeftButtonPress = (e) => {
    if (
      !model.activeState ||
      !model.activeState.getActive() ||
      !model.pickable ||
      ignoreKey(e)
    ) {
      return macro.VOID;
    }
    if (
      model.activeState === model.widgetState.getMoveHandle() &&
      getNumberOfPlacedHandles(model.widgetState) === 0
    ) {
      publicAPI.placeHandle(0);
    } else if (
      model.widgetState.getMoveHandle().getActive() &&
      getNumberOfPlacedHandles(model.widgetState) === 1
    ) {
      publicAPI.placeHandle(1);
    } else if (model.dragable && !model.widgetState.getText().getActive()) {
      // Grab handle1, handle2 or whole widget
      updateCursor(e);
    }
    publicAPI.invokeStartInteractionEvent();
    return macro.EVENT_ABORT;
  };

  // --------------------------------------------------------------------------
  // Mouse move: Drag selected handle / Handle follow the mouse
  // --------------------------------------------------------------------------

  publicAPI.handleMouseMove = (callData) => {
    const manipulator =
      model.activeState?.getManipulator?.() ?? model.manipulator;
    if (
      manipulator &&
      model.pickable &&
      model.dragable &&
      model.activeState &&
      model.activeState.getActive() &&
      !ignoreKey(callData)
    ) {
      const worldCoords = manipulator.handleEvent(
        callData,
        model._apiSpecificRenderWindow
      );
      const translation = model.previousPosition
        ? vtkMath.subtract(worldCoords, model.previousPosition, [])
        : [0, 0, 0];
      model.previousPosition = worldCoords;
      if (
        // is placing first or second handle
        model.activeState === model.widgetState.getMoveHandle() ||
        // is dragging already placed first or second handle
        model._isDragging
      ) {
        if (model.activeState.setOrigin) {
          model.activeState.setOrigin(worldCoords);
        } else {
          // Dragging line
          publicAPI
            .getHandle(0)
            .setOrigin(
              vtkMath.add(publicAPI.getHandle(0).getOrigin(), translation, [])
            );
          publicAPI
            .getHandle(1)
            .setOrigin(
              vtkMath.add(publicAPI.getHandle(1).getOrigin(), translation, [])
            );
        }
        publicAPI.updateHandleOrientations();
        updateTextPosition(model);
        publicAPI.invokeInteractionEvent();
        return macro.EVENT_ABORT;
      }
    }
    return macro.VOID;
  };

  // --------------------------------------------------------------------------
  // Left release: Finish drag
  // --------------------------------------------------------------------------

  publicAPI.handleLeftButtonRelease = () => {
    if (
      !model.activeState ||
      !model.activeState.getActive() ||
      !model.pickable
    ) {
      publicAPI.rotateHandlesToFaceCamera();
      return macro.VOID;
    }
    if (model.hasFocus && publicAPI.isPlaced()) {
      publicAPI.loseFocus();
      return macro.VOID;
    }

    if (model._isDragging && publicAPI.isPlaced()) {
      const wasTextActive = model.widgetState.getText().getActive();
      // Recompute offsets
      publicAPI.placeText();
      model.widgetState.deactivate();
      model.activeState = null;
      if (!wasTextActive) {
        model._interactor.cancelAnimation(publicAPI);
      }
      model._apiSpecificRenderWindow.setCursor('pointer');

      model.hasFocus = false;
      model._isDragging = false;
    } else if (model.activeState !== model.widgetState.getMoveHandle()) {
      model.widgetState.deactivate();
    }

    if (
      (model.hasFocus && !model.activeState) ||
      (model.activeState && !model.activeState.getActive())
    ) {
      model._widgetManager.enablePicking();
      model._interactor.render();
    }

    publicAPI.invokeEndInteractionEvent();
    return macro.EVENT_ABORT;
  };

  // --------------------------------------------------------------------------
  // Focus API - moveHandle follow mouse when widget has focus
  // --------------------------------------------------------------------------

  publicAPI.grabFocus = () => {
    if (!model.hasFocus && !publicAPI.isPlaced()) {
      model.activeState = model.widgetState.getMoveHandle();
      model.activeState.setShape(publicAPI.getHandle(0).getShape());
      publicAPI.setMoveHandleVisibility(true);
      model.activeState.activate();
      model._interactor.requestAnimation(publicAPI);
      publicAPI.invokeStartInteractionEvent();
    }
    model.hasFocus = true;
  };

  // --------------------------------------------------------------------------

  publicAPI.loseFocus = () => {
    if (model.hasFocus) {
      model._interactor.cancelAnimation(publicAPI);
      publicAPI.invokeEndInteractionEvent();
    }
    model.widgetState.deactivate();
    model.widgetState.getMoveHandle().deactivate();
    model.activeState = null;
    model.hasFocus = false;
    model._widgetManager.enablePicking();
    model._interactor.render();
  };
}
<commit_msg>fix(linewidget): fix moveHandle not visible<commit_after>import Constants from 'vtk.js/Sources/Widgets/Widgets3D/LineWidget/Constants';
import macro from 'vtk.js/Sources/macros';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math/';
import {
  calculateTextPosition,
  updateTextPosition,
  getNumberOfPlacedHandles,
  isHandlePlaced,
  getPoint,
} from 'vtk.js/Sources/Widgets/Widgets3D/LineWidget/helpers';

const { ShapeType } = Constants;
// Total number of points to place
const MAX_POINTS = 2;

const handleGetters = ['getHandle1', 'getHandle2', 'getMoveHandle'];

export default function widgetBehavior(publicAPI, model) {
  model.classHierarchy.push('vtkLineWidgetProp');
  model._isDragging = false;

  /**
   * Returns the handle at the handleIndex'th index.
   * @param {number} handleIndex 0, 1 or 2
   */
  publicAPI.getHandle = (handleIndex) =>
    model.widgetState[handleGetters[handleIndex]]();

  publicAPI.isPlaced = () =>
    getNumberOfPlacedHandles(model.widgetState) === MAX_POINTS;

  // --------------------------------------------------------------------------
  // Interactor event
  // --------------------------------------------------------------------------

  function ignoreKey(e) {
    return e.altKey || e.controlKey || e.shiftKey;
  }

  function updateCursor(callData) {
    model._isDragging = true;
    const manipulator =
      model.activeState?.getManipulator?.() ?? model.manipulator;
    model.previousPosition = [
      ...manipulator.handleEvent(callData, model._apiSpecificRenderWindow),
    ];
    model._apiSpecificRenderWindow.setCursor('grabbing');
    model._interactor.requestAnimation(publicAPI);
  }

  // --------------------------------------------------------------------------
  // Text methods
  // --------------------------------------------------------------------------

  /**
   * check for handle 2 position in comparison to handle 1 position
   * and sets text offset to not overlap on the line representation
   */

  function getOffsetDirectionForTextPosition() {
    const pos1 = publicAPI.getHandle(0).getOrigin();
    const pos2 = publicAPI.getHandle(1).getOrigin();

    let dySign = 1;

    if (pos1 && pos2) {
      if (pos1[0] <= pos2[0]) {
        dySign = pos1[1] <= pos2[1] ? 1 : -1;
      } else {
        dySign = pos1[1] <= pos2[1] ? -1 : 1;
      }
    }
    return dySign;
  }

  /**
   * place SVGText on line according to both handle positions
   * which purpose is to never have text representation overlapping
   * on PolyLine representation
   * */
  publicAPI.placeText = () => {
    const dySign = getOffsetDirectionForTextPosition();
    const textPropsCp = { ...model.representations[3].getTextProps() };
    textPropsCp.dy = dySign * Math.abs(textPropsCp.dy);
    model.representations[3].setTextProps(textPropsCp);
    model._interactor.render();
  };

  publicAPI.setText = (text) => {
    model.widgetState.getText().setText(text);
    model._interactor.render();
  };

  // --------------------------------------------------------------------------
  // Handle positioning methods
  // --------------------------------------------------------------------------

  // Handle utilities ---------------------------------------------------------

  function getLineDirection(p1, p2) {
    const dir = vtkMath.subtract(p1, p2, []);
    vtkMath.normalize(dir);
    return dir;
  }

  // Handle orientation & rotation ---------------------------------------------------------

  function computeMousePosition(p1, callData) {
    const displayMousePos = publicAPI.computeWorldToDisplay(
      model._renderer,
      ...p1
    );
    const worldMousePos = publicAPI.computeDisplayToWorld(
      model._renderer,
      callData.position.x,
      callData.position.y,
      displayMousePos[2]
    );
    return worldMousePos;
  }

  /**
   * Returns the  handle orientation to match the direction vector of the polyLine from one tip to another
   * @param {number} handleIndex 0 for handle1, 1 for handle2
   * @param {object} callData if specified, uses mouse position as 2nd point
   */
  function getHandleOrientation(handleIndex, callData = null) {
    const point1 = getPoint(handleIndex, model.widgetState);
    const point2 = callData
      ? computeMousePosition(point1, callData)
      : getPoint(1 - handleIndex, model.widgetState);
    return point1 && point2 ? getLineDirection(point1, point2) : null;
  }

  /**
   * Orient handle
   * @param {number} handleIndex 0, 1 or 2
   * @param {object} callData optional, see getHandleOrientation for details.
   */
  function updateHandleOrientation(handleIndex, callData = null) {
    const orientation = getHandleOrientation(Math.min(1, handleIndex));
    model.representations[handleIndex].setOrientation(orientation);
  }

  publicAPI.updateHandleOrientations = () => {
    updateHandleOrientation(0);
    updateHandleOrientation(1);
    updateHandleOrientation(2);
  };

  publicAPI.rotateHandlesToFaceCamera = () => {
    model.representations[0].setViewMatrix(
      Array.from(model._camera.getViewMatrix())
    );
    model.representations[1].setViewMatrix(
      Array.from(model._camera.getViewMatrix())
    );
  };

  // Handles visibility ---------------------------------------------------------

  publicAPI.setMoveHandleVisibility = (visibility) => {
    model.representations[2].setVisibilityFlagArray([visibility, visibility]);
    model.widgetState.getMoveHandle().setVisible(visibility);
    model.representations[2].updateActorVisibility();
  };

  /**
   * Set actor visibility to true unless it is a NONE handle
   * and uses state visibility variable for the displayActor visibility to
   * allow pickable handles even when they are not displayed on screen
   * @param handle : the handle state object
   * @param handleNb : the handle number according to its label in widget state
   */
  publicAPI.updateHandleVisibility = (handleIndex) => {
    const handle = publicAPI.getHandle(handleIndex);
    const visibility =
      handle.getVisible() && isHandlePlaced(handleIndex, model.widgetState);
    model.representations[handleIndex].setVisibilityFlagArray([
      visibility,
      visibility && handle.getShape() !== ShapeType.NONE,
    ]);
    model.representations[handleIndex].updateActorVisibility();
    model._interactor.render();
  };

  /**
   * Called when placing a point from the first time.
   * @param {number} handleIndex
   */
  publicAPI.placeHandle = (handleIndex) => {
    const handle = publicAPI.getHandle(handleIndex);
    handle.setOrigin(...model.widgetState.getMoveHandle().getOrigin());

    publicAPI.updateHandleOrientations();
    publicAPI.rotateHandlesToFaceCamera();
    model.widgetState.getText().setOrigin(calculateTextPosition(model));
    publicAPI.updateHandleVisibility(handleIndex);

    if (handleIndex === 0) {
      // For the line (handle1, handle2, moveHandle) to be displayed
      // correctly, handle2 origin must be valid.
      publicAPI
        .getHandle(1)
        .setOrigin(...model.widgetState.getMoveHandle().getOrigin());
      // Now that handle2 has a valid origin, hide it
      publicAPI.updateHandleVisibility(1);

      model.widgetState
        .getMoveHandle()
        .setShape(publicAPI.getHandle(1).getShape());
    }
    if (handleIndex === 1) {
      publicAPI.placeText();
      publicAPI.setMoveHandleVisibility(false);
    }
  };

  // --------------------------------------------------------------------------
  // Left press: Select handle to drag
  // --------------------------------------------------------------------------

  publicAPI.handleLeftButtonPress = (e) => {
    if (
      !model.activeState ||
      !model.activeState.getActive() ||
      !model.pickable ||
      ignoreKey(e)
    ) {
      return macro.VOID;
    }
    if (
      model.activeState === model.widgetState.getMoveHandle() &&
      getNumberOfPlacedHandles(model.widgetState) === 0
    ) {
      publicAPI.placeHandle(0);
    } else if (
      model.widgetState.getMoveHandle().getActive() &&
      getNumberOfPlacedHandles(model.widgetState) === 1
    ) {
      publicAPI.placeHandle(1);
    } else if (model.dragable && !model.widgetState.getText().getActive()) {
      // Grab handle1, handle2 or whole widget
      updateCursor(e);
    }
    publicAPI.invokeStartInteractionEvent();
    return macro.EVENT_ABORT;
  };

  // --------------------------------------------------------------------------
  // Mouse move: Drag selected handle / Handle follow the mouse
  // --------------------------------------------------------------------------

  publicAPI.handleMouseMove = (callData) => {
    const manipulator =
      model.activeState?.getManipulator?.() ?? model.manipulator;
    if (
      manipulator &&
      model.pickable &&
      model.dragable &&
      model.activeState &&
      model.activeState.getActive() &&
      !ignoreKey(callData)
    ) {
      const worldCoords = manipulator.handleEvent(
        callData,
        model._apiSpecificRenderWindow
      );
      const translation = model.previousPosition
        ? vtkMath.subtract(worldCoords, model.previousPosition, [])
        : [0, 0, 0];
      model.previousPosition = worldCoords;
      if (
        // is placing first or second handle
        model.activeState === model.widgetState.getMoveHandle() ||
        // is dragging already placed first or second handle
        model._isDragging
      ) {
        if (model.activeState.setOrigin) {
          model.activeState.setOrigin(worldCoords);
          publicAPI.setMoveHandleVisibility(!publicAPI.isPlaced());
        } else {
          // Dragging line
          publicAPI
            .getHandle(0)
            .setOrigin(
              vtkMath.add(publicAPI.getHandle(0).getOrigin(), translation, [])
            );
          publicAPI
            .getHandle(1)
            .setOrigin(
              vtkMath.add(publicAPI.getHandle(1).getOrigin(), translation, [])
            );
        }
        publicAPI.updateHandleOrientations();
        updateTextPosition(model);
        publicAPI.invokeInteractionEvent();
        return macro.EVENT_ABORT;
      }
    }
    return macro.VOID;
  };

  // --------------------------------------------------------------------------
  // Left release: Finish drag
  // --------------------------------------------------------------------------

  publicAPI.handleLeftButtonRelease = () => {
    if (
      !model.activeState ||
      !model.activeState.getActive() ||
      !model.pickable
    ) {
      publicAPI.rotateHandlesToFaceCamera();
      return macro.VOID;
    }
    if (model.hasFocus && publicAPI.isPlaced()) {
      publicAPI.loseFocus();
      return macro.VOID;
    }

    if (model._isDragging && publicAPI.isPlaced()) {
      const wasTextActive = model.widgetState.getText().getActive();
      // Recompute offsets
      publicAPI.placeText();
      model.widgetState.deactivate();
      model.activeState = null;
      if (!wasTextActive) {
        model._interactor.cancelAnimation(publicAPI);
      }
      model._apiSpecificRenderWindow.setCursor('pointer');

      model.hasFocus = false;
      model._isDragging = false;
    } else if (model.activeState !== model.widgetState.getMoveHandle()) {
      model.widgetState.deactivate();
    }

    if (
      (model.hasFocus && !model.activeState) ||
      (model.activeState && !model.activeState.getActive())
    ) {
      model._widgetManager.enablePicking();
      model._interactor.render();
    }

    publicAPI.invokeEndInteractionEvent();
    return macro.EVENT_ABORT;
  };

  // --------------------------------------------------------------------------
  // Focus API - moveHandle follow mouse when widget has focus
  // --------------------------------------------------------------------------

  publicAPI.grabFocus = () => {
    if (!model.hasFocus && !publicAPI.isPlaced()) {
      model.activeState = model.widgetState.getMoveHandle();
      model.activeState.setShape(publicAPI.getHandle(0).getShape());
      publicAPI.setMoveHandleVisibility(true);
      model.activeState.activate();
      model._interactor.requestAnimation(publicAPI);
      publicAPI.invokeStartInteractionEvent();
    }
    model.hasFocus = true;
  };

  // --------------------------------------------------------------------------

  publicAPI.loseFocus = () => {
    if (model.hasFocus) {
      model._interactor.cancelAnimation(publicAPI);
      publicAPI.invokeEndInteractionEvent();
    }
    model.widgetState.deactivate();
    model.widgetState.getMoveHandle().deactivate();
    model.activeState = null;
    model.hasFocus = false;
    model._widgetManager.enablePicking();
    model._interactor.render();
  };
}
<|endoftext|>


<commit_before>var win1 = Ti.UI.currentWindow;

var atmos = win1.atmos;
var currentUserId = atmos.currentUserId();
var theme = require('theme');
var themeFGColorMain = theme.colorMain();
var themeFGColorSub = theme.colorSub();
var themeBGColor = theme.backgroundColor(win1.timeline_type);
var themeBGColorLight = theme.backgroundColorLight(win1.timeline_type);

var reloadButton = Ti.UI.createButton({
	systemButton: Titanium.UI.iPhone.SystemButton.REFRESH
});

reloadButton.addEventListener('click', function(e) {
	refreshTimeline();
});
win1.leftNavButton = reloadButton;

function showSendMessageWindow(title, timelineType, replyToMsgId, toUserIds) {
	var messageWindow = Ti.UI.createWindow({
		url: 'message_window.js',
		title: title,
		color: themeFGColorMain,
		backgroundColor: themeBGColor,
		backgroundColorLight: themeBGColorLight,
		layout: 'vertical',
		isPrivate: timelineType === 'private',
		replyToMsgId: replyToMsgId,
		toUserIds: toUserIds,
		atmos: atmos
	});
	Ti.UI.currentTab.open(messageWindow);
}

var messageButton = Ti.UI.createButton({
	systemButton: Titanium.UI.iPhone.SystemButton.ADD
});
messageButton.addEventListener('click', function(e) {
	showSendMessageWindow('new message', win1.timeline_type);
});
win1.rightNavButton = messageButton;

var tableView = Ti.UI.createTableView({
	data: []
});

var latestMessageCreatedAt = undefined;
var oldestMessageCreatedAt = undefined;

function judgeMessageStatus(createdAt) {
	if (latestMessageCreatedAt && createdAt <= latestMessageCreatedAt && oldestMessageCreatedAt && createdAt >= oldestMessageCreatedAt) {
		return 'exists';
	}
	else if (!latestMessageCreatedAt || createdAt > latestMessageCreatedAt) {
		return 'newer';
	}
	else {
		return 'older';
	}
}

function setCreatedAt(createdAt) {
	if (!latestMessageCreatedAt || createdAt > latestMessageCreatedAt) {
		latestMessageCreatedAt = createdAt;
	}
	if (!oldestMessageCreatedAt || createdAt < oldestMessageCreatedAt) {
		oldestMessageCreatedAt = createdAt;
	}
}

var timelineData = [];
var timelineMetaData = {};
function updateTimeline(timeline) {
	var heads = [];
	var tails = [];
	timeline.forEach(function(tlItem, i, a) {
		var createdAtUtc = new Date(tlItem['created_at']);
		var newMessageStatus = judgeMessageStatus(createdAtUtc);
		if (newMessageStatus != 'exists') {
			var row = Ti.UI.createTableViewRow({
				//height: 150,
				height: 'auto',
				layout: 'vertical',
				backgroundColor: themeBGColor
			});

			var imageView = Ti.UI.createImageView({
				image: atmos.getAvatorUrl(tlItem['created_by']),
				width: 48,
				height: 48,
				left: 5,
				top: 5
			});
			row.add(imageView);

			var nameLabel = Ti.UI.createLabel({
				width: 257,
	//			height: 12,
				height: 'auto',
				left: 58,
				top: -48,
				fontSize: 6,
				fontWeight: 'bold',
				color: themeFGColorSub
			});
			nameLabel.text = tlItem['created_by'];
			row.add(nameLabel);

			var commentLabel = Ti.UI.createLabel({
				width: 257,
	//			height: 100,
				height: 'auto',
				left: 58,
				top: 1,
				fontSize: 8,
				color: themeFGColorMain
			});
			commentLabel.text = tlItem.message;
			row.add(commentLabel);

			var dateLabel = Ti.UI.createLabel({
				width: 180,
	//			height: 12,
				height: 'auto',
				right: 0,
				top: 5,
				font: {fontSize: 1},
				color: themeFGColorSub
			});
			//var createdAtUtc = new Date(tlItem['created_at']);
			dateLabel.text = String.formatDate(createdAtUtc, 'medium') + ' ' + String.formatTime(createdAtUtc, 'medium');
			row.add(dateLabel);

			var replyButton = Ti.UI.createButton({
				width: 'auto',
				height: 'auto',
				left: 58,
				top: 2,
				color: themeFGColorSub,
				backgroundImage: 'reply_s.png'
			});
			var replyHandler = (function() {
				var sourceMsgId = tlItem['_id'];
				if (tlItem['to_user_id']) { // for private
					var toUserIds = tlItem['to_user_id'];
				}
				if (tlItem['addresses'] && tlItem['addresses']['users']) {
					var toUserIds = tlItem['addresses']['users'];
				}
				if (toUserIds) {
					var createdBy = tlItem['created_by'];
					var addressesUsers = [];
					toUserIds.forEach(function(userId, i, a) { if (userId !== currentUserId) { addressesUsers.push(userId); } });
					if (createdBy !== currentUserId && addressesUsers.indexOf(createdBy) === -1) {
						addressesUsers.push(createdBy);
					}
				}
				return function(e) {
					var orgMsg = timelineMetaData[sourceMsgId];
					showSendMessageWindow('reply message', win1.timeline_type, orgMsg['_id'], addressesUsers);
				};
			})();
			replyButton.addEventListener('click', replyHandler);
			row.add(replyButton);

			if (newMessageStatus === 'newer') {
				heads.push(row);
			}
			else {
				tails.push(row);
			}

			timelineMetaData[tlItem['_id']] = tlItem;
		}
	});
	timeline.forEach(function(tlItem, i, a) {
		setCreatedAt(new Date(tlItem['created_at']));
	});

	var nextData = heads.concat(timelineData);
	nextData = nextData.concat(tails);
	tableView.setData(nextData);

	timelineData = nextData;
}

// pull to refresh--
function formatDate(){
    var date = new Date;
    var datestr = (date.getMonth() + 1) +'/'+date.getDate()+'/'+date.getFullYear();
    if (date.getHours()>=12){
       datestr+=' '+(date.getHours()==12 ?
          date.getHours() : date.getHours()-12)+':'+
          date.getMinutes()+' PM';
    }
    else{
        datestr+=' '+date.getHours()+':'+date.getMinutes()+' AM';
    }
    return datestr;
}
var pulling = false;
var reloading = false;
var refreshHeaderTheme = theme.pullToRefreshTableHeader(formatDate());
var border = Ti.UI.createView(refreshHeaderTheme.border);
var tableHeader = Ti.UI.createView(refreshHeaderTheme.tableHeader);
tableHeader.add(border);
var arrow = Ti.UI.createView(refreshHeaderTheme.arrow);
var statusLabel = Ti.UI.createLabel(refreshHeaderTheme.statusLabel);
var lastUpdatedLabel = Ti.UI.createLabel(refreshHeaderTheme.lastUpdatedLabel);
var actInd = Titanium.UI.createActivityIndicator(refreshHeaderTheme.actInd);

tableHeader.add(arrow);
tableHeader.add(statusLabel);
tableHeader.add(lastUpdatedLabel);
tableHeader.add(actInd);
tableView.headerPullView = tableHeader;

win1.add(tableView);

tableView.beginReloading = function(){
    refreshTimeline();
};

function endReloading(success){
    tableView.setContentInsets({top:0},{animated:true});
    reloading = false;
    if(success){
        lastUpdatedLabel.text = "Last Updated: "+formatDate();
    }
    statusLabel.text = "Pull down to refresh...";
    actInd.hide();
    arrow.show();
};

tableView.addEventListener('scroll', function(e) {
    var offset = e.contentOffset.y;
    if (offset <= -65.0 && !pulling) {
        var t = Ti.UI.create2DMatrix();
        t = t.rotate(-180);
        pulling = true;
        arrow.animate({
            transform : t,
            duration : 180
        });
        statusLabel.text = "Release to refresh...";
    } else if (pulling && offset > -65.0 && offset < 0) {
        pulling = false;
        var t = Ti.UI.create2DMatrix();
        arrow.animate({
            transform : t,
            duration : 180
        });
        statusLabel.text = "Pull down to refresh...";
    }
});

tableView.addEventListener('dragEnd', function() {
    if (pulling && !reloading) {
        reloading = true;
        pulling = false;
        arrow.hide();
        actInd.show();
        statusLabel.text = "Reloading...";
        tableView.setContentInsets({
            top : 60
        }, {
            animated : true
        });

        tableView.scrollToTop(-60, true);
        arrow.transform = Ti.UI.create2DMatrix();
        tableView.beginReloading();
    }
});
// --pull to refresh

function refreshTimeline() {
	// var atmos = require('atmos');
	var resultApplier = function(e) {
		var resJSON = JSON.parse(e.source.responseText);
		updateTimeline(resJSON.results);
	};
	if (win1.timeline_type === 'global') {
		atmos.getGlobalTimeline(resultApplier);
	}
	else if (win1.timeline_type === 'talk') {
		atmos.getTalkTimeline(resultApplier);
	}
	else if (win1.timeline_type === 'private') {
		atmos.getPrivateTimeline(resultApplier);
	}
    endReloading(true);
}

refreshTimeline();
<commit_msg>changed reply icon into reply text, because the timeline's scrool was not smooth.<commit_after>var win1 = Ti.UI.currentWindow;

var atmos = win1.atmos;
var currentUserId = atmos.currentUserId();
var theme = require('theme');
var themeFGColorMain = theme.colorMain();
var themeFGColorSub = theme.colorSub();
var themeBGColor = theme.backgroundColor(win1.timeline_type);
var themeBGColorLight = theme.backgroundColorLight(win1.timeline_type);

var reloadButton = Ti.UI.createButton({
	systemButton: Titanium.UI.iPhone.SystemButton.REFRESH
});

reloadButton.addEventListener('click', function(e) {
	refreshTimeline();
});
win1.leftNavButton = reloadButton;

function showSendMessageWindow(title, timelineType, replyToMsgId, toUserIds) {
	var messageWindow = Ti.UI.createWindow({
		url: 'message_window.js',
		title: title,
		color: themeFGColorMain,
		backgroundColor: themeBGColor,
		backgroundColorLight: themeBGColorLight,
		layout: 'vertical',
		isPrivate: timelineType === 'private',
		replyToMsgId: replyToMsgId,
		toUserIds: toUserIds,
		atmos: atmos
	});
	Ti.UI.currentTab.open(messageWindow);
}

var messageButton = Ti.UI.createButton({
	systemButton: Titanium.UI.iPhone.SystemButton.ADD
});
messageButton.addEventListener('click', function(e) {
	showSendMessageWindow('new message', win1.timeline_type);
});
win1.rightNavButton = messageButton;

var tableView = Ti.UI.createTableView({
	data: []
});

var latestMessageCreatedAt = undefined;
var oldestMessageCreatedAt = undefined;

function judgeMessageStatus(createdAt) {
	if (latestMessageCreatedAt && createdAt <= latestMessageCreatedAt && oldestMessageCreatedAt && createdAt >= oldestMessageCreatedAt) {
		return 'exists';
	}
	else if (!latestMessageCreatedAt || createdAt > latestMessageCreatedAt) {
		return 'newer';
	}
	else {
		return 'older';
	}
}

function setCreatedAt(createdAt) {
	if (!latestMessageCreatedAt || createdAt > latestMessageCreatedAt) {
		latestMessageCreatedAt = createdAt;
	}
	if (!oldestMessageCreatedAt || createdAt < oldestMessageCreatedAt) {
		oldestMessageCreatedAt = createdAt;
	}
}

var timelineData = [];
var timelineMetaData = {};
function updateTimeline(timeline) {
	var heads = [];
	var tails = [];
	timeline.forEach(function(tlItem, i, a) {
		var createdAtUtc = new Date(tlItem['created_at']);
		var newMessageStatus = judgeMessageStatus(createdAtUtc);
		if (newMessageStatus != 'exists') {
			var row = Ti.UI.createTableViewRow({
				//height: 150,
				height: 'auto',
				layout: 'vertical',
				backgroundColor: themeBGColor
			});

			var imageView = Ti.UI.createImageView({
				image: atmos.getAvatorUrl(tlItem['created_by']),
				width: 48,
				height: 48,
				left: 5,
				top: 5
			});
			row.add(imageView);

			var nameLabel = Ti.UI.createLabel({
				width: 257,
	//			height: 12,
				height: 'auto',
				left: 58,
				top: -48,
				fontSize: 6,
				fontWeight: 'bold',
				color: themeFGColorSub
			});
			nameLabel.text = tlItem['created_by'];
			row.add(nameLabel);

			var commentLabel = Ti.UI.createLabel({
				width: 257,
	//			height: 100,
				height: 'auto',
				left: 58,
				top: 1,
				fontSize: 8,
				color: themeFGColorMain
			});
			commentLabel.text = tlItem.message;
			row.add(commentLabel);

			var dateLabel = Ti.UI.createLabel({
				width: 180,
	//			height: 12,
				height: 'auto',
				right: 0,
				top: 5,
				font: {fontSize: 1},
				color: themeFGColorSub
			});
			//var createdAtUtc = new Date(tlItem['created_at']);
			dateLabel.text = String.formatDate(createdAtUtc, 'medium') + ' ' + String.formatTime(createdAtUtc, 'medium');
			row.add(dateLabel);

			var replyButton = Ti.UI.createButton({
				width: 'auto',
				height: 'auto',
				left: 58,
				top: 2,
				color: themeFGColorSub,
				title: "reply"
			});
			var replyHandler = (function() {
				var sourceMsgId = tlItem['_id'];
				if (tlItem['to_user_id']) { // for private
					var toUserIds = tlItem['to_user_id'];
				}
				if (tlItem['addresses'] && tlItem['addresses']['users']) {
					var toUserIds = tlItem['addresses']['users'];
				}
				if (toUserIds) {
					var createdBy = tlItem['created_by'];
					var addressesUsers = [];
					toUserIds.forEach(function(userId, i, a) { if (userId !== currentUserId) { addressesUsers.push(userId); } });
					if (createdBy !== currentUserId && addressesUsers.indexOf(createdBy) === -1) {
						addressesUsers.push(createdBy);
					}
				}
				return function(e) {
					var orgMsg = timelineMetaData[sourceMsgId];
					showSendMessageWindow('reply message', win1.timeline_type, orgMsg['_id'], addressesUsers);
				};
			})();
			replyButton.addEventListener('click', replyHandler);
			row.add(replyButton);

			if (newMessageStatus === 'newer') {
				heads.push(row);
			}
			else {
				tails.push(row);
			}

			timelineMetaData[tlItem['_id']] = tlItem;
		}
	});
	timeline.forEach(function(tlItem, i, a) {
		setCreatedAt(new Date(tlItem['created_at']));
	});

	var nextData = heads.concat(timelineData);
	nextData = nextData.concat(tails);
	tableView.setData(nextData);

	timelineData = nextData;
}

// pull to refresh--
function formatDate(){
    var date = new Date;
    var datestr = (date.getMonth() + 1) +'/'+date.getDate()+'/'+date.getFullYear();
    if (date.getHours()>=12){
       datestr+=' '+(date.getHours()==12 ?
          date.getHours() : date.getHours()-12)+':'+
          date.getMinutes()+' PM';
    }
    else{
        datestr+=' '+date.getHours()+':'+date.getMinutes()+' AM';
    }
    return datestr;
}
var pulling = false;
var reloading = false;
var refreshHeaderTheme = theme.pullToRefreshTableHeader(formatDate());
var border = Ti.UI.createView(refreshHeaderTheme.border);
var tableHeader = Ti.UI.createView(refreshHeaderTheme.tableHeader);
tableHeader.add(border);
var arrow = Ti.UI.createView(refreshHeaderTheme.arrow);
var statusLabel = Ti.UI.createLabel(refreshHeaderTheme.statusLabel);
var lastUpdatedLabel = Ti.UI.createLabel(refreshHeaderTheme.lastUpdatedLabel);
var actInd = Titanium.UI.createActivityIndicator(refreshHeaderTheme.actInd);

tableHeader.add(arrow);
tableHeader.add(statusLabel);
tableHeader.add(lastUpdatedLabel);
tableHeader.add(actInd);
tableView.headerPullView = tableHeader;

win1.add(tableView);

tableView.beginReloading = function(){
    refreshTimeline();
};

function endReloading(success){
    tableView.setContentInsets({top:0},{animated:true});
    reloading = false;
    if(success){
        lastUpdatedLabel.text = "Last Updated: "+formatDate();
    }
    statusLabel.text = "Pull down to refresh...";
    actInd.hide();
    arrow.show();
};

tableView.addEventListener('scroll', function(e) {
    var offset = e.contentOffset.y;
    if (offset <= -65.0 && !pulling) {
        var t = Ti.UI.create2DMatrix();
        t = t.rotate(-180);
        pulling = true;
        arrow.animate({
            transform : t,
            duration : 180
        });
        statusLabel.text = "Release to refresh...";
    } else if (pulling && offset > -65.0 && offset < 0) {
        pulling = false;
        var t = Ti.UI.create2DMatrix();
        arrow.animate({
            transform : t,
            duration : 180
        });
        statusLabel.text = "Pull down to refresh...";
    }
});

tableView.addEventListener('dragEnd', function() {
    if (pulling && !reloading) {
        reloading = true;
        pulling = false;
        arrow.hide();
        actInd.show();
        statusLabel.text = "Reloading...";
        tableView.setContentInsets({
            top : 60
        }, {
            animated : true
        });

        tableView.scrollToTop(-60, true);
        arrow.transform = Ti.UI.create2DMatrix();
        tableView.beginReloading();
    }
});
// --pull to refresh

function refreshTimeline() {
	// var atmos = require('atmos');
	var resultApplier = function(e) {
		var resJSON = JSON.parse(e.source.responseText);
		updateTimeline(resJSON.results);
	};
	if (win1.timeline_type === 'global') {
		atmos.getGlobalTimeline(resultApplier);
	}
	else if (win1.timeline_type === 'talk') {
		atmos.getTalkTimeline(resultApplier);
	}
	else if (win1.timeline_type === 'private') {
		atmos.getPrivateTimeline(resultApplier);
	}
    endReloading(true);
}

refreshTimeline();
<|endoftext|>