Source: singletons/workspace.js

Syntree.config_maps.workspace = {};
Syntree.config_maps.workspace.accept_unmapped_config = false;
Syntree.config_maps.workspace.map = {
    /**
     * Is the tutorial enabled?
     *
     * @type {boolean}
     *
     * @see Syntree.Tutorial
     *
     * @memberof Syntree.Workspace
     */
    tutorial_enabled: {
        require: 'boolean',
        default_value: false,
    },
    /**
     * Is uploading enabled?
     *
     * @type {boolean}
     *
     * @see Syntree.Tutorial
     *
     * @memberof Syntree.Workspace
     */
    upload_enabled: {
        require: 'boolean',
        default_value: true,
    },
    /**
     * Path to a PHP script for exporting a tree.
     * Script should return a download link for an export file on success, or false on failure.
     *
     * @type {string}
     *
     * @see Syntree.Workspace._eventExprt
     *
     * @memberof Syntree.Workspace
     */
    export_tree_script: {
        require: 'string',
        default_value: '#undefined',
    },
    /**
     * Is focus checking enabled?
     * Focus checking is for if the app is embedded within a larger page.
     * It prevents confusion about whether or not the app has focus.
     *
     * @type {boolean}
     *
     * @memberof Syntree.Workspace
     */
    focus_checking_enabled: {
        require: 'boolean',
        default_value: false,
    },
};


/**
 * @class
 * @classdesc Workspace is in charge of taking user input, sanitizing it, and sending it to the appropriate lower-level control structure.
 */
