<commit_before>/*
    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 <http://www.gnu.org/licenses/>.
*/

define('view/item_info',
       ['jquery', 'underscore', 'util', 'consts', 'model/diff', 'model/types',
        'model/graph', 'messages', 'domain_types', 'view/selection'],
function($,        _,            util,   consts,   model_diff,   model_types,
         model_graph,   messages,   domain_types,        selection) {

"use strict";

var rz_core; // set via init

// constants
var DEBOUNCE_TIME = 500; // milliseconds

// aliases
var is_node = model_graph.is_node;

// variables
var msg_node = $('.info-card-message'),
    setup_done = false,
    info_container = $('.info-container'),
    close_button = $('#editbox-closebutton'),
    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);
            }
            return [attr, element];
        })),
    change_handlers = [],
    status_display = info_container.find('#displaystatus'),
    status = info_container.find('#editstatus'),
    diffBusUnsubscribe,
    outside_change = false,
    visible_attributes,
    item,
    graph,
    edited_attributes;

/**
 *  Adds the div with the label, return the edit child
 */
function form_add_element(attr)
{
    var dt_ui_element = domain_types.attribute_ui[attr],
        value_element_type = dt_ui_element === undefined ? 'textarea' : dt_ui_element,
        div = $('<div>'),
        label = $('<div>'),
        value = $('<' + value_element_type + '></' + value_element_type + '>');

    div.attr('id', attr);
    div.addClass('info-container__row');
    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.insertAfter(msg_node);
    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);
    ret = _.pick(ret, _.keys(edited_attributes));
    return ret;
}

function update_item(item, new_data)
{
    if (is_node(item)) {
        graph.update_node(item, new_data);
    } else {
        graph.update_link(item, new_data);
    }
}

function get_item_from_graph()
{
    return is_node(item) ? graph.find_node__by_id(item.id) : graph.find_link__by_id(item.id);
}

function no_change()
{
    return _.isEqual(_.pick(get_item_from_graph(), _.keys(edited_attributes)), _get_form_data());
}

function commit()
{
    if (item === null || outside_change || no_change()) {
        return;
    }
    update_item(item, _get_form_data());
}

function textarea_resize(text, min, max)
{
    var height;
    text.style.height = 'auto';
    height = text.scrollHeight;
    height = Math.max(min, 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');
    });
    streams.forEach(function (stream) {
        stream.onValue(function (e) {
            edited_attributes[e.target.id.slice(4)] = 1;
        });
    });
    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 = [];
}

/**
 * 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;
    $('#edit-dialog__save').on('click', function (e) {
        e.preventDefault();
        commit();
        hide();
    });

    // 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)) {
            var changed = changed_set[item.id].__attr_write,
                changed_keys = _.keys(changed),
                form_subset = _.pick(_get_form_data(), changed_keys);

            if (!_.isEqual(form_subset, changed)) {
                warning('!! item has been changed !!');
                outside_change = true;
            }
            return;
        }
    });
}

function warning(string)
{
    msg_node.text(string);
}

function base_element_for_attribute(attr)
{
    return info_container.find('#' + attr);
}

function edit_element_for_attribute(attr)
{
    return info_container.find('#edit' + attr);
}

function update_textarea(textarea, value)
{
    textarea.val(value);
    textarea_resize(textarea[0], 25, 150);
}

function show(_graph, new_item, opt_attributes_data)
{
    var hidden_attributes,
        visible_elements,
        hidden_elements,
        new_visible_attributes = opt_attributes_data === undefined ?
            undefined : _.pluck(opt_attributes_data, 0),
        new_editable_attributes = _.object(opt_attributes_data);

    visible_attributes = (new_visible_attributes || model_types.type_attributes(new_item.type)).slice(0).reverse();
    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(get_item_from_graph() !== null);

    setup_click_handlers();

    _.each(hidden_elements, function (element) { element.hide(); });
    _.each(visible_elements, function (element) { element.show(); });

    info_container.attr('class', 'info-container'); // clear previous type-x css classes
    info_container.addClass('type-' + item.type); // add css type class

    _.each(visible_attributes, function (attr) {
        var element = edit_element_for_attribute(attr),
            value = item[attr],
            editable = new_editable_attributes[attr] === undefined ? true
                : new_editable_attributes[attr];

        element.parent().insertAfter(msg_node);
        switch (attr) {
        case 'enddate':
        case 'startdate':
            if (element.datepicker !== undefined) {
                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);
        }
        if (element[0].readOnly !== undefined) {
            element[0].readOnly = !editable;
        }
    });
    info_container.show();
}

function init(_rz_core) {
    rz_core = _rz_core;
    item = null;
    outside_change = false;
    graph = undefined;
    visible_attributes = [];
    edited_attributes = {};
}

function hide(do_commit) {
    do_commit = do_commit === undefined || true;
    if (do_commit) {
        commit();
    }
    if (diffBusUnsubscribe) {
        diffBusUnsubscribe();
    }
    init();
    info_container.hide();
    selection.clear();
    if (rz_core.main_graph_view) {
        rz_core.main_graph_view.zen_mode__set(false);
    }
}
<commit_msg>client: fix the edge case (rz_core not defined when item_info.init is called) completely<commit_after>/*
    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 <http://www.gnu.org/licenses/>.
*/

