<commit_before>// Session Outliner
// Copyright (C) 2014, <NAME>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

"use strict";

// sdk:
const _ = require("sdk/l10n").get;

const { browserObserver } = require("observer");
const { openBrowser } = require("browser");
const { IDBStorageBackend } = require("storage");

const {
    assert,
    handleException,
    printArgs,
    isEmpty
} = require("utils");

const {
    Position,
    Traverse,
    ModelIndex,
} = require("treemodel");

const {
    SessionNode,
    TextNode,
    SeparatorNode,
    WindowNode,
    ActiveWindowNode,
    TabNode,
    ActiveTabNode,
    TabModel,
    getWindowNode,
    getTopMostTabNode,
    activateWindow,
    activateTab,
    deleteTabNode,
    deleteWindowNode,
    createHomeNode
} = require("tabmodel");

// ****************************************************************************
// TreeView event handlers:

function treeCloseHandler(model, id, withSubNodes) {
    if (!model.hasId(id)) {
        return;
    }

    // replace the active node with a new inactive node
    let node = model.getNodeById(id);
    assert(node, "Node not found.");

    if (withSubNodes) {
        let index = node.getIndex();

        //[ first stage: close windows
        let activeWindowNodes = model.getActiveWindowNodes(node);

        activeWindowNodes.forEach(function(currentNode) {
            model.closeNode(currentNode);
        });
        //]

        //[ second stage: close remaining tabs
        let groupNode = model.getNode(index); // the group node could already be closed
        let activeTabNodes = model.getActiveTabNodes(groupNode); 

        activeTabNodes.forEach(function(currentNode) {
            model.closeNode(currentNode);
        });
        //]

        return;
    } else {
        model.closeNode(node);
    }
}

function treeRemoveHandler(model, id, detach) {
    if (!model.hasId(id)) {
        return;
    }

    let node = model.getNodeById(id);
    assert(node, "Node not found.");

    if (node instanceof WindowNode) {
        deleteWindowNode(node, detach);
    } else if (node instanceof TabNode) {
        deleteTabNode(node, detach);
    } else if (node instanceof SessionNode) {
        console.warn("Attempt to remove session node.");
    } else {
        assert(!node.isContainer(), "Unknown node.");
        model.removeNode(node.getIndex());
    }
}

function treeActivateHandler(model, id) {
    if (!model.hasId(id)) {
        return;
    }

    // the old node will be replaced
    let node = model.getNodeById(id);
    assert(node, "Node not found.");

    if (node instanceof TabNode && !node.isActive()) {
        return activateTab(node);
    } else if (node instanceof WindowNode && !node.isActive()) {
        return activateWindow(node);
    } else if (node instanceof ActiveTabNode) {
        node.focus();
    } else if (node instanceof ActiveWindowNode) {
        node.focus();
    } else {
        console.warn("viewActivateNode: Invalid node type.");
    }
}

// ----------------------------------------------------------------------------

