<commit_before>'use strict';

const EDITOR_MODE_CREATE = 1;
const EDITOR_MODE_EDIT   = 2;

const ADD_NEW_FIELD_BUTTON_ROW = 'add_new_field_button';

function EntityEditor(entity_event_subscriber) {
    this.__init__(entity_event_subscriber);
}

EntityEditor.prototype = {

    __init__: function(entity_event_subscriber) {
        this.entity_event_subscriber = entity_event_subscriber;
        this.current_mode = null;
    },

    create_or_save_changes_button_pressed: function() {
        if (this.current_mode === EDITOR_MODE_CREATE) {
            this._entity_created();
        } else {
            this._modify_entity();
        }
    },

    cancel_or_delete_button_pressed: function() {
        if (this.current_mode === EDITOR_MODE_CREATE) {
            this.wall_entity_editor.force_hide_self_and_all_child_attachments_recursively();
        } else {
            MANAGER_ENTITY.delete_entity_by_id(this.entity_id_being_edited);
        }
    },

    delete_entity_field: function(field_name) {
        delete this.entity_field_rows[field_name];
        this.wall_entity_editor.delete_row_by_name(field_name);

        if (this.current_mode === EDITOR_MODE_EDIT) {
            var entity_modify = MANAGER_ENTITY.get_entity_by_id(this.entity_id_being_edited);
            entity_modify.delete_property(field_name, true);
        }
    },

    create: function(edit_mode) {
        if (!is_defined(this.wall_entity_editor)) {
            this.wall_entity_editor = new FloatingWall(600, 400, new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 0), this.entity_event_subscriber.world, false, COLOR_FLOATING_WALL_SUCCESS);
            this.wall_entity_editor.set_auto_adjust_height(true);
            this.wall_entity_editor.manual_visibility = true;
            this.wall_entity_editor.set_attachment_depth_offset(10);
            this.wall_entity_editor.add_close_button();

            this.wall_title = this.wall_entity_editor.add_row(-1).add_3D_element('HODL IOTA', TYPE_TITLE);

            this.entity_field_rows = {};

            // Default field : entity type selector.
            this.entity_field_rows[ENTITY_DEFAULT_PROPERTY_TYPE] = new FieldRowEntityType(this);
            this.entity_field_rows[ENTITY_DEFAULT_PROPERTY_TYPE].create(null);

            // Default field : entity name.
            this.entity_field_rows[ENTITY_PROPERTY_NAME] = new FieldRowName(this);
            this.entity_field_rows[ENTITY_PROPERTY_NAME].create(null);

            // Both creating a new entity and editing an existing entity will contain an add a new field button.
            this.add_new_field_button = this.wall_entity_editor.add_row(null, ADD_NEW_FIELD_BUTTON_ROW).add_2D_button([0, 1], 'add new field', COLOR_BLUE, null);

            // Both creating a new entity and editing an existing entity will contain an create/save-changes button.
            this.create_or_save_changes_button = this.wall_entity_editor.add_row(null).add_2D_button([0, 1], 'SET MY TEXT', COLOR_GREEN, this.create_or_save_changes_button_pressed.bind(this));

            // Both creating a new entity and editing an existing entity will contain an delete/cancel button.
            this.cancel_or_delete_button = this.wall_entity_editor.add_row(null).add_2D_button([0, 1], 'SET MY TEXT', COLOR_RED, this.cancel_or_delete_button_pressed.bind(this));

            // Create the entity field creator.
            this.wall_entity_field_creater = new EntityFieldCreator(this);
            this.wall_entity_field_creater.set_display_button(this.add_new_field_button);
        } else {
            this.wall_entity_editor.detach_from_parent();
        }

        // Delete all previously made non-default entity field rows.
        var field_rows_to_delete = [];
        for (var key in this.entity_field_rows) {
            if (this.entity_field_rows.hasOwnProperty(key)) {
                if (key !== ENTITY_DEFAULT_PROPERTY_TYPE && key !== ENTITY_PROPERTY_NAME) {
                    field_rows_to_delete.push(key);
                }
            }
        }
        for (var r = 0; r < field_rows_to_delete.length; r++) {
            this.wall_entity_editor.delete_row_by_name(field_rows_to_delete[r]);
            delete this.entity_field_rows[field_rows_to_delete[r]];
        }

        if (edit_mode === EDITOR_MODE_CREATE) {
            this.current_mode = EDITOR_MODE_CREATE;

            this.wall_entity_editor.attach_to(this.create_new_entity_button);
            this.wall_title.update_text('Create New Entity');

            this.entity_field_rows[ENTITY_DEFAULT_PROPERTY_TYPE].set_value_to_default();
            this.entity_field_rows[ENTITY_PROPERTY_NAME].set_value_to_default();

            this.create_or_save_changes_button.update_text('create entity');
            this.cancel_or_delete_button.update_text('cancel');
        } else {
            this.current_mode = EDITOR_MODE_EDIT;

            this.wall_entity_editor.attach_to(this.entity_being_edited_button);

            var entity_being_edited = MANAGER_ENTITY.get_entity_by_id(this.entity_id_being_edited);

            this.wall_title.update_text('Editing : ' + entity_being_edited.get_value(ENTITY_PROPERTY_NAME));

            this.create_or_save_changes_button.update_text('save changes');
            this.cancel_or_delete_button.update_text('delete entity');

            this.entity_field_rows[ENTITY_DEFAULT_PROPERTY_TYPE].set_value_from_entity();
            this.entity_field_rows[ENTITY_PROPERTY_NAME].set_value_from_entity();

            // TODO : Populate all the required entity field rows.
            var all_non_default_editable_fields = entity_being_edited.get_all_non_default_editable_fields();
            // Sort the fields alphabetically.
            all_non_default_editable_fields = all_non_default_editable_fields.sort(function(a, b) {
                return a[0] > b[0];
            });

            for (var f = 0; f < all_non_default_editable_fields.length; f++) {
                this.add_entity_field(all_non_default_editable_fields[f][0], all_non_default_editable_fields[f][1]);
            }
        }

        this.wall_entity_editor.refresh_position_and_look_at();
        this.wall_entity_editor.force_display_self_and_all_child_attachments_recursively();
    },

    _add_entity_field: function(field_name) {
        if (field_name === ENTITY_PROPERTY_START_DATE_TIME || field_name === ENTITY_PROPERTY_END_DATE_TIME) {
            this.entity_field_rows[field_name] = new FieldRowDateTime(this, field_name);
        } else if (field_name === ENTITY_PROPERTY_COMPLETED) {
            this.entity_field_rows[field_name] = new FieldRowCompleted(this);
        } else {
            this.entity_field_rows[field_name] = new FieldRowRegular(this, field_name);
        }
        return this.entity_field_rows[field_name];
    },

    add_entity_field: function(field_name, field_value) {
        // Check if the field was already added.
        if (!this.wall_entity_editor.has_row_with_name(field_name)) {

            // Get the row index of the 'add new field' button.
            var insert_index_for_row = this.wall_entity_editor.get_row_with_name(ADD_NEW_FIELD_BUTTON_ROW).row_number;

            var new_entity_field = this._add_entity_field(field_name);
            new_entity_field.create(insert_index_for_row, field_value);
        }
    },

    _opened_for_first_time: function() {
        this.entity_field_rows[ENTITY_DEFAULT_PROPERTY_TYPE].hide_extra_elements();
    },

    _hide_self_and_update: function() {
        this.wall_entity_editor.force_hide_self_and_all_child_attachments_recursively();
    },

    /*___      ___   ___         ___  __    ___         __
     |__  |\ |  |  |  |  \ /    |__  |  \ |  |  | |\ | / _`
     |___ | \|  |  |  |   |     |___ |__/ |  |  | | \| \__> */
    edit_entity: function(entity_relative_id, entity_row_button) {
        this.entity_being_edited_button = entity_row_button;
        this.entity_id_being_edited = entity_relative_id;
        this.create(EDITOR_MODE_EDIT);
        this._opened_for_first_time();
    },

    _modify_entity: function() {
        var entity_being_edited = MANAGER_ENTITY.get_entity_by_id(this.entity_id_being_edited);
        var all_entities_fields_and_values = this._get_all_entity_fields_and_values();
        for (var f = 0; f < all_entities_fields_and_values.length; f++) {
            entity_being_edited.set_property(all_entities_fields_and_values[f][0], all_entities_fields_and_values[f][1], true);
            this.entity_being_edited_button.update_text(entity_being_edited.get_value(ENTITY_PROPERTY_NAME));
        }
        this._hide_self_and_update();
    },

    /*___      ___   ___         __   __   ___      ___    __
     |__  |\ |  |  |  |  \ /    /  ` |__) |__   /\   |  | /  \ |\ |
     |___ | \|  |  |  |   |     \__, |  \ |___ /~~\  |  | \__/ | \| */
    _entity_created: function() {
        var entity_properties = {};
        var all_entities_fields_and_values = this._get_all_entity_fields_and_values();
        for (var f = 0; f < all_entities_fields_and_values.length; f++) {
            entity_properties[all_entities_fields_and_values[f][0]] = all_entities_fields_and_values[f][1];
        }
        new Entity(entity_properties, true, this.entity_event_subscriber.world);
        this._hide_self_and_update();
    },

    set_create_entity_display_button: function(button) {
        this.create_new_entity_button = button;
        this.create_new_entity_button.set_engage_function(this._create_new_entity_button_pressed.bind(this));
    },

    _create_new_entity_button_pressed: function() {
        this.create(EDITOR_MODE_CREATE);
        this._opened_for_first_time();
    },

    /*__   ___ ___ ___  ___  __   __
     / _` |__   |   |  |__  |__) /__`
     \__> |___  |   |  |___ |  \ .__/ */
    in_creation_mode: function() {
        if (is_defined(this.current_mode)) {
            return this.current_mode === EDITOR_MODE_CREATE;
        }
        return false;
    },

    in_editing_mode: function() {
        if (is_defined(this.current_mode)) {
            return this.current_mode === EDITOR_MODE_EDIT;
        }
        return false;
    },

    _get_all_entity_fields_and_values: function() {
        var entity_fields_and_values = [];
        for (var key in this.entity_field_rows) {
            if (this.entity_field_rows.hasOwnProperty(key)) {
                if (key.startsWith(ENTITY_PROPERTY_START_TOKEN)) {
                    entity_fields_and_values.push([key, this.entity_field_rows[key].get_value()]);
                }
            }
        }
        return entity_fields_and_values;
    },

    has_field: function(field_name) {
        return this.wall_entity_editor.has_row_with_name(field_name);
    }

};<commit_msg>client side dynamic content management work 06<commit_after>'use strict';

const EDITOR_MODE_CREATE = 1;
const EDITOR_MODE_EDIT   = 2;

const ADD_NEW_FIELD_BUTTON_ROW = 'add_new_field_button';

function EntityEditor(entity_event_subscriber) {
    this.__init__(entity_event_subscriber);
}