define('view/item_info',
       ['jquery', 'underscore', 'util', 'consts', 'model/diff', 'model/types',
        'model/graph', 'messages', 'domain_types', 'view/selection'],
function($,        _,            util,   consts,   model_diff,   model_types,
         model_graph,   messages,   domain_types,        selection) {

"use strict";

var rz_core; // set via init

// constants
var DEBOUNCE_TIME = 500; // milliseconds

// aliases
var is_node = model_graph.is_node;

// variables
var msg_node = $('.info-card-message'),
    setup_done = false,
    info_container = $('.info-container'),
    close_button = $('#editbox-closebutton'),
    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);
            }
            return [attr, element];
        })),
    change_handlers = [],
    status_display = info_container.find('#displaystatus'),
    status = info_container.find('#editstatus'),
    diffBusUnsubscribe,
    outside_change = false,
    visible_attributes,
    item,
    graph,
    edited_attributes;

/**
 *  Adds the div with the label, return the edit child
 */
function form_add_element(attr)
{
    var dt_ui_element = domain_types.attribute_ui[attr],
        value_element_type = dt_ui_element === undefined ? 'textarea' : dt_ui_element,
        div = $('<div>'),
        label = $('<div>'),
        value = $('<' + value_element_type + '></' + value_element_type + '>');

    div.attr('id', attr);
    div.addClass('info-container__row');
    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.insertAfter(msg_node);
    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);
    ret = _.pick(ret, _.keys(edited_attributes));
    return ret;
}

function update_item(item, new_data)
{
    if (is_node(item)) {
        graph.update_node(item, new_data);
    } else {
        graph.update_link(item, new_data);
    }
}

function get_item_from_graph()
{
    return is_node(item) ? graph.find_node__by_id(item.id) : graph.find_link__by_id(item.id);
}

function no_change()
{
    return _.isEqual(_.pick(get_item_from_graph(), _.keys(edited_attributes)), _get_form_data());
}

function commit()
{
    if (item === null || outside_change || no_change()) {
        return;
    }
    update_item(item, _get_form_data());
}

function textarea_resize(text, min, max)
{
    var height;
    text.style.height = 'auto';
    height = text.scrollHeight;
    height = Math.max(min, 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');
    });
    streams.forEach(function (stream) {
        stream.onValue(function (e) {
            edited_attributes[e.target.id.slice(4)] = 1;
        });
    });
    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 = [];
}

/**
 * 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;
    $('#edit-dialog__save').on('click', function (e) {
        e.preventDefault();
        commit();
        hide();
    });

    // 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)) {
            var changed = changed_set[item.id].__attr_write,
                changed_keys = _.keys(changed),
                form_subset = _.pick(_get_form_data(), changed_keys);

            if (!_.isEqual(form_subset, changed)) {
                warning('!! item has been changed !!');
                outside_change = true;
            }
            return;
        }
    });
}

function warning(string)
{
    msg_node.text(string);
}

function base_element_for_attribute(attr)
{
    return info_container.find('#' + attr);
}

function edit_element_for_attribute(attr)
{
    return info_container.find('#edit' + attr);
}

function update_textarea(textarea, value)
{
    textarea.val(value);
    textarea_resize(textarea[0], 25, 150);
}

function show(_graph, new_item, opt_attributes_data)
{
    var hidden_attributes,
        visible_elements,
        hidden_elements,
        new_visible_attributes = opt_attributes_data === undefined ?
            undefined : _.pluck(opt_attributes_data, 0),
        new_editable_attributes = _.object(opt_attributes_data);

    visible_attributes = (new_visible_attributes || model_types.type_attributes(new_item.type)).slice(0).reverse();
    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(get_item_from_graph() !== null);

    setup_click_handlers();

    _.each(hidden_elements, function (element) { element.hide(); });
    _.each(visible_elements, function (element) { element.show(); });

    info_container.attr('class', 'info-container'); // clear previous type-x css classes
    info_container.addClass('type-' + item.type); // add css type class

    _.each(visible_attributes, function (attr) {
        var element = edit_element_for_attribute(attr),
            value = item[attr],
            editable = new_editable_attributes[attr] === undefined ? true
                : new_editable_attributes[attr];

        element.parent().insertAfter(msg_node);
        switch (attr) {
        case 'enddate':
        case 'startdate':
            if (element.datepicker !== undefined) {
                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);
        }
        if (element[0].readOnly !== undefined) {
            element[0].readOnly = !editable;
        }
    });
    info_container.show();
}

function init(_rz_core) {
    rz_core = _rz_core;
    item = null;
    outside_change = false;
    graph = undefined;
    visible_attributes = [];
    edited_attributes = {};
}

function hide(do_commit) {
    do_commit = do_commit === undefined || true;
    if (do_commit) {
        commit();
    }
    if (diffBusUnsubscribe) {
        diffBusUnsubscribe();
    }
    init();
    info_container.hide();
    selection.clear();
    if (rz_core && rz_core.main_graph_view) {
        rz_core.main_graph_view.zen_mode__set(false);
    }
}

{'fraction_non_alphanumeric': 0.12664028327431784, 'fraction_numerical': 0.0028119141845448863, 'mean_word_length': 2.701811873554356, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3066283', 'n_tokens_mistral': 5853, 'n_tokens_neox': 5710, 'n_words': 1301}