function canMove(node, targetNode, position) {
    let model = node.getModel();
    let result = true;

    if (targetNode instanceof SessionNode) {
        result = result && (position == Position.INSIDE);
    }

    if (!targetNode.isContainer()) {
        result = result && (position != Position.INSIDE);
    }

    if (node instanceof TabNode) {
        let windowNode;

        //[ every tab node has to be inside a window node
        switch (position) {
            case Position.INSIDE:
                windowNode = getWindowNode(targetNode);
                break;
            case Position.AFTER:
            case Position.BEFORE:
                let parent = targetNode.getParent();
                if (parent) {
                    windowNode = getWindowNode(parent);
                }
                break;
        }

        if (!windowNode) {
            result = false;
        }
        //]

        //[ isolate pinned area
        let descendants = model.matchNodes(function(currentNode) {
            return !(currentNode instanceof WindowNode);
        }, {<commit_msg>refactoring<commit_after>// Session Outliner
// Copyright (C) 2014, <NAME>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

"use strict";

// sdk:
const _ = require("sdk/l10n").get;

const { browserObserver } = require("observer");
const { openBrowser } = require("browser");
const { IDBStorageBackend } = require("storage");

const {
    assert,
    handleException,
    printArgs,
    isEmpty
} = require("utils");

const {
    Position,
    Traverse,
    ModelIndex,
} = require("treemodel");

const {
    SessionNode,
    TextNode,
    SeparatorNode,
    WindowNode,
    ActiveWindowNode,
    TabNode,
    ActiveTabNode,
    TabModel,
    getWindowNode,
    getTopMostTabNode,
    activateWindow,
    activateTab,
    deleteTabNode,
    deleteWindowNode,
    createHomeNode
} = require("tabmodel");

// ****************************************************************************
// TreeView event handlers:

function treeCloseHandler(model, id, withSubNodes) {
    if (!model.hasId(id)) {
        return;
    }

    // replace the active node with a new inactive node
    let node = model.getNodeById(id);
    assert(node, "Node not found.");

    if (withSubNodes) {
        let index = node.getIndex();

        //[ first stage: close windows
        let activeWindowNodes = model.getActiveWindowNodes(node);

        activeWindowNodes.forEach(function(currentNode) {
            model.closeNode(currentNode);
        });
        //]

        //[ second stage: close remaining tabs
        let groupNode = model.getNode(index); // the group node could already be closed
        let activeTabNodes = model.getActiveTabNodes(groupNode); 

        activeTabNodes.forEach(function(currentNode) {
            model.closeNode(currentNode);
        });
        //]

        return;
    } else {
        model.closeNode(node);
    }
}

function treeRemoveHandler(model, id, detach) {
    if (!model.hasId(id)) {
        return;
    }

    let node = model.getNodeById(id);
    assert(node, "Node not found.");

    if (node instanceof WindowNode) {
        deleteWindowNode(node, detach);
    } else if (node instanceof TabNode) {
        deleteTabNode(node, detach);
    } else if (node instanceof SessionNode) {
        console.warn("Attempt to remove session node.");
    } else {
        assert(!node.isContainer(), "Unknown node.");
        model.removeNode(node.getIndex());
    }
}

function treeActivateHandler(model, id) {
    if (!model.hasId(id)) {
        return;
    }

    // the old node will be replaced
    let node = model.getNodeById(id);
    assert(node, "Node not found.");

    if (node instanceof TabNode && !node.isActive()) {
        return activateTab(node);
    } else if (node instanceof WindowNode && !node.isActive()) {
        return activateWindow(node);
    } else if (node instanceof ActiveTabNode) {
        node.focus();
    } else if (node instanceof ActiveWindowNode) {
        node.focus();
    } else {
        console.warn("viewActivateNode: Invalid node type.");
    }
}

// ----------------------------------------------------------------------------

function canMove(node, targetNode, position) {
    let model = node.getModel();
    let result = true;

    if (targetNode instanceof SessionNode) {
        result &= (position == Position.INSIDE);
    }

    if (!targetNode.isContainer()) {
        result &= (position != Position.INSIDE);
    }

    if (node instanceof TabNode) {
        let windowNode;

        //[ every tab node has to be inside a window node
        switch (position) {
            case Position.INSIDE:
                windowNode = getWindowNode(targetNode);
                break;
            case Position.AFTER:
            case Position.BEFORE:
                let parent = targetNode.getParent();
                if (parent) {
                    windowNode = getWindowNode(parent);
                }
                break;
        }

        if (!windowNode) {
            result = false;
        }
        //]

        //[ isolate pinned area
        let descendants = model.matchNodes(function(currentNode) {
            return !(currentNode instanceof WindowNode);
        }, {
{'fraction_non_alphanumeric': 0.13766450804261543, 'fraction_numerical': 0.0014622937121370379, 'mean_word_length': 2.2757440985289086, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6151158', 'n_tokens_mistral': 2599, 'n_tokens_neox': 2510, 'n_words': 758}