Syntree.Workspace = {

    initialize: function(config_matrix) {
        Syntree.Lib.config(config_matrix, this);

        // Make changes to environment based on initialization parameters.

        // Tutorial.
        if (this.tutorial_enabled) {
            $(document)
                .ready(
                    function() {
                        modal_open('tutorial');
                    })
                .on(
                    'click',
                    '.button_modal__begin-tutorial',
                    function() {
                        Syntree.Tutorial.start();
                    })
                .on(
                    'click',
                    '.toolbar_button__tutorial',
                    function() {
                        Syntree.Workspace._eventRewatchTutorial();
                    }
                );
        } else {
            $('.toolbar_button__tutorial').remove();
        }

        // Export functionality.
        if (Syntree.Lib.checkType(this.export_tree_script, 'undefined')) {
            $('.toolbar_button__export').remove();
            $('.modal_export').remove();
        }

        // Upload functionality.
        if (!this.upload_enabled) {
            $('.toolbar_button__upload').remove();
        }



        // Focus checking.
        if (this.focus_checking_enabled) {
            $('#workspace_container').prepend('<div class="focus_check_overlay"></div>');
            $('body').prepend('<div class="focus_check_underlay"></div>');
            $('.focus_check_underlay').hide();
            this.focused = false;
        }

        this._attachEventListeners();

        this.page = new Syntree.Page();
        this.page.addTree(); // Adds the default tree.
        this.page.select(this.page.tree.getRoot());
    },

    /**
     * Attach various event listeners needed for processing user input. This function is a convenience only, used so that [initialize()]{@link Workspace.initialize}) is a bit less cluttered.
     */
    _attachEventListeners: function() {
        // This stuff is to fix dragging, which as default triggers on right click.
        // We want it to NOT trigger on right click, so we maintain Workspace.rightClick
        // for the drag functions to check.
        $(document)
            .on(
                'mousedown',
                function(e) {
                    if (e.which === 3) {
                        Syntree.Workspace.rightClick = true;
                    };
                })
            .on(
                'mouseup',
                function(e) {
                    if (e.which === 3) {
                        Syntree.Workspace.rightClick = false;
                    };
                }
            );
        window.onblur = function() {
            Syntree.Workspace.rightClick = false;
        }
        // --------------------------------------------------------------------------

        // Basic events, funneled to event functions below
        $(document)
            .on(
                'click',
                '.arrow, .arrow-shadow',
                function(e) {
                    Syntree.Workspace._eventArrowClick(e);
                })
            .on(
                'click',
                '.branch, .branch-shadow, .triangle',
                function(e) {
                    Syntree.Workspace._eventBranchClick(e);
                })
            .on(
                'click',
                '.triangle-button',
                function(e) {
                    Syntree.Workspace._eventTriangleButtonClick(e);
                })
            .on(
                'click',
                '.node-label',
                function(e) {
                    Syntree.Workspace._eventNodeClick(e);
                })
            .on(
                'click',
                '.delete_button',
                function() {
                    Syntree.Workspace._eventDel();
                })
            .on(
                'click',
                '#page-background',
                function(e) {
                    Syntree.Workspace._eventBGClick(e);
                })
            .on(
                'dblclick',
                '.node-label, .highlight',
                function() {
                    Syntree.Workspace._eventEnter();
                })
            .on(
                'input',
                '.editor',
                function() {
                    Syntree.Workspace._eventEditorTyping();
                })
            .on(
                'keydown',
                function(e) {
                    if ((Syntree.Workspace.focus_checking_enabled && Syntree.Workspace.focused) ||
                        !Syntree.Workspace.focus_checking_enabled) {

                            if (e.keyCode === 13) { // Enter
                                Syntree.Workspace._eventEnter();
                            } else if (e.keyCode === 37) { // Left arrow key
                                Syntree.Workspace._eventLeft(e);
                            } else if (e.keyCode === 38) { // Up arrow key
                                Syntree.Workspace._eventUp();
                                return false;
                            } else if (e.keyCode === 39) { // Right arrow key
                                Syntree.Workspace._eventRight(e);
                            } else if (e.keyCode === 40) { // Down arrow key
                                Syntree.Workspace._eventDown(e);
                                return false;
                            } else if (e.keyCode === 46) { // Delete key
                                Syntree.Workspace._eventDel();
                            } else if (e.keyCode === 27) { // Esc key
                                Syntree.Workspace._eventEsc();
                            } else if (e.keyCode === 90 && e.ctrlKey) { // CTRL + Z
                                Syntree.Workspace._eventUndo();
                            }
                    }
                }
            );

        // Focus checking.
        if (Syntree.Workspace.focus_checking_enabled) {
            $(document)
                .on(
                    'click',
                    '.focus_check_overlay',
                    function() {
                        Syntree.Workspace._eventFocus();
                    })
                .on(
                    'click',
                    '.focus_check_underlay',
                    function() {
                        Syntree.Workspace._eventUnfocus()
                    }
                );
        }

        // Exporting trees.
        if (Syntree.Lib.checkType(this.export_tree_script, 'string')) {
            $(document)
                .on(
                    'click',
                    '.modal_section__filetype .modal_label',
                    function(e) {
                        Syntree.Workspace._eventFiletypeLabelClick(e);
                    })
                .on(
                    'click',
                    '.button_modal__export',
                    function() {
                        $(this).addClass('loading');
                        var type = $('.modal_section__filetype input:checked').val();
                        if (type === 'bracket-file') {
                            Syntree.Workspace._eventExportBrackets();
                        } else if (type === 'tree-file') {
                            Syntree.Workspace._eventExportTreeFile();
                        } else if (type === 'png') {
                            Syntree.Workspace._eventExportImage();
                        }
                        $(this).removeClass('loading');
                    }
                );
        }

        // Uploading trees.
        if (this.upload_enabled) {
            $(document).on(
                'click',
                '.toolbar_button__upload',
                function() {
                    Syntree.Workspace._eventUpload();
                });
        }

        // Saving trees.
        if (Syntree.Lib.checkType(this.save_tree_script, 'string')) {
            $(document).on(
                'click',
                '.toolbar_button__save',
                function(){
                    Syntree.Workspace._eventSave();
                });
        }

        // Opening trees.
        if (Syntree.Lib.checkType(this.get_trees_script, 'string')) {
            $(document).on(
                'click',
                '.toolbar_button__open',
                function() {
                    $.post(Syntree.Workspace.get_trees_script, {}, function(result) {
                        $('.modal_section__trees').html(result);
                    });
            });
        }
    },

    /**
     * Code to run when a branch's triangle button is clicked.
     *
     * @see Syntree.Branch
     */
    _eventTriangleButtonClick: function(e) {
        var clicked = e.currentTarget;
        var clickedId = $(clicked).attr('id');
        var id = Number(clickedId.substr(clickedId.lastIndexOf('-') + 1, clickedId.length));
        Syntree.Workspace.page.allElements[id].triangleToggle();
    },

    /**
     * Code to run when a branch is clicked.
     *
     * @see Syntree.Branch
     * @see Syntree.Page.select
     */
    _eventBranchClick: function(e) {
        var clicked = e.currentTarget;
        var clickedId = $(clicked).attr('id');
        var id = Number(clickedId.substr(clickedId.lastIndexOf('-') + 1, clickedId.length));
        Syntree.Workspace.page.select(Syntree.Workspace.page.allElements[id]);
    },

    /**
     * Code to run when an arrow is clicked.
     *
     * @see Syntree.Arrow
     * @see Syntree.Page.select
     */
    _eventArrowClick: function(e) {
        var clicked = e.currentTarget;
        var clickedId = $(clicked).attr('id');
        var id = Number(clickedId.substr(clickedId.lastIndexOf('-') + 1, clickedId.length));
        Syntree.Workspace.page.select(Syntree.Workspace.page.allElements[id]);
    },

    /**
     * Code to run when a user requests a tutorial restart/rewatch.
     *
     * @see Syntree.Tutorial
     */
    _eventRewatchTutorial: function() {
        var check;
        if (Syntree.Tutorial.running) {
            check = confirm('Restart tutorial?');
        } else {
            check = confirm('This will delete any work you have open. Start tutorial anyway?');
        }
        if (check) {
            Syntree.Workspace.page.tree.delete();
            Syntree.Workspace.page.addTree();
            Syntree.Workspace.page.select(Syntree.Workspace.page.tree.getRoot());
            Syntree.Tutorial.start();
        }
    },

    /**
     * Code to run when a user attempts to undo an action.
     *
     * @see Syntree.History.undo
     * @see Syntree.Action
     */
    _eventUndo: function() {
        Syntree.History.undo();
    },

    /**
     * Code to run when a Node is clicked.
     *
     * @see Syntree.Node
     */
    _eventNodeClick: function(e) {
        // clickedNode = Syntree.Lib.checkArg(clickedNode, 'svgtextelement');
        var node = Syntree.Workspace.page.allElements[$(e.currentTarget).attr('id').split('-')[1]];
        if (e.ctrlKey) {
            var a = this.page.createMovementArrow(node);
            if (Syntree.Lib.checkType(a, 'arrow')) {
                Syntree.Workspace.page.select(a);
                return false;
            }
        }
        Syntree.Workspace.page.select(node);
    },

    /**
     * Code to run when the user presses the left arrow key.
     *
     * @see Syntree.Page#navigateHorizontal
     */
    _eventLeft: function(e) {
        if (e.shiftKey && e.ctrlKey) {
            this.page.navigateHorizontal('left', true);
        } else {
            if ($(document.activeElement).hasClass('editor') && $(document.activeElement).val() !== '') {
                return;
            }
            this.page.navigateHorizontal('left');
        }
    },

    /**
     * Code to run when the user presses the right arrow key.
     *
     * @see Syntree.Page#navigateHorizontal
     */
    _eventRight: function(e) {
        if (e.shiftKey && e.ctrlKey) {
            this.page.navigateHorizontal('right', true);
        } else {
            if ($(document.activeElement).hasClass('editor') && $(document.activeElement).val() !== '') {
                return;
            }
            this.page.navigateHorizontal('right');
        }
    },

    /**
     * Code to run when the user presses the up arrow key.
     *
     * @see Syntree.Page#navigateUp
     */
    _eventUp: function() {
        this.page.navigateUp();
    },

    /**
     * Code to run when the user presses the down arrow key.
     *
     * @see Syntree.Page#navigateDown
     */
    _eventDown: function(e) {
        this.page.navigateDown();
    },

    /**
     * Code to run when the user tries to delete an [Element]{@link Syntree.Element}.
     */
    _eventDel: function() {
        var selected = Syntree.Workspace.page.getSelected();
        if (Syntree.Lib.checkType(selected, 'node')) {
            if (Syntree.Workspace.page.tree.root === selected) {
                var children = Syntree.Workspace.page.tree.root.getChildren().slice();
                var c = 0;
                while (c < children.length) {
                    var tree = new Syntree.Tree({
                        root: children[c],
                    })
                    Syntree.Workspace.page.deleteTree(tree);
                    c++;
                }
            } else {
                var tree = new Syntree.Tree({
                    root: selected,
                })
                Syntree.Workspace.page.deleteTree(tree);
            }
            Syntree.Workspace.page.deselect();
            if (!Syntree.Lib.checkType(Syntree.Workspace.page.getSelected(), 'node')) {
                Syntree.Workspace.page.select(Syntree.Workspace.page.tree.getRoot());
            }
        } else {
            selected.delete();
        }
    },

    /**
     * Code to run when the user presses the ESC key.
     */
    _eventEsc: function() {
        this.page.nodeEditing('cancel');
    },

    /**
     * Code to run when the user types in a [Node]{@link Syntree.Node} editor.
     */
    _eventEditorTyping: function() {
        this.page.nodeEditing('update');
    },

    /**
     * Code to run when the user clicks a file type option in the export modal.
     * Updates the displayed file suffix.
     */
    _eventFiletypeLabelClick: function(e) {
        var clicked = $(e.currentTarget).children('input');
        if ($(clicked).val() == 'bracket-file') {
            $('.modal_option__fname span').text('.txt');
        } else if ($(clicked).val() == 'tree-file') {
            $('.modal_option__fname span').text('.tree');
        } else if ($(clicked).val() == 'png') {
            $('.modal_option__fname span').text('.png');
        }
    },

    /**
     * Code for exporting the current tree as an image (png).
     */
    _eventExportImage: function() {
        // Get the tree's bounding path, and other initial values.
        var path = Syntree.Workspace.page.tree._getPath();

        var width = path.rightBound - path.leftBound;
        var height = path.bottomBound - path.topBound;

        var offsetX = (-1 * path.leftBound + 25);
        var offsetY = (-1 * path.topBound + 25);

        // Get the SVG data.
        var svgstring = '<svg>' + this.page.getSVGString() + '</svg>';

        // Set up the canvas size.
        $('#export-image-canvas').attr('width', (width + 50));
        $('#export-image-canvas').attr('height', (height + 50));

        // Use canvg to paint the SVG data to the canvas.
        canvg('export-image-canvas', svgstring, {
            offsetX: (-1 * (path.leftBound - 25)),
            offsetY: (-1 * (path.topBound - 25)),
        });

        // Download the image.
        var canvas = document.getElementById('export-image-canvas');
        var imgd = canvas.toDataURL('image/png');
        var link = '<a id="temp-file-download" href="' + imgd + '" download="mytree.png"></a>';
        $('body').append(link);
        $(link)[0].click();
    },

    /**
     * Code for exporting the current tree as a tree file.
     */
    _eventExportTreeFile: function() {
        var fname = $('.modal_option__fname input').val();
        var treestring = this.page.tree.getTreestring();
        if (Syntree.Lib.checkType(this.export_tree_script, 'string')) {
            $.post(this.export_tree_script, {fname: fname, type: 'tree-file', treestring: treestring}, function(link){
                $('body').append(link);
                $('#temp-file-download')[0].click();
                $('#temp-file-download').remove();
            })

        }
    },

    /**
     * Code for exporting the current tree as bracket notation (.txt file).
     */
    _eventExportBrackets: function() {
        $('.loading-icon').show();
        // Get fname
        var fname = $('.modal_option__fname input').val();
        // Get brackets
        var brackets = this.page.tree.getBracketNotation();
        // Post it
        if (Syntree.Lib.checkType(this.export_tree_script, 'string')) {
            $.post(this.export_tree_script, {fname: fname, type: 'bracket-file', brackets: brackets}, function(link) {
                $('body').append(link);
                $('#temp-file-download')[0].click();
                $('#temp-file-download').remove();
                $('.loading-icon').hide();
            });
        }
    },

    /**
     * Code to run when the user presses Enter.
     */
    _eventEnter: function() {
        this.page.nodeEditing('toggle');
    },

    toString: function() {
        return '[object Workspace]';
    },

    _eventFocus: function() {
        $('.focus_check_overlay').hide();
        $('.focus_check_underlay').show();
        window.scrollTo($('#workspace').offset().left,$('#workspace').offset().top);
        // $('body').css('overflow','hidden');
        $('#workspace_container').css('z-index',103);
        this.focused = true;
    },

    _eventUnfocus: function() {
        $('.focus_check_overlay').show();
        $('.focus_check_underlay').hide();
        $('body').css('overflow','initial');
        $('#workspace_container').css('z-index',0);
        this.focused = false;
    },

    // IMPORTANT:
    // All this stuff is on hold. Please ignore it.
    _eventBGClick: function(e) {
        return; //temporary
        var x = e.pageX - $('#workspace').offset().left;
        var y = e.pageY - $('#workspace').offset().top;
        var nearest = this.page.getNearestNode(x,y);
        var newNode = new Syntree.Node(0,0);

        if (Syntree.Lib.checkType(nearest, 'object')) {
            if (nearest.deltaY < -10) {
                if (nearest.deltaX > 0) {
                    nearest.node.addChild(newNode,0);
                } else {
                    nearest.node.addChild(newNode);
                }
            } else {
                var childIndex = nearest.node.getParent().getChildren().indexOf(nearest.node);
                if (nearest.deltaX > 0) {
                    nearest.node.addChild(newNode,childIndex);
                } else {
                    nearest.node.addChild(newNode,childIndex+1);
                }
            }
        }
    },

    _eventUpload: function() {
        var W = this;
        $('body').append('<input type="file" id="temp-choose-file">');
        $('#temp-choose-file').change(function() {
            var f = document.getElementById('temp-choose-file').files[0];
            if (f) {
                var reader = new FileReader();
                reader.readAsText(f, 'UTF-8');
                reader.onload = function (e) {
                    W.page.openTree(e.target.result);
                }
                reader.onerror = function (e) {
                    alert('Unable to read file. Please upload a .tree file.')
                }
            }
            $('#temp-choose-file').remove();
        });
        $('#temp-choose-file').click();
    },

    _eventSave: function() {
        var treestring = this.page.tree.getTreestring();
        var W = this;
        if (Syntree.Lib.checkType(this.save_tree_script, 'string')) {
            $.post(this.save_tree_script,{treestring:treestring,treeid:this.page.tree.getId()},function(result){
                if (Number(result)) {
                    if (!Syntree.Lib.checkType(Syntree.Workspace.page.tree.getId(), 'number')) {
                        W.page.tree.setId(Number(result));
                    }
                    alert('Saved');
                } else {
                    alert('Sorry, there was a problem saving your tree');
                }
            });
        }
    },

}