EntityEditor.prototype = {

    __init__: function(entity_event_subscriber) {
        this.entity_event_subscriber = entity_event_subscriber;
        this.current_mode = null;
    },

    create_or_save_changes_button_pressed: function() {
        if (this.current_mode === EDITOR_MODE_CREATE) {
            this._entity_created();
        } else {
            this._modify_entity();
        }
    },

    cancel_or_delete_button_pressed: function() {
        if (this.current_mode === EDITOR_MODE_CREATE) {
            this.wall_entity_editor.force_hide_self_and_all_child_attachments_recursively();
        } else {
            MANAGER_ENTITY.delete_entity_by_id(this.entity_id_being_edited);
        }
    },

    delete_entity_field: function(field_name) {
        delete this.entity_field_rows[field_name];
        this.wall_entity_editor.delete_row_by_name(field_name);

        if (this.current_mode === EDITOR_MODE_EDIT) {
            var entity_modify = MANAGER_ENTITY.get_entity_by_id(this.entity_id_being_edited);
            entity_modify.delete_property(field_name, true);
        }
    },

    create: function(edit_mode) {
        if (!is_defined(this.wall_entity_editor)) {
            this.wall_entity_editor = new FloatingWall(600, 400, null, null, this.entity_event_subscriber.world, false, COLOR_FLOATING_WALL_SUCCESS);
            this.wall_entity_editor.manual_visibility = true;
            this.wall_entity_editor.set_attachment_depth_offset(10);
            this.wall_entity_editor.add_close_button();

            this.wall_title = this.wall_entity_editor.add_row(-1).add_3D_element('HODL IOTA', TYPE_TITLE);

            this.entity_field_rows = {};

            // Default field : entity type selector.
            this.entity_field_rows[ENTITY_DEFAULT_PROPERTY_TYPE] = new FieldRowEntityType(this);
            this.entity_field_rows[ENTITY_DEFAULT_PROPERTY_TYPE].create(null);

            // Default field : entity name.
            this.entity_field_rows[ENTITY_PROPERTY_NAME] = new FieldRowName(this);
            this.entity_field_rows[ENTITY_PROPERTY_NAME].create(null);

            // Both creating a new entity and editing an existing entity will contain an add a new field button.
            this.add_new_field_button = this.wall_entity_editor.add_row(null, ADD_NEW_FIELD_BUTTON_ROW).add_2D_button([0, 1], 'add new field', COLOR_BLUE, null);

            // Both creating a new entity and editing an existing entity will contain an create/save-changes button.
            this.create_or_save_changes_button = this.wall_entity_editor.add_row(null).add_2D_button([0, 1], 'SET MY TEXT', COLOR_GREEN, this.create_or_save_changes_button_pressed.bind(this));

            // Both creating a new entity and editing an existing entity will contain an delete/cancel button.
            this.cancel_or_delete_button = this.wall_entity_editor.add_row(null).add_2D_button([0, 1], 'SET MY TEXT', COLOR_RED, this.cancel_or_delete_button_pressed.bind(this));

            // Create the entity field creator.
            this.wall_entity_field_creater = new EntityFieldCreator(this);
            this.wall_entity_field_creater.set_display_button(this.add_new_field_button);
        } else {
            this.wall_entity_editor.detach_from_parent();
        }

        // Delete all previously made non-default entity field rows.
        var field_rows_to_delete = [];
        for (var key in this.entity_field_rows) {
            if (this.entity_field_rows.hasOwnProperty(key)) {
                if (key !== ENTITY_DEFAULT_PROPERTY_TYPE && key !== ENTITY_PROPERTY_NAME) {
                    field_rows_to_delete.push(key);
                }
            }
        }
        for (var r = 0; r < field_rows_to_delete.length; r++) {
            this.wall_entity_editor.delete_row_by_name(field_rows_to_delete[r]);
            delete this.entity_field_rows[field_rows_to_delete[r]];
        }

        if (edit_mode === EDITOR_MODE_CREATE) {
            this.current_mode = EDITOR_MODE_CREATE;

            this.wall_entity_editor.attach_to(this.create_new_entity_button);
            this.wall_title.update_text('Create New Entity');

            this.entity_field_rows[ENTITY_DEFAULT_PROPERTY_TYPE].set_value_to_default();
            this.entity_field_rows[ENTITY_PROPERTY_NAME].set_value_to_default();

            this.create_or_save_changes_button.update_text('create entity');
            this.cancel_or_delete_button.update_text('cancel');
        } else {
            this.current_mode = EDITOR_MODE_EDIT;

            this.wall_entity_editor.attach_to(this.entity_being_edited_button);

            var entity_being_edited = MANAGER_ENTITY.get_entity_by_id(this.entity_id_being_edited);

            this.wall_title.update_text('Editing : ' + entity_being_edited.get_value(ENTITY_PROPERTY_NAME));

            this.create_or_save_changes_button.update_text('save changes');
            this.cancel_or_delete_button.update_text('delete entity');

            this.entity_field_rows[ENTITY_DEFAULT_PROPERTY_TYPE].set_value_from_entity();
            this.entity_field_rows[ENTITY_PROPERTY_NAME].set_value_from_entity();

            // TODO : Populate all the required entity field rows.
            var all_non_default_editable_fields = entity_being_edited.get_all_non_default_editable_fields();
            // Sort the fields alphabetically.
            all_non_default_editable_fields = all_non_default_editable_fields.sort(function(a, b) {
                return a[0] > b[0];
            });

            for (var f = 0; f < all_non_default_editable_fields.length; f++) {
                this.add_entity_field(all_non_default_editable_fields[f][0], all_non_default_editable_fields[f][1]);
            }
        }

        this.wall_entity_editor.set_auto_adjust_height(true);
        this.wall_entity_editor.auto_adjust_height_if_needed();
        this.wall_entity_editor.refresh_position_and_look_at();
        this.wall_entity_editor.force_display_self_and_all_child_attachments_recursively();
    },

    _add_entity_field: function(field_name) {
        if (field_name === ENTITY_PROPERTY_START_DATE_TIME || field_name === ENTITY_PROPERTY_END_DATE_TIME) {
            this.entity_field_rows[field_name] = new FieldRowDateTime(this, field_name);
        } else if (field_name === ENTITY_PROPERTY_COMPLETED) {
            this.entity_field_rows[field_name] = new FieldRowCompleted(this);
        } else {
            this.entity_field_rows[field_name] = new FieldRowRegular(this, field_name);
        }
        return this.entity_field_rows[field_name];
    },

    add_entity_field: function(field_name, field_value) {
        // Check if the field was already added.
        if (!this.wall_entity_editor.has_row_with_name(field_name)) {

            // Get the row index of the 'add new field' button.
            var insert_index_for_row = this.wall_entity_editor.get_row_with_name(ADD_NEW_FIELD_BUTTON_ROW).row_number;

            var new_entity_field = this._add_entity_field(field_name);
            new_entity_field.create(insert_index_for_row, field_value);
        }
    },

    _opened_for_first_time: function() {
        this.entity_field_rows[ENTITY_DEFAULT_PROPERTY_TYPE].hide_extra_elements();
    },

    _hide_self_and_update: function() {
        this.wall_entity_editor.force_hide_self_and_all_child_attachments_recursively();
    },

    /*___      ___   ___         ___  __    ___         __
     |__  |\ |  |  |  |  \ /    |__  |  \ |  |  | |\ | / _`
     |___ | \|  |  |  |   |     |___ |__/ |  |  | | \| \__> */
    edit_entity: function(entity_relative_id, entity_row_button) {
        this.entity_being_edited_button = entity_row_button;
        this.entity_id_being_edited = entity_relative_id;
        this.create(EDITOR_MODE_EDIT);
        this._opened_for_first_time();
    },

    _modify_entity: function() {
        var entity_being_edited = MANAGER_ENTITY.get_entity_by_id(this.entity_id_being_edited);
        var all_entities_fields_and_values = this._get_all_entity_fields_and_values();
        for (var f = 0; f < all_entities_fields_and_values.length; f++) {
            entity_being_edited.set_property(all_entities_fields_and_values[f][0], all_entities_fields_and_values[f][1], true);
            this.entity_being_edited_button.update_text(entity_being_edited.get_value(ENTITY_PROPERTY_NAME));
        }
        this._hide_self_and_update();
    },

    /*___      ___   ___         __   __   ___      ___    __
     |__  |\ |  |  |  |  \ /    /  ` |__) |__   /\   |  | /  \ |\ |
     |___ | \|  |  |  |   |     \__, |  \ |___ /~~\  |  | \__/ | \| */
    _entity_created: function() {
        var entity_properties = {};
        var all_entities_fields_and_values = this._get_all_entity_fields_and_values();
        for (var f = 0; f < all_entities_fields_and_values.length; f++) {
            entity_properties[all_entities_fields_and_values[f][0]] = all_entities_fields_and_values[f][1];
        }
        new Entity(entity_properties, true, this.entity_event_subscriber.world);
        this._hide_self_and_update();
    },

    set_create_entity_display_button: function(button) {
        this.create_new_entity_button = button;
        this.create_new_entity_button.set_engage_function(this._create_new_entity_button_pressed.bind(this));
    },

    _create_new_entity_button_pressed: function() {
        this.create(EDITOR_MODE_CREATE);
        this._opened_for_first_time();
    },

    /*__   ___ ___ ___  ___  __   __
     / _` |__   |   |  |__  |__) /__`
     \__> |___  |   |  |___ |  \ .__/ */
    in_creation_mode: function() {
        if (is_defined(this.current_mode)) {
            return this.current_mode === EDITOR_MODE_CREATE;
        }
        return false;
    },

    in_editing_mode: function() {
        if (is_defined(this.current_mode)) {
            return this.current_mode === EDITOR_MODE_EDIT;
        }
        return false;
    },

    _get_all_entity_fields_and_values: function() {
        var entity_fields_and_values = [];
        for (var key in this.entity_field_rows) {
            if (this.entity_field_rows.hasOwnProperty(key)) {
                if (key.startsWith(ENTITY_PROPERTY_START_TOKEN)) {
                    entity_fields_and_values.push([key, this.entity_field_rows[key].get_value()]);
                }
            }
        }
        return entity_fields_and_values;
    },

    has_field: function(field_name) {
        return this.wall_entity_editor.has_row_with_name(field_name);
    }

};<|endoftext|>


