summaryrefslogtreecommitdiff
path: root/src/client/view/item_info.js
diff options
context:
space:
mode:
authorAlon Levy <alon@pobox.com>2015-04-06 11:43:16 +0300
committerAlon Levy <alon@pobox.com>2015-04-06 11:43:16 +0300
commit3ae06034b88e7a9887a1c9fb2f5e623cd9469669 (patch)
treef4fcf31e491ded0f747538af683322bdad0730ba /src/client/view/item_info.js
parentcadfc79763a9c5c7473ca8ab764a0f9c56b125c1 (diff)
client: property tab cleanup; resolve #396
Diffstat (limited to 'src/client/view/item_info.js')
-rw-r--r--src/client/view/item_info.js276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/client/view/item_info.js b/src/client/view/item_info.js
new file mode 100644
index 00000000..1f187ce0
--- /dev/null
+++ b/src/client/view/item_info.js
@@ -0,0 +1,276 @@
+define(['jquery', 'jquery-ui', 'util', 'consts', 'view/helpers', 'model/diff', 'model/types'],
+function($, _unused_jquery_ui, util, consts, view_helpers, model_diff, model_types) {
+
+var DEBOUNCE_TIME = 500; // milliseconds
+
+var item = null,
+ msg_node = $('.info-card-message'),
+ graph,
+ setup_done = false,
+ info = $('#info'),
+ info_container = $('.info-container'),
+ form_element = $('#editbox'),
+ delete_button = $('#edit-dialog__delete'),
+ form = _.object(model_types.all_attributes.map(function (attr) {
+ var element = edit_element_for_attribute(attr);
+
+ if (element.length == 0) {
+ element = form_add_element(attr, 'textarea');
+ }
+ return [attr, element];
+ })),
+ change_handlers = [],
+ status_display = info.find('#displaystatus'),
+ status = info.find('#editstatus');
+
+/**
+ * Adds the div with the label, return the edit child
+ */
+function form_add_element(attr, value_element_type)
+{
+ var div = $('<div></div>'),
+ label = $('<label></label>'),
+ value = $('<' + value_element_type + '></' + value_element_type + '>'),
+ delete_button = form_element.find('#info-container__bottom-btn-bar');
+
+ div.attr('id', attr);
+ label.addClass('info-card-attr');
+ label.text(model_types.attribute_titles[attr] + ':');
+ value.addClass('info-card-attr-val');
+ value.attr('id', 'edit' + attr);
+ div.append(label);
+ div.append(value);
+ div.insertBefore(delete_button);
+ return value;
+}
+
+function clean_url(candidate_url)
+{
+ if (candidate_url.length == 0) {
+ return '';
+ }
+ if (candidate_url.search('://') != -1) {
+ return candidate_url;
+ }
+ return 'https://' + candidate_url;
+}
+
+function _get_form_data() {
+ var ret = _.object(_.keys(form),_.values(form).map(function (x) { return x.val(); }));
+
+ ret.url = clean_url(ret.url);
+ return ret;
+}
+
+function is_node(item)
+{
+ return item.__src === undefined;
+}
+
+function update_item(item, new_data)
+{
+ if (is_node(item)) {
+ graph.update_node(item, new_data);
+ } else {
+ graph.update_link(item, new_data);
+ }
+}
+
+function commit()
+{
+ if (item === null) {
+ return;
+ }
+ update_item(item, _get_form_data());
+}
+
+function textarea_resize(text, max)
+{
+ var height;
+ text.style.height = 'auto';
+ height = text.scrollHeight;
+ if (max) {
+ height = Math.min(max, height);
+ }
+ text.style.height = height + 'px';
+}
+
+function setup_change_handlers()
+{
+ disable_change_handlers();
+ // auto save after DEBOUNCE_TIME inactivity
+ var streams = _.map(_.values(form), function (element) {
+ return element.asEventStream('change input keyup');
+ });
+ var single = _.reduce(streams, function (stream_a, stream_b) { return stream_a.merge(stream_b); });
+ change_handlers = [single.debounce(DEBOUNCE_TIME).onValue(function () {
+ commit();
+ })];
+}
+
+function disable_change_handlers()
+{
+ _.each(change_handlers, function (unsub) { unsub(); });
+ change_handlers = [];
+}
+
+function delete_item()
+{
+ if (is_node(item)) {
+ graph.nodes__delete([item.id]);
+ } else {
+ graph.links__delete([item.id]);
+ }
+}
+
+/**
+ * XXX
+ * need to update fields iff there have been no new input events associated with them since commit.
+ * do not look at contents because that will exist in the past too.
+ *
+ * see Bacon.awaiting.
+ *
+ * For now we just show a warning instead telling the user the dialog is not up to date.
+ */
+function setup_click_handlers()
+{
+ setup_change_handlers();
+ if (setup_done) {
+ return;
+ }
+ setup_done = true;
+ form_element.on('keydown', function (e) {
+ if (e.which == consts.VK_ENTER && e.target !== delete_button[0]) {
+ e.preventDefault();
+ }
+ });
+ delete_button.on('click', function (e) {
+ var msg = is_node(item) ? 'delete node?' : 'delete link?';
+
+ e.preventDefault();
+ hide();
+ if (confirm(msg)) {
+ delete_item(item);
+ }
+ });
+ $('#edit-dialog__save').on('click', function (e) {
+ e.preventDefault();
+ hide();
+ commit();
+ });
+
+ // warn user if the item has been changed while open
+ diffBusUnsubscribe = graph.diffBus.onValue(function (diff) {
+ var removed_set = is_node(item) ? diff.node_id_set_rm : diff.link_id_set_rm,
+ changed_set = is_node(item) ? diff.id_to_node_map : diff.id_to_link_map;
+
+ if (model_diff.is_topo_diff(diff) && _.contains(removed_set, item.id)) {
+ warning('!! item has been deleted !!');
+ return;
+ }
+ if (model_diff.is_attr_diff(diff) || _.contains(_.keys(changed_set), item.id)) {
+ warning('!! item has been changed !!');
+ return;
+ }
+ });
+}
+
+function warning(string)
+{
+ msg_node.text(string);
+}
+
+function base_element_for_attribute(attr)
+{
+ return info.find('#' + attr);
+}
+
+function edit_element_for_attribute(attr)
+{
+ return info.find('#edit' + attr);
+}
+
+function update_textarea(textarea, value)
+{
+ textarea.val(value);
+ textarea_resize(textarea[0], 150);
+}
+
+function show(_graph, new_item, visible_attributes)
+{
+ var visible_attributes,
+ hidden_attributes,
+ visible_elements,
+ hidden_elements,
+ max_height;
+
+ visible_attributes = visible_attributes || model_types.type_attributes(new_item.type).slice(0);
+ hidden_attributes = _.difference(model_types.all_attributes, visible_attributes),
+ visible_elements = visible_attributes.map(base_element_for_attribute);
+ hidden_elements = hidden_attributes.map(base_element_for_attribute);
+ warning(''); // reset warning
+ graph = _graph;
+ item = new_item;
+ util.assert((is_node(item) ? graph.find_node__by_id : graph.find_link__by_id)(item.id) != null);
+
+ setup_click_handlers();
+
+ _.each(hidden_elements, function (element) { element.hide(); });
+ _.each(visible_elements, function (element) { element.show(); });
+
+ info.attr('class', 'info');
+ info.addClass('type-' + item.type); // Add a class to distinguish types for css
+
+ // hack - should be able to set max-height via css percentage, no?
+ max_height = $(document.body).innerHeight() - $('.info')[0].getBoundingClientRect().top * 2;
+ info_container[0].style['max-height'] = String(max_height) + 'px';
+
+ info_container.show();
+ console.log(visible_attributes);
+ console.log(hidden_attributes);
+ _.each(visible_attributes, function (attr) {
+ var element = edit_element_for_attribute(attr);
+ value = item[attr];
+
+ switch (attr) {
+ case 'enddate':
+ case 'startdate':
+ element.datepicker({
+ inline: true,
+ showOtherMonths: true,
+ dayNamesMin: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+ });
+ break;
+ case 'name':
+ case 'description':
+ update_textarea(element, value);
+ break;
+ case 'status':
+ if (_.contains(rz_config.role_set, 'admin')) {
+ status.val(item.status);
+ status.show();
+ status_display.hide();
+ } else {
+ status_display.text(item.status);
+ status.hide();
+ status_display.show();
+ }
+ break;
+ }
+ if (element.val !== undefined) {
+ element.val(value);
+ }
+ });
+}
+
+function hide() {
+ item = null;
+ info_container.hide();
+}
+
+return {
+ show: show,
+ hide: hide,
+};
+
+});