/*
This file is part of rhizi, a collaborative knowledge graph editor.
Copyright (C) 2014-2015 Rhizi
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
define(['jquery', 'underscore', 'Bacon', 'view/selection'],
function($, _, Bacon, selection) {
var MINUTE_IN_MSEC = 60000;
var incomingActivityBus = new Bacon.Bus(),
graph_view_element,
graph_view,
graph,
activities = [];
function is_topo(diff)
{
return diff['link_id_set_rm'] !== undefined && diff['node_set_add'] !== undefined,
diff['node_id_set_rm'] !== undefined && diff['link_set_add'] !== undefined;
}
function is_attr(diff)
{
return diff['__type_node'] !== undefined && diff['__type_link'] !== undefined;
}
function node_ids_from_diff(diff)
{
return is_attr(diff) ? _.keys(diff['__type_node']) : _.pluck(diff.node_set_add, 'id');
}
function link_ids_from_diff(diff)
{
return is_attr(diff) ? _.keys(diff['__type_link']) : _.pluck(diff.link_set_add, 'id');
}
function diff_to_summary_str__topo(diff)
{
var nodes_added = _.pluck(diff.node_set_add, 'name'),
links_added = _.pluck(diff.link_set_add, 'name');
return (nodes_added.length > 0 ? 'added ' + nodes_added.join(' ') + ' nodes' : '') +
(nodes_added.length > 0 && links_added.length > 0 ? ', ' : '') +
(links_added.length > 0 ? 'added ' + links_added.join(' ') + ' links' : '');
}
function diff_to_summary_str__attr(diff)
{
//var nodes_changed = _.map(diff.__type_node0
var nodes_changed,
links_changed;
function collect(root, graph_elem_find_func) {
return _.map(_.keys(root), function (id) {
var e_name_or_id,
cur_graph_elem,
writes = root[id].__attr_write,
ret = [];
_.each(_.keys(writes), function (key) {
var val = writes[key];
if (key === 'name') {
ret.push('renamed to ' + val)
} else {
ret.push(key + ' changed to ' + val);
}
});
_.map(root[id].__attr_remove, function (id) {
ret.push(id + ' removed');
});
// attempt name resolution
cur_graph_elem = graph_elem_find_func(id);
e_name_or_id = (cur_graph_elem && cur_graph_elem.name) || id; // use id as fallback, eg. when element not present in current graph
return e_name_or_id + ': ' + ret.join(', '); /* TODO: use the previous name of the node, not the new name */
});
}
nodes_changed = collect(diff.__type_node, graph.find_node__by_id);
links_changed = collect(diff.__type_link, graph.find_link__by_id);
return (nodes_changed.concat(links_changed)).join(';');
}
function diff_to_summary_str(diff)
{
if (is_topo(diff)) {
return diff_to_summary_str__topo(diff);
}
if (is_attr(diff)) {
return diff_to_summary_str__attr(diff);
}
}
function time_diff(old_date, new_date)
{
var delta = Math.floor((new_date - old_date) / 1000.0),
seconds = Math.floor(delta % 60),
minutes = Math.floor((delta / 60) % 60),
hours = Math.floor((delta / 3600) % 24),
days = Math.floor((delta / 86400));
if (delta < 60) {
return 'less than a minute ago'
}
if (delta < 3600) {
if (minutes === 1) return '1 minute ago';
return '' + minutes + ' minutes ago';
}
if (delta < 86400) {
if (hours === 1) return '1 hour ago';
return '' + hours + ' hours ago';
}
if (days === 1) return 'a day ago';
return '' + days + ' days ago';
}
function make_time_ago_div(creation_date, now)
{
return
}
function Activity(diff)
{
var meta = diff.meta || {},
new_div = $('
'),
activity = this,
affected_node_ids = node_ids_from_diff(diff),
affected_link_ids = link_ids_from_diff(diff),
author = meta.author || 'Anonymous',
sentence = meta.sentence,
explanation = (sentence !== undefined && sentence.length > 0) ? sentence : diff_to_summary_str(diff);
this.div = new_div;
if (is_topo(diff)) {
new_div[0].classList.add('topo-diff');
}
if (is_attr(diff)) {
new_div[0].classList.add('attr-diff');
}
this.creation_date = meta.ts_created !== undefined ? new Date(meta.ts_created) : new Date();
this.commit = meta.commit; // [!] unused, should link to permanent url
this.author = author;
this.sentence = sentence;
this.explanation = explanation;
this.diff = diff;
this.affected_node_ids = affected_node_ids;
this.affected_link_ids = affected_link_ids;
this.author_element = $('by ' + author + ''),
this.explanation_element = $('' + explanation + '');
this.time_ago_element = $('');
new_div.append([this.explanation_element, this.author_element, this.time_ago_element]);
new_div.on('click', function (event) {
var affected_nodes = _.filter(_.map(activity.affected_node_ids, graph.find_node__by_id), null);
(event.shiftKey ? selection.invert_both : selection.select_both)
(affected_nodes, affected_links);
});
new_div.hover(function (e) {
var affected_nodes = _.filter(_.map(activity.affected_node_ids, graph.find_node__by_id), null);
affected_links = _.filter(_.map(activity.affected_link_ids, graph.find_link__by_id), null);
_.each(affected_nodes, graph_view.node__hover__start);
_.each(affected_links, graph_view.link__hover__start);
}, function (e) {
var affected_nodes = _.filter(_.map(activity.affected_node_ids, graph.find_node__by_id), null);
affected_links = _.filter(_.map(activity.affected_link_ids, graph.find_link__by_id), null);
_.each(affected_nodes, graph_view.node__hover__end);
_.each(affected_links, graph_view.link__hover__end);
});
this.update_time_ago_element(new Date());
}
Activity.prototype.update_time_ago_element = function (zero_time) {
this.time_ago_element.text(time_diff(this.creation_date, zero_time));
}
/**
* appendActivity(activity)
*
* [!] should be diff?
*
*/
function appendActivity(diff)
{
var activity = new Activity(diff);
activities.push(activity);
$('#activity_view__body').prepend(activity.div);
// TODO - only update visible (visible_in / out hooks?)
// TODO - x button
// TODO - user name (requires protocol update?)
}
function update_div_ago(activity)
{
activity.update_time_ago_element(new Date());
}
function update_ago()
{
_.each(activities, update_div_ago);
}
function clear()
{
activities.splice(0); // reset activities
$('#activity_view__body').empty();
}
function init(_graph, _graph_view, _graph_view_element)
{
incomingActivityBus.onValue(appendActivity);
graph_view = _graph_view;
graph = _graph;
setInterval(update_ago, MINUTE_IN_MSEC);
$('#checkbox__activity-view-toggle').on('change', function() {
$('#activity_view').toggle();
});
clear();
}
return {
init: init,
clear: clear,
incomingActivityBus: incomingActivityBus,
};
}); // close define