<commit_before>/****************************************************************************
 * Copyright (C) 2009-2010 GGA Software Services LLC
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU Affero General Public License version 3 as published by the Free
 * Software Foundation and appearing in the file LICENSE.GPL included in
 * the packaging of this file.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

if (!window.Prototype)
	throw new Error("Prototype.js should be loaded first");
if (!window.rnd || !rnd.ReStruct)
	throw new Error("rnd.MolData should be defined prior to loading this file");

// TODO re-factoring needed: client_area parameter is excessive, should be available in render
rnd.Editor = function(render)
{
    this.ui = ui; // TODO ui ref should be passed as a parameter
    this.render = render;

    this._selectionHelper = new rnd.Editor.SelectionHelper(this);
};
rnd.Editor.prototype.selectAll = function() {
    var selection = {};
    for (var map in rnd.ReStruct.maps) {
        selection[map] = ui.render.ctab[map].ikeys();
    }
    this._selectionHelper.setSelection(selection);
};
rnd.Editor.prototype.deselectAll = function() {
    this._selectionHelper.setSelection();
};
rnd.Editor.prototype.hasSelection = function() {
    if ('selection' in this._selectionHelper)
        for (var map in this._selectionHelper.selection)
            if (this._selectionHelper.selection[map].length > 0) return true;
    return false;
};
rnd.Editor.prototype.getSelection = function(explicit) {
    var selection = {};
    if ('selection' in this._selectionHelper) {
        for (var map in this._selectionHelper.selection) {
            selection[map] = this._selectionHelper.selection[map].slice(0);
        }
    }
    if (explicit) {
        var struct = this.render.ctab.molecule;
        // "auto-select" the atoms for the bonds in selection
        if ('bonds' in selection) {
            selection.bonds.each(
                function(bid) {
                    var bond = struct.bonds.get(bid);
                    selection.atoms = selection.atoms || [];
                    if (selection.atoms.indexOf(bond.begin) < 0) selection.atoms.push(bond.begin);
                    if (selection.atoms.indexOf(bond.end) < 0) selection.atoms.push(bond.end);
                },
                this
            );
        }
        // "auto-select" the bonds with both atoms selected
        if ('atoms' in selection && 'bonds' in selection) {
            struct.bonds.each(
                function(bid) {
                    if (!('bonds' in selection) || selection.bonds.indexOf(bid) < 0) {
                        var bond = struct.bonds.get(bid);
                        if (selection.atoms.indexOf(bond.begin) >= 0 && selection.atoms.indexOf(bond.end) >= 0) {
                            selection.bonds = selection.bonds || [];
                            selection.bonds.push(bid);
                        }
                    }
                },
                this
            );
        }
    }
    return selection;
};
rnd.Editor.prototype.toolFor = function(tool) {
    if (tool == 'selector_lasso') {
        return new rnd.Editor.LassoTool(this, 0);
    } else if (tool == 'selector_square') {
        return new rnd.Editor.LassoTool(this, 1);
    } else if (tool == 'selector_fragment') {
        return new rnd.Editor.LassoTool(this, 1, true);
    } else if (tool == 'select_erase') {
        return new rnd.Editor.EraserTool(this, 1); // TODO last selector mode is better
    } else if (tool.startsWith('atom_')) {
        return new rnd.Editor.AtomTool(this, ui.atomLabel(tool)); // TODO should not refer ui directly, re-factoring needed
    } else if (tool.startsWith('bond_')) {
        return new rnd.Editor.BondTool(this, ui.bondType(tool)); // TODO should not refer ui directly, re-factoring needed
    } else if (tool == 'chain') {
        return new rnd.Editor.ChainTool(this);
    } else if (tool.startsWith('template_')) {
        return new rnd.Editor.TemplateTool(this, parseInt(tool.split('_')[1]));
    } else if (tool == 'charge_plus') {
        return new rnd.Editor.ChargeTool(this, 1);
    } else if (tool == 'charge_minus') {
        return new rnd.Editor.ChargeTool(this, -1);
    } else if (tool == 'sgroup') {
        return new rnd.Editor.SGroupTool(this);
    } else if (tool == 'paste') {
        return new rnd.Editor.PasteTool(this);
    } else if (tool == 'reaction_arrow') {
        return new rnd.Editor.ReactionArrowTool(this);
    } else if (tool == 'reaction_plus') {
        return new rnd.Editor.ReactionPlusTool(this);
    } else if (tool == 'reaction_map') {
        return new rnd.Editor.ReactionMapTool(this);
    } else if (tool == 'reaction_unmap') {
        return new rnd.Editor.ReactionUnmapTool(this);
    } else if (tool == 'rgroup_label') {
        return new rnd.Editor.RGroupAtomTool(this);
    } else if (tool == 'rgroup_fragment') {
        return new rnd.Editor.RGroupFragmentTool(this);
    } else if (tool == 'rgroup_attpoints') {
        return new rnd.Editor.APointTool(this);
    } else if (tool.startsWith('transform_rotate')) {
        return new rnd.Editor.RotateTool(this);
    }
    return null;
};


rnd.Editor.SelectionHelper = function(editor) {
    this.editor = editor;
};
rnd.Editor.SelectionHelper.prototype.setSelection = function(selection, add) {
    if (!('selection' in this) || !add) {
        this.selection = {};
        for (var map1 in rnd.ReStruct.maps) this.selection[map1] = []; // TODO it should NOT be mandatory
    }
    if (selection && 'id' in selection && 'map' in selection) {
        (selection[selection.map] = selection[selection.map] || []).push(selection.id);
    }
    if (selection) {
        for (var map2 in this.selection) {
            if (map2 in selection) {
                for (var i = 0; i < selection[map2].length; i++) {
                    if (this.selection[map2].indexOf(selection[map2][i]) < 0) {
                        this.selection[map2].push(selection[map2][i]);
                    }
                }
            }
        }
    }
    this.editor.render.setSelection(this.selection);
    this.editor.render.update();

    ui.updateClipboardButtons(); // TODO notify ui about selection
};
rnd.Editor.SelectionHelper.prototype.isSelected = function(item) {
    var render = this.editor.render;
    var ctab = render.ctab;
    if (item.map == 'frags' || item.map == 'rgroups') {
        var atoms = item.map == 'frags' ? 
            ctab.frags.get(item.id).fragGetAtoms(render, item.id) : 
            ctab.rgroups.get(item.id).getAtoms(render);
        return !Object.isUndefined(this.selection['atoms']) 
            && util.Set.subset(util.Set.fromList(atoms), util.Set.fromList(this.selection['atoms']));
    }
    return 'selection' in this
        && !Object.isUndefined(this.selection[item.map])
        && this.selection[item.map].indexOf(item.id) > -1;
};


rnd.Editor.EditorTool = function(editor) {
    this.editor = editor;
};
rnd.Editor.EditorTool.prototype.processEvent = function(name, event) {
    if (!('touches' in event) || event.touches.length == 1) {
        if (name + '0' in this) 
            return this[name + '0'](event); 
        else if (name in this) 
            return this[name](event);
        console.log('EditorTool.dispatchEvent: event \'' + name + '\' is not handled.');
    } else if ('lastEvent' in this.OnMouseDown0) {
        // here we finish previous MouseDown and MouseMoves with simulated MouseUp
        // before gesture (canvas zoom, scroll, rotate) started
        return this.OnMouseUp0(event);
    }
};
rnd.Editor.EditorTool.prototype.OnMouseOver = function() {};
rnd.Editor.EditorTool.prototype.OnMouseDown = function() {};
rnd.Editor.EditorTool.prototype.OnMouseMove = function() {};
rnd.Editor.EditorTool.prototype.OnMouseUp = function() {};
rnd.Editor.EditorTool.prototype.OnClick = function() {};
rnd.Editor.EditorTool.prototype.OnDblClick = function() {};
rnd.Editor.EditorTool.prototype.OnMouseOut = function() {};
rnd.Editor.EditorTool.prototype.OnKeyPress = function() {};
rnd.Editor.EditorTool.prototype.OnCancel = function() {}; // called when we abandon the tool
rnd.Editor.EditorTool.prototype.OnMouseDown0 = function(event) {
    if (this.editor.ui.hideBlurredControls()) return true; // TODO review

    this.OnMouseDown0.lastEvent = event;
    this.OnMouseMove0.lastEvent = event;

    if ('OnMouseDown' in this) return this.OnMouseDown(event);
};
rnd.Editor.EditorTool.prototype.OnMouseMove0 = function(event) {
    this.OnMouseMove0.lastEvent = event;

    if ('OnMouseMove' in this) return this.OnMouseMove(event);
};
rnd.Editor.EditorTool.prototype.OnMouseUp0 = function(event) {
    // here we suppress event we got when second touch released in guesture
    if (!('lastEvent' in this.OnMouseDown0)) return true;

    if ('lastEvent' in this.OnMouseMove0) {
        // this data is missing for 'touchend' event when last finger is out
        event = Object.clone(event); // pageX & pageY properties are readonly in Opera
        event.pageX = this.OnMouseMove0.lastEvent.pageX;
        event.pageY = this.OnMouseMove0.lastEvent.pageY;
    }

    try {
        if ('OnMouseUp' in this) return this.OnMouseUp(event);
    } finally {
        delete this.OnMouseDown0.lastEvent;
    }
};
rnd.Editor.EditorTool.prototype.OnKeyPress0 = function(event) {
    if (!((event.metaKey && ui.is_osx) || (event.ctrlKey && !ui.is_osx)) && !event.altKey && ('lastEvent' in this.OnMouseMove0)) {
        if (114 == (Prototype.Browser.IE ? event.keyCode : event.which)) { // 'r'
            return rnd.Editor.RGroupAtomTool.prototype.OnMouseUp.call(this, this.OnMouseMove0.lastEvent);
        }
        var ci = this.editor.render.findItem(this.OnMouseMove0.lastEvent);
        if (ci) {
            var labels = {
                Br : 66, Cl : 67, A: 97, C: 99, F : 102, H : 104, I : 105, N : 110, O : 111, P : 112, S : 115
            };
            for (var label in labels) {
                if (labels[label] == (Prototype.Browser.IE ? event.keyCode : event.which)) {
                    ci.label = { label : label };
                    if (ci.map == 'atoms') {
                        this.editor.ui.addUndoAction(ui.Action.fromAtomsAttrs(ci.id, ci.label));
                    } else if (ci.id == -1) {
                        this.editor.ui.addUndoAction(
                            this.editor.ui.Action.fromAtomAddition(
                                this.editor.ui.page2obj(this.OnMouseMove0.lastEvent),
                                ci.label
                            ),
                            true
                        );
                    }
                    this.editor.ui.render.update();
                    return true;
                }
            }
        }
    }
    if ('OnKeyPress' in this) return this.OnKeyPress(event);
    return false;
};
rnd.Editor.EditorTool.prototype._calcAngle = function (pos0, pos1) {
    var v = util.Vec2.diff(pos1, pos0);
    var angle = Math.atan2(v.y, v.x);
    var sign = angle < 0 ? - 1 : 1;
    var floor = Math.floor(Math.abs(angle) / (Math.PI / 12)) * (Math.PI / 12);
    angle = sign * (floor + ((Math.abs(angle) - floor < Math.PI / 24) ? 0 : Math.PI / 12));
    return angle;
};<commit_msg>fixed: KETCHER-381 (Hotkeys for atoms does not work if any atom on the structure is selected)<commit_after>/****************************************************************************
 * Copyright (C) 2009-2010 GGA Software Services LLC
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU Affero General Public License version 3 as published by the Free
 * Software Foundation and appearing in the file LICENSE.GPL included in
 * the packaging of this file.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

if (!window.Prototype)
	throw new Error("Prototype.js should be loaded first");
if (!window.rnd || !rnd.ReStruct)
	throw new Error("rnd.MolData should be defined prior to loading this file");

// TODO re-factoring needed: client_area parameter is excessive, should be available in render
rnd.Editor = function(render)
{
    this.ui = ui; // TODO ui ref should be passed as a parameter
    this.render = render;

    this._selectionHelper = new rnd.Editor.SelectionHelper(this);
};
rnd.Editor.prototype.selectAll = function() {
    var selection = {};
    for (var map in rnd.ReStruct.maps) {
        selection[map] = ui.render.ctab[map].ikeys();
    }
    this._selectionHelper.setSelection(selection);
};
rnd.Editor.prototype.deselectAll = function() {
    this._selectionHelper.setSelection();
};
rnd.Editor.prototype.hasSelection = function() {
    if ('selection' in this._selectionHelper)
        for (var map in this._selectionHelper.selection)
            if (this._selectionHelper.selection[map].length > 0) return true;
    return false;
};
rnd.Editor.prototype.getSelection = function(explicit) {
    var selection = {};
    if ('selection' in this._selectionHelper) {
        for (var map in this._selectionHelper.selection) {
            selection[map] = this._selectionHelper.selection[map].slice(0);
        }
    }
    if (explicit) {
        var struct = this.render.ctab.molecule;
        // "auto-select" the atoms for the bonds in selection
        if ('bonds' in selection) {
            selection.bonds.each(
                function(bid) {
                    var bond = struct.bonds.get(bid);
                    selection.atoms = selection.atoms || [];
                    if (selection.atoms.indexOf(bond.begin) < 0) selection.atoms.push(bond.begin);
                    if (selection.atoms.indexOf(bond.end) < 0) selection.atoms.push(bond.end);
                },
                this
            );
        }
        // "auto-select" the bonds with both atoms selected
        if ('atoms' in selection && 'bonds' in selection) {
            struct.bonds.each(
                function(bid) {
                    if (!('bonds' in selection) || selection.bonds.indexOf(bid) < 0) {
                        var bond = struct.bonds.get(bid);
                        if (selection.atoms.indexOf(bond.begin) >= 0 && selection.atoms.indexOf(bond.end) >= 0) {
                            selection.bonds = selection.bonds || [];
                            selection.bonds.push(bid);
                        }
                    }
                },
                this
            );
        }
    }
    return selection;
};
rnd.Editor.prototype.toolFor = function(tool) {
    if (tool == 'selector_lasso') {
        return new rnd.Editor.LassoTool(this, 0);
    } else if (tool == 'selector_square') {
        return new rnd.Editor.LassoTool(this, 1);
    } else if (tool == 'selector_fragment') {
        return new rnd.Editor.LassoTool(this, 1, true);
    } else if (tool == 'select_erase') {
        return new rnd.Editor.EraserTool(this, 1); // TODO last selector mode is better
    } else if (tool.startsWith('atom_')) {
        return new rnd.Editor.AtomTool(this, ui.atomLabel(tool)); // TODO should not refer ui directly, re-factoring needed
    } else if (tool.startsWith('bond_')) {
        return new rnd.Editor.BondTool(this, ui.bondType(tool)); // TODO should not refer ui directly, re-factoring needed
    } else if (tool == 'chain') {
        return new rnd.Editor.ChainTool(this);
    } else if (tool.startsWith('template_')) {
        return new rnd.Editor.TemplateTool(this, parseInt(tool.split('_')[1]));
    } else if (tool == 'charge_plus') {
        return new rnd.Editor.ChargeTool(this, 1);
    } else if (tool == 'charge_minus') {
        return new rnd.Editor.ChargeTool(this, -1);
    } else if (tool == 'sgroup') {
        return new rnd.Editor.SGroupTool(this);
    } else if (tool == 'paste') {
        return new rnd.Editor.PasteTool(this);
    } else if (tool == 'reaction_arrow') {
        return new rnd.Editor.ReactionArrowTool(this);
    } else if (tool == 'reaction_plus') {
        return new rnd.Editor.ReactionPlusTool(this);
    } else if (tool == 'reaction_map') {
        return new rnd.Editor.ReactionMapTool(this);
    } else if (tool == 'reaction_unmap') {
        return new rnd.Editor.ReactionUnmapTool(this);
    } else if (tool == 'rgroup_label') {
        return new rnd.Editor.RGroupAtomTool(this);
    } else if (tool == 'rgroup_fragment') {
        return new rnd.Editor.RGroupFragmentTool(this);
    } else if (tool == 'rgroup_attpoints') {
        return new rnd.Editor.APointTool(this);
    } else if (tool.startsWith('transform_rotate')) {
        return new rnd.Editor.RotateTool(this);
    }
    return null;
};


rnd.Editor.SelectionHelper = function(editor) {
    this.editor = editor;
};
rnd.Editor.SelectionHelper.prototype.setSelection = function(selection, add) {
    if (!('selection' in this) || !add) {
        this.selection = {};
        for (var map1 in rnd.ReStruct.maps) this.selection[map1] = []; // TODO it should NOT be mandatory
    }
    if (selection && 'id' in selection && 'map' in selection) {
        (selection[selection.map] = selection[selection.map] || []).push(selection.id);
    }
    if (selection) {
        for (var map2 in this.selection) {
            if (map2 in selection) {
                for (var i = 0; i < selection[map2].length; i++) {
                    if (this.selection[map2].indexOf(selection[map2][i]) < 0) {
                        this.selection[map2].push(selection[map2][i]);
                    }
                }
            }
        }
    }
    this.editor.render.setSelection(this.selection);
    this.editor.render.update();

    ui.updateClipboardButtons(); // TODO notify ui about selection
};
rnd.Editor.SelectionHelper.prototype.isSelected = function(item) {
    var render = this.editor.render;
    var ctab = render.ctab;
    if (item.map == 'frags' || item.map == 'rgroups') {
        var atoms = item.map == 'frags' ? 
            ctab.frags.get(item.id).fragGetAtoms(render, item.id) : 
            ctab.rgroups.get(item.id).getAtoms(render);
        return !Object.isUndefined(this.selection['atoms']) 
            && util.Set.subset(util.Set.fromList(atoms), util.Set.fromList(this.selection['atoms']));
    }
    return 'selection' in this
        && !Object.isUndefined(this.selection[item.map])
        && this.selection[item.map].indexOf(item.id) > -1;
};


rnd.Editor.EditorTool = function(editor) {
    this.editor = editor;
};
rnd.Editor.EditorTool.prototype.processEvent = function(name, event) {
    if (!('touches' in event) || event.touches.length == 1) {
        if (name + '0' in this) 
            return this[name + '0'](event); 
        else if (name in this) 
            return this[name](event);
        console.log('EditorTool.dispatchEvent: event \'' + name + '\' is not handled.');
    } else if ('lastEvent' in this.OnMouseDown0) {
        // here we finish previous MouseDown and MouseMoves with simulated MouseUp
        // before gesture (canvas zoom, scroll, rotate) started
        return this.OnMouseUp0(event);
    }
};
rnd.Editor.EditorTool.prototype.OnMouseOver = function() {};
rnd.Editor.EditorTool.prototype.OnMouseDown = function() {};
rnd.Editor.EditorTool.prototype.OnMouseMove = function() {};
rnd.Editor.EditorTool.prototype.OnMouseUp = function() {};
rnd.Editor.EditorTool.prototype.OnClick = function() {};
rnd.Editor.EditorTool.prototype.OnDblClick = function() {};
rnd.Editor.EditorTool.prototype.OnMouseOut = function() {};
rnd.Editor.EditorTool.prototype.OnKeyPress = function() {};
rnd.Editor.EditorTool.prototype.OnCancel = function() {}; // called when we abandon the tool
rnd.Editor.EditorTool.prototype.OnMouseDown0 = function(event) {
    if (this.editor.ui.hideBlurredControls()) return true; // TODO review

    this.OnMouseDown0.lastEvent = event;
    this.OnMouseMove0.lastEvent = event;

    if ('OnMouseDown' in this) return this.OnMouseDown(event);
};
rnd.Editor.EditorTool.prototype.OnMouseMove0 = function(event) {
    this.OnMouseMove0.lastEvent = event;

    if ('OnMouseMove' in this) return this.OnMouseMove(event);
};
rnd.Editor.EditorTool.prototype.OnMouseUp0 = function(event) {
    // here we suppress event we got when second touch released in guesture
    if (!('lastEvent' in this.OnMouseDown0)) return true;

    if ('lastEvent' in this.OnMouseMove0) {
        // this data is missing for 'touchend' event when last finger is out
        event = Object.clone(event); // pageX & pageY properties are readonly in Opera
        event.pageX = this.OnMouseMove0.lastEvent.pageX;
        event.pageY = this.OnMouseMove0.lastEvent.pageY;
    }

    try {
        if ('OnMouseUp' in this) return this.OnMouseUp(event);
    } finally {
        delete this.OnMouseDown0.lastEvent;
    }
};
rnd.Editor.EditorTool.prototype.OnKeyPress0 = function(event) {
    if (!((event.metaKey && ui.is_osx) || (event.ctrlKey && !ui.is_osx)) && !event.altKey && ('lastEvent' in this.OnMouseMove0)) {
        if (114 == (Prototype.Browser.IE ? event.keyCode : event.which)) { // 'r'
            return rnd.Editor.RGroupAtomTool.prototype.OnMouseUp.call(this, this.OnMouseMove0.lastEvent);
        }
        var labels = {
            Br : 66, Cl : 67, A: 97, C: 99, F : 102, H : 104, I : 105, N : 110, O : 111, P : 112, S : 115
        };
        for (var label in labels) {
            if (labels[label] == (Prototype.Browser.IE ? event.keyCode : event.which)) {
                var selection = this.editor.getSelection();
                if (selection && 'atoms' in selection && selection.atoms.length > 0) {
                    this.editor.ui.addUndoAction(
                        this.editor.ui.Action.fromAtomsAttrs(selection.atoms, { label : label }),
                        true
                    );
                    this.editor.ui.render.update();
                    return true;
                } else {
                    var ci = this.editor.render.findItem(this.OnMouseMove0.lastEvent);
                    if (ci) {
                        ci.label = { label : label };
                        if (ci.map == 'atoms') {
                            this.editor.ui.addUndoAction(
                                ui.Action.fromAtomsAttrs(ci.id, ci.label),
                                true
                            );
                        } else if (ci.id == -1) {
                            this.editor.ui.addUndoAction(
                                this.editor.ui.Action.fromAtomAddition(
                                    this.editor.ui.page2obj(this.OnMouseMove0.lastEvent),
                                    ci.label
                                ),
                                true
                            );
                        }
                        this.editor.ui.render.update();
                        return true;
                    }
                }
            }
        }
    }
    if ('OnKeyPress' in this) return this.OnKeyPress(event);
    return false;
};
rnd.Editor.EditorTool.prototype._calcAngle = function (pos0, pos1) {
    var v = util.Vec2.diff(pos1, pos0);
    var angle = Math.atan2(v.y, v.x);
    var sign = angle < 0 ? - 1 : 1;
    var floor = Math.floor(Math.abs(angle) / (Math.PI / 12)) * (Math.PI / 12);
    angle = sign * (floor + ((Math.abs(angle) - floor < Math.PI / 24) ? 0 : Math.PI / 12));
    return angle;
};
{'fraction_non_alphanumeric': 0.1418039347853562, 'fraction_numerical': 0.009268231031722626, 'mean_word_length': 2.2900900900900902, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2041150', 'n_tokens_mistral': 7185, 'n_tokens_neox': 6915, 'n_words': 1640}