<commit_before>var rd = (function () {
    function findParent(elem, type) {
        do {
            elem = elem.parentNode;
        } while (elem.nodeName !== type.toUpperCase());
        return elem;
    }

    function findNextSibling(elem, pred) {
        while (elem && !pred(elem)) {
            elem = elem.nextSibling;
        }
        return elem;
    }

    function nextSiblingWithClass(elem, clazz) {
        return findNextSibling(elem, function (e) {
            return hasClass(e, clazz);
        });
    }

    function doWithChildren(elem, action) {
        var e = elem.firstChild;
        while (e) {
            action(e);
            e = e.nextSibling;
        }
    }

    function startsWith(s, test) {
        return s.substring(0, test.length) === test;
    }

    function rest(s, test) {
        return startsWith(s, test) ? s.substring(test.length) : false;
    }

    function hasClass(node, clazz) {
        return node.className && node.className.indexOf(clazz) >= 0;
    }

    function wrap(node, wrapper) {
        var next = node.nextSibling, parent = node.parentNode;
        parent.removeChild(node);
        wrapper.appendChild(node);
        if (next) {
            parent.insertBefore(wrapper, next);
        } else {
            parent.appendChild(wrapper);
        }
    }

    function parseQuery(q) {
        var i, parts, res = {}, params = q.substring(1).split('&');
        if (q.charAt(0) === '?') {
            for (i = 0; i < params.length; i++) {
                parts = params[i].split('=');
                res[decodeURIComponent(parts[0])] = parts.length == 1 ? null : decodeURIComponent(parts[1]);
            }
        }
        return res;
    }

    var items = {
        load: function (name) {
            var val = sessionStorage.getItem(name);
            return val ? JSON.parse(val) : null;
        },

        store: function (name, value) {
            sessionStorage.setItem(name, JSON.stringify(value));
        },
        remove: function (name) {
            sessionStorage.removeItem(name);
        }
    };

    return {
        setBaseUri: function (uri) {
            var href = window.location.href,
                hostPos = href.indexOf('://'),
                pathPos = href.indexOf('/', hostPos + 3),
                endPathPos = href.indexOf('/resource/'),
                host = href.substring(hostPos + 3, pathPos),
                path = href.substring(pathPos + 1, endPathPos);
            rd.baseUri = normalize(uri.replace('$host', host).replace('$path', path + '/../..'));

            function normalize(path) {
                var len;
                path = path.replace(/\/\.\//g, '/');
                do {
                    len = path.length;
                    path = path.replace(/\/[^\/]+\/\.\.\//, '/');
                } while (path.length != len);
                return path;
            }
        },

        showTrWithId: function (elem, id) {
            var show = null;
            doWithChildren(findParent(elem, 'tbody'), function (tr) {
                if (tr.nodeName === 'TR' && (hasClass(tr, 'bodyType') || show != null)) {
                    show = hasClass(tr, id);
                    tr.style.display = show ? 'table-row' : 'none';
                }
            });
        },
        showBodyWithId: function (elem, id) {
            var show = null;
            doWithChildren(elem.parentNode, function (div) {
                if (div.nodeName === 'DIV' && (hasClass(div, 'body') || show != null)) {
                    show = hasClass(div, id);
                    div.style.display = show ? 'block' : 'none';
                }
            });
        },
        showActionDetail: function (elem) {
            if (elem) {
                //doWithChildren(elem.parentNode, function (e) {
                //    if (hasClass(e, 'actionDetail')) {
                //        e.style.display = 'none';
                //    }
                //});
                var next = nextSiblingWithClass(elem, 'actionDetail');
                next.style.display = next.style.display === 'block' ? 'none' : 'block';
            }
        },
        tryOut: function (button, type, path, securitySchemes) {
            showLoader(true);
            showResponse();
            sendRequest(createRequest(rd.baseUri + path, securitySchemes), handleResponse);

            function createRequest(uri, securitySchemes) {
                var i, prop,
                    req = {
                        uri: uri, body: null, query: '', header: {}
                    },
                    form = findParent(button, 'form'),
                    creds = items.load('creds');
                if (creds) {
                    if (securitySchemes.indexOf(creds.name + ',') >= 0) {
                        for (prop in creds.data) {
                            setRequestValue(req, {name: prop, value: creds.data[prop]});
                        }
                    }
                }
                for (i = 0; i < form.elements.length; i++) {
                    setRequestValue(req, form.elements[i]);
                }
                return req;
            }

            function setRequestValue(req, elem) {
                var r;
                if (elem.value) {
                    if (r = rest(elem.name, 'q_')) {
                        req.query += encodeURIComponent(r) + '=' + encodeURIComponent(elem.value) + '&';
                    } else if (r = rest(elem.name, 'h_')) {
                        req.header[r] = elem.value;
                    } else if (r = rest(elem.name, 'u_')) {
                        req.uri = req.uri.replace('{' + r + '}', elem.value);
                    } else if (hasClass(elem, 'request') && hasClass(elem, 'contentType')) {
                        req.header['Content-Type'] = elem.value;
                    } else if (startsWith(elem.name, 'body') && findParent(elem, 'tr').style.display === 'table-row') {
                        req.body = elem.value;
                    }
                }
            }

            function showLoader(show) {
                var loader = nextSiblingWithClass(button, 'loader');
                loader.style.display = show ? 'inline' : 'none';
            }

            function hideLoader() {
                showLoader(false);
            }

            function showResponse() {
                var response = nextSiblingWithClass(button, 'response');
                response.style.display = 'block';
            }

            function sendRequest(r, handler) {
                var h, url = (r.uri + '?' + r.query).replace(/[?&]$/, ''),
                    req = new XMLHttpRequest();
                req.onreadystatechange = function () {
                    if (req.readyState === 4) {
                        handler(url, req);
                    }
                };
                req.addEventListener("load", hideLoader, false);
                req.addEventListener("error", hideLoader, false);
                req.addEventListener("abort", hideLoader, false);
                req.open(type, url, true);
                for (h in r.header) {
                    req.setRequestHeader(h, r.header[h]);
                    req.requestHeaders = r.header;
                }
                try {
                    req.send(r.body);
                } catch (e) {
                    alert('Could not send request: ' + e);
                }
            }

            function handleResponse(url, req) {
                var response = nextSiblingWithClass(button, 'response'),
                    i, reqHeaderStr = '';
                for (i in req.requestHeaders) {
                    reqHeaderStr += i + ': ' + req.requestHeaders[i] + '\n';
                }
                response.querySelector('[name=requestUrl]').firstChild.nodeValue = url;
                response.querySelector('[name=requestHeaders]').firstChild.nodeValue = reqHeaderStr;
                var resBody = response.querySelector('[name=responseBody]');
                if (isJson(req.getResponseHeader('Content-Type'))) {
                    resBody.innerHTML = PR.prettyPrintOne(js_beautify(req.responseText));
                    linkify(resBody);
                } else {
                    resBody.firstChild.nodeValue = req.responseText;
                }
                response.querySelector('[name=responseCode]').firstChild.nodeValue = req.status + ' ' + req.statusText;
                response.querySelector('[name=responseHeaders]').firstChild.nodeValue = req.getAllResponseHeaders();
            }

            function linkify(resBody) {
                var i, p, a, url, known, strings = resBody.querySelectorAll('.str');
                for (i = 0; i < strings.length; i++) {
                    var content = strings[i].firstChild.nodeValue;
                    if (content.substring(0, 5) === '"http') {
                        url = content.substring(1, content.length - 1);
                        known = knownLink(url);
                        if (known) {
                            url = document.location.protocol + '//' + document.location.host + document.location.pathname + '/' + rd.relPath + known.url + '.html?';
                            for (p in known.query) {
                                url += 'q_' + encodeURIComponent(p) + '=' + encodeURIComponent(known.query[p]) + '&';
                            }
                            for (p in known.vars) {
                                url += 'u_' + encodeURIComponent(p) + '=' + encodeURIComponent(known.vars[p]) + '&';
                            }
                            url += 'method=get&run';
                        }
                        a = document.createElement('a');
                        a.setAttribute('href', url);
                        a.setAttribute('target', '_blank');
                        wrap(strings[i], a);
                        strings[i].className = 'link';
                    }
                }
            }

            function knownLink(url) {
                var i, j, patternParts, res, match, parts, qPos;
                if (url.substring(0, rd.baseUri.length) === rd.baseUri) {
                    url = url.substring(rd.baseUri.length);
                    qPos = url.indexOf('?');
                    parts = (qPos < 0 ? url : url.substring(0, qPos)).split('/');
                    for (i = 0; i < rd.urls.length; i++) {
                        patternParts = rd.urls[i].split('/');
                        if (patternParts.length === parts.length) {
                            res = {url: rd.urls[i], vars: {}, query: parseQuery(url.substring(qPos))};
                            match = true;
                            for (j = 0; j < patternParts.length; j++) {
                                if (patternParts[j].charAt(0) === '{') {
                                    res.vars[patternParts[j].substring(1, patternParts[j].length - 1)] = parts[j];
                                } else if (patternParts[j] !== parts[j]) {
                                    match = false;
                                }
                            }
                            if (match) {
                                return res;
                            }
                        }
                    }
                }
            }

            function isJson(mimeType) {
                mimeType = mimeType || '';
                var pos = mimeType.indexOf(';');
                mimeType = pos >= 0 ? mimeType.substring(0, pos) : mimeType;
                return mimeType === 'application/json' || mimeType.substring(mimeType.length - 5) === '+json';
            }
        },
        login: function (button, name) {
            var i, elem, data = {},
                form = findParent(button, 'form');
            for (i = 0; i < form.elements.length; i++) {
                elem = form.elements[i];
                data[elem.name] = elem.value;
            }
            items.store('creds', {name: name, data: data});
            rd.initSecData(button);
        },
        logout: function (button, global) {
            items.remove('creds');
            if (global) {
                rd.initGlobalSecData(button);
            } else {
                rd.initSecData(button);
            }
        },
        initSecData: function (script) {
            var i, elem,
                form = findParent(script, 'form'),
                data = items.load('creds');

            if (data) {
                data = data.data;
            }
            for (i = 0; i < form.elements.length; i++) {
                elem = form.elements[i];
                if (elem.name === 'login') {
                    elem.style.display = data ? 'none' : 'inline';
                } else if (elem.name === 'logout') {
                    elem.style.display = data ? 'inline' : 'none';
                }
                if (data) {
                    elem.value = data[elem.name];
                }
            }
        },
        initGlobalSecData: function (script) {
            var data = items.load('creds'),
                logout = findParent(script, 'div').querySelector('[name=logout]');
            logout.style.display = data ? 'inline' : 'none';
        },
        showModel: function (span, model) {
            span.parentNode.querySelector('.modelTitle').className = (model ? 'active' : 'inactive') + ' modelTitle';
            span.parentNode.querySelector('.exampleTitle').className = (model ? 'inactive' : 'active') + ' exampleTitle';
            span.parentNode.querySelector('.model').style.display = model ? 'block' : 'none';
            span.parentNode.querySelector('.example').style.display = model ? 'none' : 'block';
        },
        useExample: function (div) {
            var input, code, rawExample = div.querySelector('.rawExample');
            code = (rawExample ? rawExample : div).firstChild.nodeValue;
            findParent(div, 'tr').querySelectorAll('td textarea,td input')[0].value = code;
        },
        applyQuery: function () {
            var inputs, i, q, foldables, expanded, query = parseQuery(document.location.search);
            for (q in query) {
                if (q.charAt(1) === '_') {
                    inputs = document.querySelectorAll('input[name=' + q + ']');
                    for (i = 0; i < inputs.length; i++) {
                        inputs[i].value = query[q];
                    }
                }
            }
            if (query['method']) {
                var method = query['method'].toUpperCase();
                rd.showActionDetail(document.querySelector('.actionHeader.bg_' + method));
                if (query['run']) {
                    document.querySelector('.try.' + method).click();
                }
            }
            if ((expanded = query['expanded']) !== undefined) {
                var ex = (expanded || '').split(',');
                foldables = document.querySelectorAll('.foldable.collapsed');
                for (i = 0; i < foldables.length; i++) {
                    rd.switchFoldable(foldables[i], expanded === null || ex.indexOf(foldables[i].getAttribute('name')) !== -1);
                }
            } else {
                var tree = items.load('tree') || {};
                foldables = document.querySelectorAll('.foldable');
                for (i = 0; i < foldables.length; i++) {
                    var expanded = tree[foldables[i].getAttribute('name')];
                    rd.switchFoldable(foldables[i], expanded === undefined ? false : expanded);
                }
            }
            var loc = document.location.pathname,
                pos = loc.indexOf('resource'),
                path = decodeURIComponent(loc.substring(pos + 8, loc.length - 5)),
                escPath=path.replace(/\//g,'\\/').replace(/\{/g,'\\{').replace(/\}/g,'\\}'),
                link = document.querySelector('a[title=' + escPath + ']');
            if (link) {
                link.classList.add('active');
            }
        },
        switchFoldable: function (span, expanded) {
            var cl = span.classList,
                name = span.getAttribute('name'),
                tree = items.load('tree') || {};
            if (hasClass(span, 'collapsed') && expanded !== false) {
                tree[name] = true;
                cl.remove('collapsed');
                nextSiblingWithClass(span, 'subLink').style.display = 'block';
            } else if (expanded !== true) {
                tree[name] = false;
                cl.add('collapsed');
                nextSiblingWithClass(span, 'subLink').style.display = 'none';
            }
            items.store('tree', tree);
        }
    };
}());<commit_msg>cut too long responses (fix #12)<commit_after>var rd = (function () {
    function findParent(elem, type) {
        do {
            elem = elem.parentNode;
        } while (elem.nodeName !== type.toUpperCase());
        return elem;
    }

    function findNextSibling(elem, pred) {
        while (elem && !pred(elem)) {
            elem = elem.nextSibling;
        }
        return elem;
    }

    function nextSiblingWithClass(elem, clazz) {
        return findNextSibling(elem, function (e) {
            return hasClass(e, clazz);
        });
    }

    function doWithChildren(elem, action) {
        var e = elem.firstChild;
        while (e) {
            action(e);
            e = e.nextSibling;
        }
    }

    function startsWith(s, test) {
        return s.substring(0, test.length) === test;
    }

    function rest(s, test) {
        return startsWith(s, test) ? s.substring(test.length) : false;
    }

    function hasClass(node, clazz) {
        return node.className && node.className.indexOf(clazz) >= 0;
    }

    function wrap(node, wrapper) {
        var next = node.nextSibling, parent = node.parentNode;
        parent.removeChild(node);
        wrapper.appendChild(node);
        if (next) {
            parent.insertBefore(wrapper, next);
        } else {
            parent.appendChild(wrapper);
        }
    }

    function parseQuery(q) {
        var i, parts, res = {}, params = q.substring(1).split('&');
        if (q.charAt(0) === '?') {
            for (i = 0; i < params.length; i++) {
                parts = params[i].split('=');
                res[decodeURIComponent(parts[0])] = parts.length == 1 ? null : decodeURIComponent(parts[1]);
            }
        }
        return res;
    }

    var items = {
        load: function (name) {
            var val = sessionStorage.getItem(name);
            return val ? JSON.parse(val) : null;
        },

        store: function (name, value) {
            sessionStorage.setItem(name, JSON.stringify(value));
        },
        remove: function (name) {
            sessionStorage.removeItem(name);
        }
    };

    return {
        setBaseUri: function (uri) {
            var href = window.location.href,
                hostPos = href.indexOf('://'),
                pathPos = href.indexOf('/', hostPos + 3),
                endPathPos = href.indexOf('/resource/'),
                host = href.substring(hostPos + 3, pathPos),
                path = href.substring(pathPos + 1, endPathPos);
            rd.baseUri = normalize(uri.replace('$host', host).replace('$path', path + '/../..'));

            function normalize(path) {
                var len;
                path = path.replace(/\/\.\//g, '/');
                do {
                    len = path.length;
                    path = path.replace(/\/[^\/]+\/\.\.\//, '/');
                } while (path.length != len);
                return path;
            }
        },

        showTrWithId: function (elem, id) {
            var show = null;
            doWithChildren(findParent(elem, 'tbody'), function (tr) {
                if (tr.nodeName === 'TR' && (hasClass(tr, 'bodyType') || show != null)) {
                    show = hasClass(tr, id);
                    tr.style.display = show ? 'table-row' : 'none';
                }
            });
        },
        showBodyWithId: function (elem, id) {
            var show = null;
            doWithChildren(elem.parentNode, function (div) {
                if (div.nodeName === 'DIV' && (hasClass(div, 'body') || show != null)) {
                    show = hasClass(div, id);
                    div.style.display = show ? 'block' : 'none';
                }
            });
        },
        showActionDetail: function (elem) {
            if (elem) {
                //doWithChildren(elem.parentNode, function (e) {
                //    if (hasClass(e, 'actionDetail')) {
                //        e.style.display = 'none';
                //    }
                //});
                var next = nextSiblingWithClass(elem, 'actionDetail');
                next.style.display = next.style.display === 'block' ? 'none' : 'block';
            }
        },
        tryOut: function (button, type, path, securitySchemes) {
            showLoader(true);
            showResponse();
            sendRequest(createRequest(rd.baseUri + path, securitySchemes), handleResponse);

            function createRequest(uri, securitySchemes) {
                var i, prop,
                    req = {
                        uri: uri, body: null, query: '', header: {}
                    },
                    form = findParent(button, 'form'),
                    creds = items.load('creds');
                if (creds) {
                    if (securitySchemes.indexOf(creds.name + ',') >= 0) {
                        for (prop in creds.data) {
                            setRequestValue(req, {name: prop, value: creds.data[prop]});
                        }
                    }
                }
                for (i = 0; i < form.elements.length; i++) {
                    setRequestValue(req, form.elements[i]);
                }
                return req;
            }

            function setRequestValue(req, elem) {
                var r;
                if (elem.value) {
                    if (r = rest(elem.name, 'q_')) {
                        req.query += encodeURIComponent(r) + '=' + encodeURIComponent(elem.value) + '&';
                    } else if (r = rest(elem.name, 'h_')) {
                        req.header[r] = elem.value;
                    } else if (r = rest(elem.name, 'u_')) {
                        req.uri = req.uri.replace('{' + r + '}', elem.value);
                    } else if (hasClass(elem, 'request') && hasClass(elem, 'contentType')) {
                        req.header['Content-Type'] = elem.value;
                    } else if (startsWith(elem.name, 'body') && findParent(elem, 'tr').style.display === 'table-row') {
                        req.body = elem.value;
                    }
                }
            }

            function showLoader(show) {
                var loader = nextSiblingWithClass(button, 'loader');
                loader.style.display = show ? 'inline' : 'none';
            }

            function hideLoader() {
                showLoader(false);
            }

            function showResponse() {
                var response = nextSiblingWithClass(button, 'response');
                response.style.display = 'block';
            }

            function sendRequest(r, handler) {
                var h, url = (r.uri + '?' + r.query).replace(/[?&]$/, ''),
                    req = new XMLHttpRequest();
                req.onreadystatechange = function () {
                    if (req.readyState === 4) {
                        handler(url, req);
                    }
                };
                req.addEventListener("load", hideLoader, false);
                req.addEventListener("error", hideLoader, false);
                req.addEventListener("abort", hideLoader, false);
                req.open(type, url, true);
                for (h in r.header) {
                    req.setRequestHeader(h, r.header[h]);
                    req.requestHeaders = r.header;
                }
                try {
                    req.send(r.body);
                } catch (e) {
                    alert('Could not send request: ' + e);
                }
            }

            function handleResponse(url, req) {
                var response = nextSiblingWithClass(button, 'response'),
                    i, reqHeaderStr = '';
                for (i in req.requestHeaders) {
                    reqHeaderStr += i + ': ' + req.requestHeaders[i] + '\n';
                }
                response.querySelector('[name=requestUrl]').firstChild.nodeValue = url;
                response.querySelector('[name=requestHeaders]').firstChild.nodeValue = reqHeaderStr;
                var resBody = response.querySelector('[name=responseBody]'),
                    resText = req.responseText;
                if (resText.length > 100000) {
                    resText = resText.substring(0, 100000) + '...';
                }
                if (isJson(req.getResponseHeader('Content-Type'))) {
                    resBody.innerHTML = PR.prettyPrintOne(js_beautify(resText));
                    linkify(resBody);
                } else {
                    resBody.firstChild.nodeValue = resText;
                }
                response.querySelector('[name=responseCode]').firstChild.nodeValue = req.status + ' ' + req.statusText;
                response.querySelector('[name=responseHeaders]').firstChild.nodeValue = req.getAllResponseHeaders();
            }

            function linkify(resBody) {
                var i, p, a, url, known, strings = resBody.querySelectorAll('.str');
                for (i = 0; i < strings.length; i++) {
                    var content = strings[i].firstChild.nodeValue;
                    if (content.substring(0, 5) === '"http') {
                        url = content.substring(1, content.length - 1);
                        known = knownLink(url);
                        if (known) {
                            url = document.location.protocol + '//' + document.location.host + document.location.pathname + '/' + rd.relPath + known.url + '.html?';
                            for (p in known.query) {
                                url += 'q_' + encodeURIComponent(p) + '=' + encodeURIComponent(known.query[p]) + '&';
                            }
                            for (p in known.vars) {
                                url += 'u_' + encodeURIComponent(p) + '=' + encodeURIComponent(known.vars[p]) + '&';
                            }
                            url += 'method=get&run';
                        }
                        a = document.createElement('a');
                        a.setAttribute('href', url);
                        a.setAttribute('target', '_blank');
                        wrap(strings[i], a);
                        strings[i].className = 'link';
                    }
                }
            }

            function knownLink(url) {
                var i, j, patternParts, res, match, parts, qPos;
                if (url.substring(0, rd.baseUri.length) === rd.baseUri) {
                    url = url.substring(rd.baseUri.length);
                    qPos = url.indexOf('?');
                    parts = (qPos < 0 ? url : url.substring(0, qPos)).split('/');
                    for (i = 0; i < rd.urls.length; i++) {
                        patternParts = rd.urls[i].split('/');
                        if (patternParts.length === parts.length) {
                            res = {url: rd.urls[i], vars: {}, query: parseQuery(url.substring(qPos))};
                            match = true;
                            for (j = 0; j < patternParts.length; j++) {
                                if (patternParts[j].charAt(0) === '{') {
                                    res.vars[patternParts[j].substring(1, patternParts[j].length - 1)] = parts[j];
                                } else if (patternParts[j] !== parts[j]) {
                                    match = false;
                                }
                            }
                            if (match) {
                                return res;
                            }
                        }
                    }
                }
            }

            function isJson(mimeType) {
                mimeType = mimeType || '';
                var pos = mimeType.indexOf(';');
                mimeType = pos >= 0 ? mimeType.substring(0, pos) : mimeType;
                return mimeType === 'application/json' || mimeType.substring(mimeType.length - 5) === '+json';
            }
        },
        login: function (button, name) {
            var i, elem, data = {},
                form = findParent(button, 'form');
            for (i = 0; i < form.elements.length; i++) {
                elem = form.elements[i];
                data[elem.name] = elem.value;
            }
            items.store('creds', {name: name, data: data});
            rd.initSecData(button);
        },
        logout: function (button, global) {
            items.remove('creds');
            if (global) {
                rd.initGlobalSecData(button);
            } else {
                rd.initSecData(button);
            }
        },
        initSecData: function (script) {
            var i, elem,
                form = findParent(script, 'form'),
                data = items.load('creds');

            if (data) {
                data = data.data;
            }
            for (i = 0; i < form.elements.length; i++) {
                elem = form.elements[i];
                if (elem.name === 'login') {
                    elem.style.display = data ? 'none' : 'inline';
                } else if (elem.name === 'logout') {
                    elem.style.display = data ? 'inline' : 'none';
                }
                if (data) {
                    elem.value = data[elem.name];
                }
            }
        },
        initGlobalSecData: function (script) {
            var data = items.load('creds'),
                logout = findParent(script, 'div').querySelector('[name=logout]');
            logout.style.display = data ? 'inline' : 'none';
        },
        showModel: function (span, model) {
            span.parentNode.querySelector('.modelTitle').className = (model ? 'active' : 'inactive') + ' modelTitle';
            span.parentNode.querySelector('.exampleTitle').className = (model ? 'inactive' : 'active') + ' exampleTitle';
            span.parentNode.querySelector('.model').style.display = model ? 'block' : 'none';
            span.parentNode.querySelector('.example').style.display = model ? 'none' : 'block';
        },
        useExample: function (div) {
            var input, code, rawExample = div.querySelector('.rawExample');
            code = (rawExample ? rawExample : div).firstChild.nodeValue;
            findParent(div, 'tr').querySelectorAll('td textarea,td input')[0].value = code;
        },
        applyQuery: function () {
            var inputs, i, q, foldables, expanded, query = parseQuery(document.location.search);
            for (q in query) {
                if (q.charAt(1) === '_') {
                    inputs = document.querySelectorAll('input[name=' + q + ']');
                    for (i = 0; i < inputs.length; i++) {
                        inputs[i].value = query[q];
                    }
                }
            }
            if (query['method']) {
                var method = query['method'].toUpperCase();
                rd.showActionDetail(document.querySelector('.actionHeader.bg_' + method));
                if (query['run']) {
                    document.querySelector('.try.' + method).click();
                }
            }
            if ((expanded = query['expanded']) !== undefined) {
                var ex = (expanded || '').split(',');
                foldables = document.querySelectorAll('.foldable.collapsed');
                for (i = 0; i < foldables.length; i++) {
                    rd.switchFoldable(foldables[i], expanded === null || ex.indexOf(foldables[i].getAttribute('name')) !== -1);
                }
            } else {
                var tree = items.load('tree') || {};
                foldables = document.querySelectorAll('.foldable');
                for (i = 0; i < foldables.length; i++) {
                    var expanded = tree[foldables[i].getAttribute('name')];
                    rd.switchFoldable(foldables[i], expanded === undefined ? false : expanded);
                }
            }
            var loc = document.location.pathname,
                pos = loc.indexOf('resource'),
                path = decodeURIComponent(loc.substring(pos + 8, loc.length - 5)),
                escPath = path.replace(/\//g, '\\/').replace(/\{/g, '\\{').replace(/\}/g, '\\}'),
                link = document.querySelector('a[title=' + escPath + ']');
            if (link) {
                link.classList.add('active');
            }
        },
        switchFoldable: function (span, expanded) {
            var cl = span.classList,
                name = span.getAttribute('name'),
                tree = items.load('tree') || {};
            if (hasClass(span, 'collapsed') && expanded !== false) {
                tree[name] = true;
                cl.remove('collapsed');
                nextSiblingWithClass(span, 'subLink').style.display = 'block';
            } else if (expanded !== true) {
                tree[name] = false;
                cl.add('collapsed');
                nextSiblingWithClass(span, 'subLink').style.display = 'none';
            }
            items.store('tree', tree);
        }
    };
}());<|endoftext|>


<commit_before>let {extend, timed, compose, identity} = require('./helpers')
let {Var, Fun, App, Def} = require('./core')
let {parse} = require('./parser')

// Returns the string representation for a given term t.
let termStr = (t, appParens = false, funParens = false) => {
  let h = t.highlight || identity
  switch (t.type) {
  case Var:
  case Def:
    return h(t.name)
  case Fun:
    let lambda = `λ${t.param}`
    if (t.highlightVar) lambda = t.highlightVar(lambda)
    let funStr = `${lambda}.${termStr(t.body)}`
    return h(funParens ? `(${funStr})` : funStr)
  case App:
    let lStr = termStr(t.left, false, true)
    let rStr = termStr(t.right, true, funParens)
    let appStr = `${lStr} ${rStr}`
    return h(appParens ? `(${appStr})` : appStr)
  }
}

let highlight = (t, fn) => {
  if (t.highlight)
    fn = compose(fn, t.highlight)
  return extend({}, t, {highlight: fn})
}

let highlightFunctionVar = (t, x, fn) => {
  let hx = highlight(Var(x), fn)
  let ht = substitute(t, x, hx)
  return extend(Fun(x, ht), {highlightVar: fn})
}

let composeFun = (fn, x) => (b) => fn(Fun(x, b))
let composeAppL = (fn, l) => (r) => fn(App(l, r))
let composeAppR = (fn, r) => (l) => fn(App(l, r))

let reduceCallByName = (t, cb) => {
  switch (t.type) {
  case Var:
  case Fun:
    return t
  case App:
    let l = reduceCallByName(t.left, composeAppR(cb, t.right))
    if (l.type === Fun)
      return reduceCallByName(apply(l, t.right, cb), cb)
    else
      // TODO This is suspicious. If some reductions were made in previous
      // l = reduceCallByName ... call, then we are losing the result of those
      // reductions, but we have recorded them with cb.
      return App(l, t.right)
  case Def:
    cb(markStep('def', t, t.term))
    return reduceCallByName(t.term, cb)
  }
}

let reduceNormal = (t, cb) => {
  switch (t.type) {
  case Var:
    return t
  case Fun:
    return Fun(t.param, reduceNormal(t.body, composeFun(cb, t.param)))
  case App:
    let l = reduceCallByName(t.left, composeAppR(cb, t.right))
    if (l.type === Fun) {
      return reduceNormal(apply(l, t.right, cb), cb)
    } else {
      l = reduceNormal(l, composeAppR(cb, t.right)) // Finish reducing l.
      let r = reduceNormal(t.right, composeAppL(cb, l))
      return App(l, r)
    }
  case Def:
    cb(markStep('def', t, t.term))
    return reduceNormal(t.term, cb)
  }
}

let reduceCallByValue = (t, cb) => {
  switch (t.type) {
  case Var:
  case Fun:
    return t
  case App:
    let l = reduceCallByValue(t.left, composeAppR(cb, t.right))
    let r = reduceCallByValue(t.right, composeAppL(cb, l))
    if (l.type === Fun)
      return reduceCallByValue(apply(l, r, cb), cb)
    else
      return App(l, r)
  case Def:
    cb(markStep('def', t, t.term))
    return reduceCallByValue(t.term, cb)
  }
}

let reduceApplicative = (t, cb) => {
  switch (t.type) {
  case Var:
    return t
  case Fun:
    return Fun(t.param, reduceApplicative(t.body, composeFun(cb, t.param)))
  case App:
    let l = reduceCallByValue(t.left, composeAppR(cb, t.right))
    if (l.type === Fun) {
      let r = reduceCallByValue(t.right, composeAppL(cb, l))
      return reduceApplicative(apply(l, r, cb), cb)
    } else {
      l = reduceApplicative(l, composeAppR(cb, t.right))
      let r = reduceApplicative(t.right, composeAppL(cb, l))
      return App(l, r)
    }
  case Def:
    cb(markStep('def', t, t.term))
    return reduceApplicative(t.term, cb)
  }
}

let apply = (fun, subst, cb) => {
  let renameCb = composeFun(composeAppR(cb, subst), fun.param)
  let renamedBody = renameForSubstitution(fun.body, fun.param, subst, renameCb)
  let renamed = App(Fun(fun.param, renamedBody), subst)
  let applied = applySubstitution(renamedBody, fun.param, subst)
  cb(markStep('beta', renamed, applied))
  return applied
}

// Applies the substitution T[x := S]
// I.e., substitutes the variable x for the term S in the term T.
let substitute = (t, x, s) => {
  switch (t.type) {
  case Var:
    // x[x := S] = S
    // y[x := S] = y
    return t.name === x ? s : t
  case Fun:
    // (λx.E)[x := S] = λx.E
    // λx creates a new context for x so no further substitution is needed.
    if (t.param === x) return t
    // (λy.E)[x := S] with x != y
    // If y is free in S and x is free in E, then must α-convert λy.E to avoid
    // name conflicts.
    if (freeIn(t.param, s) && freeIn(x, t.body)) {
      // (λy.E)[x := S] = λy'.(E[y := y'][x := S])
      let newVarName = renameVar(t.param, t.body, s)
      let renamedBody = applySubstitution(t.body, t.param, Var(newVarName))
      return Fun(newVarName, substitute(renamedBody, x, s))
    } else {
      // (λy.E)[x := S] = λy.(E[x := S])
      return Fun(t.param, substitute(t.body, x, s))
    }
  case App:
    // (U V)[x := S] = (U[x := S]) (V[x := S])
    return App(substitute(t.left, x, s), substitute(t.right, x, s))
  case Def:
    return t
  }
}

// Performs the α-conversions necessary for the substitution T[x := S], but does
// not perform the substitution itself.
// Records the α-conversions by calling cb.
let renameForSubstitution = (t, x, s, cb) => {
  switch (t.type) {
  case Var:
  case Def:
    return t
  case Fun:
    if (t.param === x) return t
    if (freeIn(t.param, s) && freeIn(x, t.body)) {
      let newVarName = renameVar(t.param, t.body, s)
      let renamedBody = applySubstitution(t.body, t.param, Var(newVarName))
      cb(markStep('alpha', t, t = Fun(newVarName, renamedBody)))
    }
    let body = renameForSubstitution(t.body, x, s, composeFun(cb, t.param))
    return Fun(t.param, body)
  case App:
    let l = renameForSubstitution(t.left, x, s, composeAppR(cb, t.right))
    let r = renameForSubstitution(t.right, x, s, composeAppL(cb, l))
    return App(l, r)
  }
}

// Applies the substitution T[x := S] directly, without doing α-conversions.
let applySubstitution = (t, x, s) => {
  switch (t.type) {
  case Var:
    return t.name === x ? s : t
  case Fun:
    return t.param === x
      ? t
      : Fun(t.param, applySubstitution(t.body, x, s))
  case App:
    let l = applySubstitution(t.left, x, s)
    let r = applySubstitution(t.right, x, s)
    return App(l, r)
  case Def:
    return t
  }
}

// Renames a variable to avoid naming conflicts case doing: a substitution.
let renameVar = (oldName, t, s) => {
  // Split the name into base and number part.
  let base = oldName.replace(/\d+$/, '')
  let match = oldName.match(/\d+$/)
  let n = match ? parseInt(match[0]) : 0

  while (true) {
    n++
    let newName = base + n
    let isValid =
      // Avoid name collisions with substitution term.
      !freeIn(newName, s) &&
      // Avoid name collisions with free variables in body.
      !freeIn(newName, t) &&
      // Avoid name collisions with inner functions.
      !varRenameCollides(t, oldName, newName)
    if (isValid)
      return newName
  }
}

// Whether the variable x is free in the term t.
let freeIn = (x, t) => {
  switch (t.type) {
  case Var:
    return t.name === x
  case Fun:
    return t.param !== x && freeIn(x, t.body)
  case App:
    return freeIn(x, t.left) || freeIn(x, t.right)
  case Def:
    // TODO return false directly. Defs don't have free variables!
    return freeIn(x, t.term)
  }
}

// Whether a variable rename collides in a given term. That is, if changing the
// occurrences of oldName with newName in t would make it change t's meaning
// (i.e. not be α-equivalent).
let varRenameCollides = (t, oldName, newName) => {
  switch (t.type) {
  case Var:
    return false
  case Fun:
    // A variable rename collides with this function if the old variable
    // was free in the function and the new name for the variable is the
    // same as the param of the function, thus changing old free variable
    // binding.
    return t.param === newName && freeIn(oldName, t) ||
      // Or if the renaming collides in the body of the function.
      varRenameCollides(t.body, oldName, newName)
  case App:
    return varRenameCollides(t.left, oldName, newName) ||
      varRenameCollides(t.right, oldName, newName)
  case Def:
    // TODO Is this necessary? Defs should never collide, as they don't have
    // free vars!
    return varRenameCollides(t.term, oldName, newName)
  }
}

let markStep = (type, before, after) =>
  extend({}, after, {step: {type, before}})

let find = (t, fn) => {
  if (fn(t))
    return t
  switch (t.type) {
  case Var:
  case Def:
    return
  case Fun:
    return find(t.body, fn)
  case App:
    return find(t.left, fn) || find(t.right, fn)
  }
}

let replace = (t, from, to) => {
  if (t === from)
    return to
  switch (t.type) {
  case Var:
  case Def:
    return t
  case Fun:
    let body = replace(t.body, from, to)
    return t.body === body ? t : Fun(t.param, body)
  case App:
    let l = replace(t.left, from, to)
    if (t.left !== l) return App(l, t.right)
    let r = replace(t.right, from, to)
    return t.right === r ? t : App(l, r)
  }
}

let expandStep = (t, options = {}) => {
  let stepTerm = find(t, subT => subT.step)
  let type = stepTerm.step.type
  let before = stepTerm.step.before
  let after = stepTerm

  let highlightFormer = options.highlightFormerTerm || identity
  let highlightSubst = options.highlightSubstitutionTerm || identity
  let highlightStep = options.highlightStep || identity

  switch (type) {
  case 'alpha':
    before = highlightFunctionVar(before.body, before.param, highlightFormer)
    after = highlightFunctionVar(after.body, after.param, highlightSubst)
    break
  case 'beta':
    let fun = before.left
    let hs = highlight(before.right, highlightSubst)
    let ha = highlightFunctionVar(fun.body, fun.param, highlightFormer)
    before = App(ha, hs)
    after = substitute(fun.body, fun.param, hs)
    break
  case 'def':
    before = highlight(before, highlightFormer)
    after = highlight(after, highlightSubst)
  }

  before = highlight(before, highlightStep)
  after = highlight(after, highlightStep)

  before = termStr(replace(t, stepTerm, before))
  after = termStr(replace(t, stepTerm, after))

  return {type, before, after}
}

let alphaEq = (t1, t2) => {
  if (t1.type === Def) return alphaEq(t1.term, t2)
  if (t2.type === Def) return alphaEq(t1, t2.term)
  if (t1.type !== t2.type) return false
  switch (t1.type) {
  case Var:
    return t1.name === t2.name
  case Fun:
    if (t1.param === t2.param)
      return alphaEq(t1.body, t2.body)
    else
      return alphaEq(t1.body, substitute(t2.body, t2.param, Var(t1.param)))
  case App:
    return alphaEq(t1.left, t2.left) && alphaEq(t1.right, t2.right)
  }
}

let findSynonyms = (term, defs) => {
  let synonyms = []
  for (let name in defs)
    if (alphaEq(term, defs[name]))
      synonyms.push(name)
  return synonyms
}

let defaultOptions = {
  maxSteps: 100,
  strategy: 'normal',
}

let reduceFunctions = {
  normal: reduceNormal,
  applicative: reduceApplicative,
  cbn: reduceCallByName,
  cbv: reduceCallByValue,
}

// Reduces a term up to its normal form and returns TODO What does it return?
let reduceTerm = timed('reduce', (term, defs, options) => {
  let {maxSteps, strategy} = extend({}, defaultOptions, options)
  let reduce = reduceFunctions[strategy]
  let enough = {}
  let steps = []
  let terminates = false
  try {
    reduce(term, t => {
      if (steps.length >= maxSteps) throw enough
      steps.push(t)
    })
    terminates = true
  } catch (e) {
    if (e !== enough) throw e
    terminates = false
  }

  let last = steps[steps.length - 1] || term
  let finalSynonyms = findSynonyms(last, defs)
  let initial = termStr(term)
  let final = termStr(last)
  let totalSteps = steps.length
  let renderStep = (i, options) =>
    expandStep(steps[i], options)
  return {initial, final, finalSynonyms, terminates, totalSteps, renderStep}
})

// Reduce a program and return with the reduction for each term in the program.
let reduceProgram = (program, options = {}) => {
  let {terms, defs} = parse(program)
  return terms.map(term => reduceTerm(term, defs, options))
}

module.exports = {
  Var, Fun, App, Def,
  parse,
  termStr,
  reduceProgram,
}
<commit_msg>Remove some TODOs<commit_after>let {extend, timed, compose, identity} = require('./helpers')
let {Var, Fun, App, Def} = require('./core')
let {parse} = require('./parser')

// Returns the string representation for a given term t.
let termStr = (t, appParens = false, funParens = false) => {
  let h = t.highlight || identity
  switch (t.type) {
  case Var:
  case Def:
    return h(t.name)
  case Fun:
    let lambda = `λ${t.param}`
    if (t.highlightVar) lambda = t.highlightVar(lambda)
    let funStr = `${lambda}.${termStr(t.body)}`
    return h(funParens ? `(${funStr})` : funStr)
  case App:
    let lStr = termStr(t.left, false, true)
    let rStr = termStr(t.right, true, funParens)
    let appStr = `${lStr} ${rStr}`
    return h(appParens ? `(${appStr})` : appStr)
  }
}

let highlight = (t, fn) => {
  if (t.highlight)
    fn = compose(fn, t.highlight)
  return extend({}, t, {highlight: fn})
}

let highlightFunctionVar = (t, x, fn) => {
  let hx = highlight(Var(x), fn)
  let ht = substitute(t, x, hx)
  return extend(Fun(x, ht), {highlightVar: fn})
}

let composeFun = (fn, x) => (b) => fn(Fun(x, b))
let composeAppL = (fn, l) => (r) => fn(App(l, r))
let composeAppR = (fn, r) => (l) => fn(App(l, r))

let reduceCallByName = (t, cb) => {
  switch (t.type) {
  case Var:
  case Fun:
    return t
  case App:
    let l = reduceCallByName(t.left, composeAppR(cb, t.right))
    if (l.type === Fun)
      return reduceCallByName(apply(l, t.right, cb), cb)
    else
      // TODO This is suspicious. If some reductions were made in previous
      // l = reduceCallByName ... call, then we are losing the result of those
      // reductions, but we have recorded them with cb.
      return App(l, t.right)
  case Def:
    cb(markStep('def', t, t.term))
    return reduceCallByName(t.term, cb)
  }
}

let reduceNormal = (t, cb) => {
  switch (t.type) {
  case Var:
    return t
  case Fun:
    return Fun(t.param, reduceNormal(t.body, composeFun(cb, t.param)))
  case App:
    let l = reduceCallByName(t.left, composeAppR(cb, t.right))
    if (l.type === Fun) {
      return reduceNormal(apply(l, t.right, cb), cb)
    } else {
      l = reduceNormal(l, composeAppR(cb, t.right)) // Finish reducing l.
      let r = reduceNormal(t.right, composeAppL(cb, l))
      return App(l, r)
    }
  case Def:
    cb(markStep('def', t, t.term))
    return reduceNormal(t.term, cb)
  }
}

let reduceCallByValue = (t, cb) => {
  switch (t.type) {
  case Var:
  case Fun:
    return t
  case App:
    let l = reduceCallByValue(t.left, composeAppR(cb, t.right))
    let r = reduceCallByValue(t.right, composeAppL(cb, l))
    if (l.type === Fun)
      return reduceCallByValue(apply(l, r, cb), cb)
    else
      return App(l, r)
  case Def:
    cb(markStep('def', t, t.term))
    return reduceCallByValue(t.term, cb)
  }
}

let reduceApplicative = (t, cb) => {
  switch (t.type) {
  case Var:
    return t
  case Fun:
    return Fun(t.param, reduceApplicative(t.body, composeFun(cb, t.param)))
  case App:
    let l = reduceCallByValue(t.left, composeAppR(cb, t.right))
    if (l.type === Fun) {
      let r = reduceCallByValue(t.right, composeAppL(cb, l))
      return reduceApplicative(apply(l, r, cb), cb)
    } else {
      l = reduceApplicative(l, composeAppR(cb, t.right))
      let r = reduceApplicative(t.right, composeAppL(cb, l))
      return App(l, r)
    }
  case Def:
    cb(markStep('def', t, t.term))
    return reduceApplicative(t.term, cb)
  }
}

let apply = (fun, subst, cb) => {
  let renameCb = composeFun(composeAppR(cb, subst), fun.param)
  let renamedBody = renameForSubstitution(fun.body, fun.param, subst, renameCb)
  let renamed = App(Fun(fun.param, renamedBody), subst)
  let applied = applySubstitution(renamedBody, fun.param, subst)
  cb(markStep('beta', renamed, applied))
  return applied
}

// Applies the substitution T[x := S]
// I.e., substitutes the variable x for the term S in the term T.
let substitute = (t, x, s) => {
  switch (t.type) {
  case Var:
    // x[x := S] = S
    // y[x := S] = y
    return t.name === x ? s : t
  case Fun:
    // (λx.E)[x := S] = λx.E
    // λx creates a new context for x so no further substitution is needed.
    if (t.param === x) return t
    // (λy.E)[x := S] with x != y
    // If y is free in S and x is free in E, then must α-convert λy.E to avoid
    // name conflicts.
    if (freeIn(t.param, s) && freeIn(x, t.body)) {
      // (λy.E)[x := S] = λy'.(E[y := y'][x := S])
      let newVarName = renameVar(t.param, t.body, s)
      let renamedBody = applySubstitution(t.body, t.param, Var(newVarName))
      return Fun(newVarName, substitute(renamedBody, x, s))
    } else {
      // (λy.E)[x := S] = λy.(E[x := S])
      return Fun(t.param, substitute(t.body, x, s))
    }
  case App:
    // (U V)[x := S] = (U[x := S]) (V[x := S])
    return App(substitute(t.left, x, s), substitute(t.right, x, s))
  case Def:
    return t
  }
}

// Performs the α-conversions necessary for the substitution T[x := S], but does
// not perform the substitution itself.
// Records the α-conversions by calling cb.
let renameForSubstitution = (t, x, s, cb) => {
  switch (t.type) {
  case Var:
  case Def:
    return t
  case Fun:
    if (t.param === x) return t
    if (freeIn(t.param, s) && freeIn(x, t.body)) {
      let newVarName = renameVar(t.param, t.body, s)
      let renamedBody = applySubstitution(t.body, t.param, Var(newVarName))
      cb(markStep('alpha', t, t = Fun(newVarName, renamedBody)))
    }
    let body = renameForSubstitution(t.body, x, s, composeFun(cb, t.param))
    return Fun(t.param, body)
  case App:
    let l = renameForSubstitution(t.left, x, s, composeAppR(cb, t.right))
    let r = renameForSubstitution(t.right, x, s, composeAppL(cb, l))
    return App(l, r)
  }
}

// Applies the substitution T[x := S] directly, without doing α-conversions.
let applySubstitution = (t, x, s) => {
  switch (t.type) {
  case Var:
    return t.name === x ? s : t
  case Fun:
    return t.param === x
      ? t
      : Fun(t.param, applySubstitution(t.body, x, s))
  case App:
    let l = applySubstitution(t.left, x, s)
    let r = applySubstitution(t.right, x, s)
    return App(l, r)
  case Def:
    return t
  }
}

// Renames a variable to avoid naming conflicts case doing: a substitution.
let renameVar = (oldName, t, s) => {
  // Split the name into base and number part.
  let base = oldName.replace(/\d+$/, '')
  let match = oldName.match(/\d+$/)
  let n = match ? parseInt(match[0]) : 0

  while (true) {
    n++
    let newName = base + n
    let isValid =
      // Avoid name collisions with substitution term.
      !freeIn(newName, s) &&
      // Avoid name collisions with free variables in body.
      !freeIn(newName, t) &&
      // Avoid name collisions with inner functions.
      !varRenameCollides(t, oldName, newName)
    if (isValid)
      return newName
  }
}

// Whether the variable x is free in the term t.
let freeIn = (x, t) => {
  switch (t.type) {
  case Var:
    return t.name === x
  case Fun:
    return t.param !== x && freeIn(x, t.body)
  case App:
    return freeIn(x, t.left) || freeIn(x, t.right)
  case Def:
    // Definitions don't have free variables.
    return false
  }
}

// Whether a variable rename collides in a given term. That is, if changing the
// occurrences of oldName with newName in t would make it change t's meaning
// (i.e. not be α-equivalent).
let varRenameCollides = (t, oldName, newName) => {
  switch (t.type) {
  case Var:
  case Def:
    return false
  case Fun:
    // A variable rename collides with this function if the old variable
    // was free in the function and the new name for the variable is the
    // same as the param of the function, thus changing old free variable
    // binding.
    return t.param === newName && freeIn(oldName, t) ||
      // Or if the renaming collides in the body of the function.
      varRenameCollides(t.body, oldName, newName)
  case App:
    return varRenameCollides(t.left, oldName, newName) ||
      varRenameCollides(t.right, oldName, newName)
  }
}

let markStep = (type, before, after) =>
  extend({}, after, {step: {type, before}})

let find = (t, fn) => {
  if (fn(t))
    return t
  switch (t.type) {
  case Var:
  case Def:
    return
  case Fun:
    return find(t.body, fn)
  case App:
    return find(t.left, fn) || find(t.right, fn)
  }
}

let replace = (t, from, to) => {
  if (t === from)
    return to
  switch (t.type) {
  case Var:
  case Def:
    return t
  case Fun:
    let body = replace(t.body, from, to)
    return t.body === body ? t : Fun(t.param, body)
  case App:
    let l = replace(t.left, from, to)
    if (t.left !== l) return App(l, t.right)
    let r = replace(t.right, from, to)
    return t.right === r ? t : App(l, r)
  }
}

let expandStep = (t, options = {}) => {
  let stepTerm = find(t, subT => subT.step)
  let type = stepTerm.step.type
  let before = stepTerm.step.before
  let after = stepTerm

  let highlightFormer = options.highlightFormerTerm || identity
  let highlightSubst = options.highlightSubstitutionTerm || identity
  let highlightStep = options.highlightStep || identity

  switch (type) {
  case 'alpha':
    before = highlightFunctionVar(before.body, before.param, highlightFormer)
    after = highlightFunctionVar(after.body, after.param, highlightSubst)
    break
  case 'beta':
    let fun = before.left
    let hs = highlight(before.right, highlightSubst)
    let ha = highlightFunctionVar(fun.body, fun.param, highlightFormer)
    before = App(ha, hs)
    after = substitute(fun.body, fun.param, hs)
    break
  case 'def':
    before = highlight(before, highlightFormer)
    after = highlight(after, highlightSubst)
  }

  before = highlight(before, highlightStep)
  after = highlight(after, highlightStep)

  before = termStr(replace(t, stepTerm, before))
  after = termStr(replace(t, stepTerm, after))

  return {type, before, after}
}

let alphaEq = (t1, t2) => {
  if (t1.type === Def) return alphaEq(t1.term, t2)
  if (t2.type === Def) return alphaEq(t1, t2.term)
  if (t1.type !== t2.type) return false
  switch (t1.type) {
  case Var:
    return t1.name === t2.name
  case Fun:
    if (t1.param === t2.param)
      return alphaEq(t1.body, t2.body)
    else
      return alphaEq(t1.body, substitute(t2.body, t2.param, Var(t1.param)))
  case App:
    return alphaEq(t1.left, t2.left) && alphaEq(t1.right, t2.right)
  }
}

let findSynonyms = (term, defs) => {
  let synonyms = []
  for (let name in defs)
    if (alphaEq(term, defs[name]))
      synonyms.push(name)
  return synonyms
}

let reduceFunctions = {
  normal: reduceNormal,
  applicative: reduceApplicative,
  cbn: reduceCallByName,
  cbv: reduceCallByValue,
}

// Reduces a term up to its normal form.
let reduceTerm = timed('reduce', (term, defs,
    {maxSteps = 100, strategy = 'normal'} = {}) => {
  let reduce = reduceFunctions[strategy]
  let enough = {}
  let steps = []
  let terminates = false
  try {
    reduce(term, t => {
      if (steps.length >= maxSteps) throw enough
      steps.push(t)
    })
    terminates = true
  } catch (e) {
    if (e !== enough) throw e
    terminates = false
  }

  let last = steps[steps.length - 1] || term
  let finalSynonyms = findSynonyms(last, defs)
  let initial = termStr(term)
  let final = termStr(last)
  let totalSteps = steps.length
  let renderStep = (i, options) =>
    expandStep(steps[i], options)
  return {initial, final, finalSynonyms, terminates, totalSteps, renderStep}
})

// Reduce a program and return with the reduction for each term in the program.
let reduceProgram = (program, options = {}) => {
  let {terms, defs} = parse(program)
  return terms.map(term => reduceTerm(term, defs, options))
}

module.exports = {
  Var, Fun, App, Def,
  parse,
  termStr,
  reduceProgram,
}
<|endoftext|>


<commit_before>define([
    'uijet_dir/uijet',
    'widgets/Overlay',
    'project_widgets/LegendItem',
    'project_widgets/Select',
    'project_mixins/Delayed',
    'controllers/LegendsList'
], function (uijet) {

    function chartMode () {
        // reset the state of selected legend item
        this.current_index = null;
        this.$element.removeClass('picking');
        this.picking = false;
        this.resource.each(function (model) {
            model.set('disabled', false);
        });
    }

    function positionNormalizationMenu () {
        var height = this.$wrapper[0].offsetHeight,
            container = uijet.$element.find('#legend_controls')[0],
            selector = uijet.$element.find('#normalization_selector')[0],
            container_h = container.offsetHeight,
            top = uijet.utils.getOffsetOf(selector, container).y;
        this.$wrapper.css('top', (container_h - top < height ? -height : 44) + 'px');
    }

    uijet.Factory('LegendItem', {
        type    : 'LegendItem',
        config  : {
            mixins          : ['Templated', 'Translated'],
            template_name   : 'legend_item',
            dont_fetch      : true,
            data_events     : {
                'change:state'      : function (model, state) {
                    var count = state.selected.length;
                    this.$element.find('.selected_nodes_count').text(count);
                    uijet.publish('selected_nodes_count.updated', count);
                },
                'change:muni'       : function (model, value) {
                    this.$element.find('.entity').text(value.get('name'));
                },
                'change:color'      : function (model, color) {
                    this.setColor(color);
                },
                'change:title'      : function (model, value) {
                    uijet.publish('legend_item_title.updated', {
                        id      : model.id,
                        title   : value
                    });
                },
                'change:disabled'   : function (model, value) {
                    value ? this.disable() : this.enable();
                }
            },
            signals         : {
                post_init   : 'wake'
            },
            app_events      : {
                'add_legend.clicked': function () {
                    this.resource.set('disabled', true);
                }
            }
        }
    })
    .Factory('LegendOverlay', {
        type    : 'Overlay',
        config  : {
            app_events  : {
                'add_legend.clicked'        : 'wake',
                'add_legend_cancel.clicked' : 'sleep',
                'entities_list.selected'    : 'sleep'
            }
        }
    });

    return [{
        type    : 'Button',
        config  : {
            element     : '#add_legend',
            signals     : {
                pre_click       : 'disable',
                pre_wake        : 'awaking',
                post_appear     : function () { this.$element.removeClass('hide'); },
                post_disappear  : function () { this.$element.addClass('hide'); }
            },
            app_events  : {
                'entities_list.selected'    : 'enable',
                'add_legend_cancel.clicked' : 'enable',
                'picker_done.clicked'       : 'wake',
                'nodes_picker.awake'        : 'sleep',
                'welcome'                   : 'wake'
            }
        }
    }, {
        type    : 'Button',
        config  : {
            element     : '#add_legend_cancel',
            dont_wake   : true,
            signals     : {
                pre_click   : 'sleep'
            },
            app_events  : {
                'add_legend.clicked'    : 'wake',
                'entities_list.selected': 'sleep'
            }
        }
    }, {
        type    : 'Pane',
        config  : {
            element     : '#legends_list',
            mixins      : ['Scrolled'],
            adapters    : ['LegendsList', 'jqWheelScroll'],
            resource    : 'LegendItems',
            data_events : {
                remove  : function () { this.resource.setColors(); },
                add     : function () { this.resource.setColors(); },
                reset   : 'resetItems'
            },
            signals     : {
                post_init   : 'createOverlay'
            },
            app_events  : {
                chart_colors                : function (colors) {
                    this.resource.colors = colors;
                },
                'app.resize'                : 'sizeAndScroll',
                'legends_list.duplicate'    : 'addItem+',
                'legends_list.selected'     : 'selectItem+',
                'legends_list.delete'       : 'removeItem+',
                'entities_list.selected'    : function (muni_id) {
                    this.addItem()
                        .setEntity(muni_id)
                        .updateState();
                    uijet.utils.requestAnimFrame(
                        this.scrollTo.bind(this, this.$element.find('.legend_item:not(.disabled)'))
                    );
                },
                'nodes_list.selection'      : 'updateSelection+',
                'picker_done.clicked'       : chartMode,
                'chart_reset'               : chartMode,
                'add_legend_cancel.clicked' : chartMode,
                'legend_item_added'         : function () {
                    this.sizeAndScroll();
                    uijet.utils.requestAnimFrame(
                        this.scrollTo.bind(this, this.$element.find('.legend_item:last-child'))
                    );
                },
                'nodes_picker.awake'        : function () {
                    this.$element.addClass('picking');
                    this.picking = true;
                    this.sizeAndScroll();
                },
                'add_legend.awaking'        : function () {
                    this.$element.removeClass('picking');
                    this.picking = false;
                    this.sizeAndScroll();
                },
                'amount_type.updated'       : function (type) {
                    this.resource.at(this.current_index).set('amount_type', type);
                }
            }
        }
    }, {
        type    : 'Select',
        config  : {
            element     : '#normalization_selector',
            menu        : {
                element         : '#normalization_menu',
                float_position  : 'top:44px;left:0px',
                signals         : {
                    post_wake   : 'awake',
                    post_sleep  : 'asleep'
                },
                app_events      : {
                    'app.resize'            : positionNormalizationMenu,
                    'legend_item_added'     : positionNormalizationMenu,
                    'legend_item_removed'   : positionNormalizationMenu
                }
            },
            app_events  : {
                'normalization_menu.awake'  : 'activate',
                'normalization_menu.asleep' : 'deactivate'
            }
        }
    }];

});
<commit_msg>Show/hide normalization selector when have/don't have items in legend<commit_after>define([
    'uijet_dir/uijet',
    'widgets/Overlay',
    'project_widgets/LegendItem',
    'project_widgets/Select',
    'project_mixins/Delayed',
    'controllers/LegendsList'
], function (uijet) {

    function chartMode () {
        // reset the state of selected legend item
        this.current_index = null;
        this.$element.removeClass('picking');
        this.picking = false;
        this.resource.each(function (model) {
            model.set('disabled', false);
        });
    }

    function positionNormalizationMenu () {
        var height = this.$wrapper[0].offsetHeight,
            container = uijet.$element.find('#legend_controls')[0],
            selector = uijet.$element.find('#normalization_selector')[0],
            container_h = container.offsetHeight,
            top = uijet.utils.getOffsetOf(selector, container).y;
        this.$wrapper.css('top', (container_h - top < height ? -height : 44) + 'px');
    }

    uijet.Factory('LegendItem', {
        type    : 'LegendItem',
        config  : {
            mixins          : ['Templated', 'Translated'],
            template_name   : 'legend_item',
            dont_fetch      : true,
            data_events     : {
                'change:state'      : function (model, state) {
                    var count = state.selected.length;
                    this.$element.find('.selected_nodes_count').text(count);
                    uijet.publish('selected_nodes_count.updated', count);
                },
                'change:muni'       : function (model, value) {
                    this.$element.find('.entity').text(value.get('name'));
                },
                'change:color'      : function (model, color) {
                    this.setColor(color);
                },
                'change:title'      : function (model, value) {
                    uijet.publish('legend_item_title.updated', {
                        id      : model.id,
                        title   : value
                    });
                },
                'change:disabled'   : function (model, value) {
                    value ? this.disable() : this.enable();
                }
            },
            signals         : {
                post_init   : 'wake'
            },
            app_events      : {
                'add_legend.clicked': function () {
                    this.resource.set('disabled', true);
                }
            }
        }
    })
    .Factory('LegendOverlay', {
        type    : 'Overlay',
        config  : {
            app_events  : {
                'add_legend.clicked'        : 'wake',
                'add_legend_cancel.clicked' : 'sleep',
                'entities_list.selected'    : 'sleep'
            }
        }
    });

    return [{
        type    : 'Button',
        config  : {
            element     : '#add_legend',
            signals     : {
                pre_click       : 'disable',
                pre_wake        : 'awaking',
                post_appear     : function () { this.$element.removeClass('hide'); },
                post_disappear  : function () { this.$element.addClass('hide'); }
            },
            app_events  : {
                'entities_list.selected'    : 'enable',
                'add_legend_cancel.clicked' : 'enable',
                'picker_done.clicked'       : 'wake',
                'nodes_picker.awake'        : 'sleep',
                'welcome'                   : 'wake'
            }
        }
    }, {
        type    : 'Button',
        config  : {
            element     : '#add_legend_cancel',
            dont_wake   : true,
            signals     : {
                pre_click   : 'sleep'
            },
            app_events  : {
                'add_legend.clicked'    : 'wake',
                'entities_list.selected': 'sleep'
            }
        }
    }, {
        type    : 'Pane',
        config  : {
            element     : '#legends_list',
            mixins      : ['Scrolled'],
            adapters    : ['LegendsList', 'jqWheelScroll'],
            resource    : 'LegendItems',
            data_events : {
                remove  : function () { this.resource.setColors(); },
                add     : function () { this.resource.setColors(); },
                reset   : 'resetItems'
            },
            signals     : {
                post_init   : 'createOverlay'
            },
            app_events  : {
                chart_colors                : function (colors) {
                    this.resource.colors = colors;
                },
                'app.resize'                : 'sizeAndScroll',
                'legends_list.duplicate'    : 'addItem+',
                'legends_list.selected'     : 'selectItem+',
                'legends_list.delete'       : 'removeItem+',
                'entities_list.selected'    : function (muni_id) {
                    this.addItem()
                        .setEntity(muni_id)
                        .updateState();
                    uijet.utils.requestAnimFrame(
                        this.scrollTo.bind(this, this.$element.find('.legend_item:not(.disabled)'))
                    );
                },
                'nodes_list.selection'      : 'updateSelection+',
                'picker_done.clicked'       : chartMode,
                'chart_reset'               : chartMode,
                'add_legend_cancel.clicked' : chartMode,
                'legend_item_added'         : function () {
                    this.sizeAndScroll();
                    uijet.utils.requestAnimFrame(
                        this.scrollTo.bind(this, this.$element.find('.legend_item:last-child'))
                    );
                },
                'nodes_picker.awake'        : function () {
                    this.$element.addClass('picking');
                    this.picking = true;
                    this.sizeAndScroll();
                },
                'add_legend.awaking'        : function () {
                    this.$element.removeClass('picking');
                    this.picking = false;
                    this.sizeAndScroll();
                },
                'amount_type.updated'       : function (type) {
                    this.resource.at(this.current_index).set('amount_type', type);
                }
            }
        }
    }, {
        type    : 'Select',
        config  : {
            element     : '#normalization_selector',
            dont_wake   : true,
            menu        : {
                element         : '#normalization_menu',
                float_position  : 'top:44px;left:0px',
                signals         : {
                    post_wake   : 'awake',
                    post_sleep  : 'asleep'
                },
                app_events      : {
                    'app.resize'            : positionNormalizationMenu,
                    'legend_item_added'     : positionNormalizationMenu,
                    'legend_item_removed'   : positionNormalizationMenu
                }
            },
            app_events  : {
                legend_item_removed         : function () {
                    if ( ! uijet.Resource('LegendItems').length )
                        this.sleep();
                },
                legend_item_added           : function () {
                    if ( uijet.Resource('LegendItems').length === 1 )
                        this.wake();
                },
                'normalization_menu.awake'  : 'activate',
                'normalization_menu.asleep' : 'deactivate'
            }
        }
    }];

});
<|endoftext|>


<commit_before>/**
 * NTFS.COLLATION: Collation Rules // u32
 * @type {Object}
 */
module.exports = {
  // Collate by binary compare where
  // the first byte is most significant
  BINARY = 0x00000000,
  // Collate file names as Unicode strings
  FILE_NAME = 0x01000000,
  UNICODE_STRING = 0x02000000,
  // Collate Unicode strings by comparing their binary Unicode values,
  // except that when a character can be uppercased,
  // the upper case value collates before the lower case one.
  NTOFS_ULONG = 0x10000000,
  NTOFS_SID = 0x11000000,
  NTOFS_SECURITY_HASH = 0x12000000,
  NTOFS_ULONGS = 0x13000000,
}
<commit_msg>Updated enum/collation-rules: Fixed SyntaxError<commit_after>/**
 * NTFS.COLLATION: Collation Rules // u32
 * @type {Object}
 */
module.exports = {
  // Collate by binary compare where
  // the first byte is most significant
  BINARY: 0x00000000,
  // Collate file names as Unicode strings
  FILE_NAME: 0x01000000,
  UNICODE_STRING: 0x02000000,
  // Collate Unicode strings by comparing their binary Unicode values,
  // except that when a character can be uppercased,
  // the upper case value collates before the lower case one.
  NTOFS_ULONG: 0x10000000,
  NTOFS_SID: 0x11000000,
  NTOFS_SECURITY_HASH: 0x12000000,
  NTOFS_ULONGS: 0x13000000,
}
<|endoftext|>


<commit_before>// @flow
const merge = require('lodash').merge;
const chalk = require('chalk');
const knex = require('knex');
const log = require('./logger').log;
const clone = require('lodash').clone;

type ConnectionOptsType = {
  debug: ?any,
  connection: string,
  pool?: {
    min: number,
    max: number,
  }
};

const connectionOptionDefaults = {
  debug: false,
  client: 'pg',
  pool: {
    min: 2,
    max: process.env.DB_CONNECTION_POOL_MAX || 10,
  },
};

const db = {};
const enableSQLLogging = function enableSQLLogging(knexInstance: Function): void {
  const runningQueries = {};
  knexInstance.on('query', (query) => {
    runningQueries[query.__knexQueryUid] = Date.now();
  });

  knexInstance.on('query-response', function(response, query, builder) {
    let totalTimeInMS = chalk.green.bold('unknown execution time');

    if (runningQueries[query.__knexQueryUid]) {
      totalTimeInMS = chalk.green.bold(`${Date.now() - runningQueries[query.__knexQueryUid]}ms`);
      delete runningQueries[query.__knexQueryUid];
    }

    if (query.sql && query.bindings && query.bindings.length) {
      const oldSql = clone(query.sql);
      const bindings = clone(query.bindings);
      const sql = oldSql.replace(/\$\d+/gi, (x) => {
        return knexInstance.raw('?', [bindings.shift()]);
      });

      log(chalk.magenta.bold(sql), totalTimeInMS);
    } else if (query.sql) {
      log(chalk.magenta.bold(query.sql), totalTimeInMS);
    }
  });
};

module.exports = {
  enableSQLLogging,
  db,
  addDBConnection(connectionName: string, connOpts: ConnectionOptsType): Function {
    if (db[connectionName]) {
      log(chalk.yellow.bold(`Tried adding db connection, ${connectionName}, but it was already set`));
      return db[connectionName];
    }

    const knexInstance = knex(merge({}, connectionOptionDefaults, connOpts));

    if (process.env.KNEX_DEBUG) {
      enableSQLLogging(knexInstance);
    }

    db[connectionName] = knexInstance;

    return knexInstance;
  },
};
<commit_msg>Add database connection name to logs<commit_after>// @flow
const merge = require('lodash').merge;
const chalk = require('chalk');
const knex = require('knex');
const log = require('./logger').log;
const clone = require('lodash').clone;

type ConnectionOptsType = {
  debug: ?any,
  connection: string,
  pool?: {
    min: number,
    max: number,
  }
};

const connectionOptionDefaults = {
  debug: false,
  client: 'pg',
  pool: {
    min: 2,
    max: process.env.DB_CONNECTION_POOL_MAX || 10,
  },
};

const db = {};
const enableSQLLogging = function enableSQLLogging(knexInstance: Function, connectionName: string): void {
  const runningQueries = {};
  knexInstance.on('query', (query) => {
    runningQueries[query.__knexQueryUid] = Date.now();
  });

  knexInstance.on('query-response', function(response, query, builder) {
    let totalTimeInMS = chalk.green.bold('unknown execution time');

    if (runningQueries[query.__knexQueryUid]) {
      totalTimeInMS = chalk.green.bold(`${Date.now() - runningQueries[query.__knexQueryUid]}ms`);
      delete runningQueries[query.__knexQueryUid];
    }

    if (query.sql && query.bindings && query.bindings.length) {
      const oldSql = clone(query.sql);
      const bindings = clone(query.bindings);
      const sql = oldSql.replace(/\$\d+/gi, (x) => {
        return knexInstance.raw('?', [bindings.shift()]);
      });

      log(chalk.blue(`[${connectionName}]`), chalk.magenta.bold(sql), totalTimeInMS);
    } else if (query.sql) {
      log(chalk.blue(`[${connectionName}]`), chalk.magenta.bold(query.sql), totalTimeInMS);
    }
  });
};

module.exports = {
  enableSQLLogging,
  db,
  addDBConnection(connectionName: string, connOpts: ConnectionOptsType): Function {
    if (db[connectionName]) {
      log(chalk.yellow.bold(`Tried adding db connection, ${connectionName}, but it was already set`));
      return db[connectionName];
    }

    const knexInstance = knex(merge({}, connectionOptionDefaults, connOpts));

    if (process.env.KNEX_DEBUG) {
      enableSQLLogging(knexInstance, connectionName);
    }

    db[connectionName] = knexInstance;

    return knexInstance;
  },
};
<|endoftext|>