<commit_before>// Copyright (c) 2011-2012 UX Productivity. All rights reserved.

function Range(start,end)
{
    this.start = start;
    this.end = end;
}

Range.fromSelection = function()
{
    var selection = window.getSelection();

    if (selection.rangeCount == 0)
        return null;

    var r = selection.getRangeAt(0);

    return new Range(new Position(r.startContainer,r.startOffset),
                     new Position(r.endContainer,r.endOffset));
}

Range.prototype.copy = function()
{
    return new Range(new Position(this.start.node,this.start.offset),
                     new Position(this.end.node,this.end.offset));
}

Range.prototype.selectWholeWords = function()
{
    if ((this.start.node.nodeType == Node.TEXT_NODE) &&
        (this.end.node.nodeType == Node.TEXT_NODE)) {
        if (this.isForwards()) {
            // this.start comes before this.end
            this.start.moveToStartOfWord();
            this.end.moveToEndOfWord();
        }
        else {
            // this.end comes before this.end
            this.start.moveToEndOfWord();
            this.end.moveToStartOfWord();
        }
    }
}

Range.prototype.omitEmptyTextSelection = function()
{
    this.start.moveForwardIfAtEnd();
    this.end.moveBackwardIfAtStart();
}

Range.prototype.isForwards = function()
{
    if (this.start.node == this.end.node)
        return (this.start.offset < this.end.offset);
    else {
        var cmp = this.start.node.compareDocumentPosition(this.end.node);
        return (cmp & (Node.DOCUMENT_POSITION_FOLLOWING | Node.DOCUMENT_POSITION_CONTAINED_BY));

    }
}

Range.prototype.getParagraphNodes = function()
{
    var result = new Array();
    var node = this.start.node;
    while (!isParagraphNode(node))
        node = node.parentNode;
    while (true) {
        if (isParagraphNode(node))
            result.push(node);
        if (node == this.end.node)
            break;
        node = nextNode(node);
    }
    return result;
}

Range.prototype.getInlineNodes = function()
{
    var result = new Array();
    var node = this.start.node;
    while (true) {
        if (isInlineNode(node))
            result.push(node);
        if (node == this.end.node)
            break;
        node = nextNode(node);
    }
    return result;
}

Range.prototype.getAllNodes = function()
{
    var result = new Array();
    var node = this.start.node;
    while (true) {
        result.push(node);
        if (node == this.end.node)
            break;
        node = nextNode(node);
    }
    return result;
}

Range.prototype.setSelection = function()
{
    window.getSelection().setBaseAndExtent(this.start.node,
                                           this.start.offset,
                                           this.end.node,
                                           this.end.offset);
}

Range.prototype.ensureRangeValidHierarchy = function()
{
    var nodes = this.getAllNodes();
    
    var depths = new Array();
    for (var i = 0; i < nodes.length; i++) {
        var depth = getNodeDepth(nodes[i]);
        if (depths[depth] == null) {
            depths[depth] = new Array();
        }
        depths[depth].push(nodes[i]);
    }
    
    for (var depth = 0; depth < depths.length; depth++) {
        var firstDepth = true;
        if (depths[depth] != null) {
            for (var i = 0; i < depths[depth].length; i++) {
                var node = depths[depth][i];
                if (!isInlineNode(node.parentNode) && isWhitespaceTextNode(node)) {
                    node.parentNode.removeChild(node);
                }
                else {
                    ensureValidHierarchy(node,firstDepth);
                }
            }
            firstDepth = false;
        }
    }
}

// Converts the range to a form in which the start node has an offset of 0, and the end node
// has an offset of nodeValue.length or childNodes.length (depending on the node type). This
// simplifies dealing with ranges as it relieves us from having to deal witih offets in element
// nodes.
Range.prototype.convertToOffsetFree = function()
{
    if ((this.start.node.nodeType == Node.ELEMENT_NODE) && (this.start.offset > 0)) {
        this.start.node = this.start.node.childNodes[this.start.offset];
        this.start.offset = 0;
    }

    if ((this.end.node.nodeType == Node.ELEMENT_NODE) &&
        (this.end.offset < this.end.node.childNodes.length)) {
        if (this.end.offset == 0) {
            this.end.node = prevNode(this.end.node);
        }
        else {
            this.end.node = this.end.node.childNodes[this.end.offset-1];
        }

        // Now set the offset to be the start of the node
        if (this.end.node.nodeType == Node.TEXT_NODE) {
            this.end.offset = this.end.node.nodeValue.length;
        }
        else if (this.end.node.nodeType == Node.ELEMENT_NODE) {
            this.end.offset = this.end.node.childNodes.length;
        }
        else
            this.end.offset = 0;
    }
}

Range.prototype.getSelectedNodes = function()
{
    if (!this.isForwards())
        return new Array();

    var result = new Array();

    this.convertToOffsetFree();

    var startNode = this.start.node;
    var endNode = this.end.node;

    // If the end node is contained within the start node, change the start node to the first
    // node in document order that is not an ancestor of the end node
    while (isAncestor(startNode,endNode) &&
           (startNode != endNode) &&
           (startNode.firstChild != null)) {
        startNode = startNode.firstChild;
    }

    if (startNode == endNode) {
        addNode(result,startNode);
        return result;
    }

    // Find common ancestor
    var commonAncestor = null;
    var startAncestor = null;<commit_msg>In Range.getSelectedNodes(), if the range is not already offset free, create a *new* range which is offset free and get the selected nodes of that, rather than modifying the current range.<commit_after>// Copyright (c) 2011-2012 UX Productivity. All rights reserved.

function Range(start,end)
{
    this.start = start;
    this.end = end;
}

Range.fromSelection = function()
{
    var selection = window.getSelection();

    if (selection.rangeCount == 0)
        return null;

    var r = selection.getRangeAt(0);

    return new Range(new Position(r.startContainer,r.startOffset),
                     new Position(r.endContainer,r.endOffset));
}

Range.prototype.copy = function()
{
    return new Range(new Position(this.start.node,this.start.offset),
                     new Position(this.end.node,this.end.offset));
}

Range.prototype.selectWholeWords = function()
{
    if ((this.start.node.nodeType == Node.TEXT_NODE) &&
        (this.end.node.nodeType == Node.TEXT_NODE)) {
        if (this.isForwards()) {
            // this.start comes before this.end
            this.start.moveToStartOfWord();
            this.end.moveToEndOfWord();
        }
        else {
            // this.end comes before this.end
            this.start.moveToEndOfWord();
            this.end.moveToStartOfWord();
        }
    }
}

Range.prototype.omitEmptyTextSelection = function()
{
    this.start.moveForwardIfAtEnd();
    this.end.moveBackwardIfAtStart();
}

Range.prototype.isForwards = function()
{
    if (this.start.node == this.end.node)
        return (this.start.offset < this.end.offset);
    else {
        var cmp = this.start.node.compareDocumentPosition(this.end.node);
        return (cmp & (Node.DOCUMENT_POSITION_FOLLOWING | Node.DOCUMENT_POSITION_CONTAINED_BY));

    }
}

Range.prototype.getParagraphNodes = function()
{
    var result = new Array();
    var node = this.start.node;
    while (!isParagraphNode(node))
        node = node.parentNode;
    while (true) {
        if (isParagraphNode(node))
            result.push(node);
        if (node == this.end.node)
            break;
        node = nextNode(node);
    }
    return result;
}

Range.prototype.getInlineNodes = function()
{
    var result = new Array();
    var node = this.start.node;
    while (true) {
        if (isInlineNode(node))
            result.push(node);
        if (node == this.end.node)
            break;
        node = nextNode(node);
    }
    return result;
}

Range.prototype.getAllNodes = function()
{
    var result = new Array();
    var node = this.start.node;
    while (true) {
        result.push(node);
        if (node == this.end.node)
            break;
        node = nextNode(node);
    }
    return result;
}

Range.prototype.setSelection = function()
{
    window.getSelection().setBaseAndExtent(this.start.node,
                                           this.start.offset,
                                           this.end.node,
                                           this.end.offset);
}

Range.prototype.ensureRangeValidHierarchy = function()
{
    var nodes = this.getAllNodes();
    
    var depths = new Array();
    for (var i = 0; i < nodes.length; i++) {
        var depth = getNodeDepth(nodes[i]);
        if (depths[depth] == null) {
            depths[depth] = new Array();
        }
        depths[depth].push(nodes[i]);
    }
    
    for (var depth = 0; depth < depths.length; depth++) {
        var firstDepth = true;
        if (depths[depth] != null) {
            for (var i = 0; i < depths[depth].length; i++) {
                var node = depths[depth][i];
                if (!isInlineNode(node.parentNode) && isWhitespaceTextNode(node)) {
                    node.parentNode.removeChild(node);
                }
                else {
                    ensureValidHierarchy(node,firstDepth);
                }
            }
            firstDepth = false;
        }
    }
}

// Converts the range to a form in which the start node has an offset of 0, and the end node
// has an offset of nodeValue.length or childNodes.length (depending on the node type). This
// simplifies dealing with ranges as it relieves us from having to deal witih offets in element
// nodes.
Range.prototype.convertToOffsetFree = function()
{
    if ((this.start.node.nodeType == Node.ELEMENT_NODE) && (this.start.offset > 0)) {
        this.start.node = this.start.node.childNodes[this.start.offset];
        this.start.offset = 0;
    }

    if ((this.end.node.nodeType == Node.ELEMENT_NODE) &&
        (this.end.offset < this.end.node.childNodes.length)) {
        if (this.end.offset == 0) {
            this.end.node = prevNode(this.end.node);
        }
        else {
            this.end.node = this.end.node.childNodes[this.end.offset-1];
        }

        // Now set the offset to be the start of the node
        if (this.end.node.nodeType == Node.TEXT_NODE) {
            this.end.offset = this.end.node.nodeValue.length;
        }
        else if (this.end.node.nodeType == Node.ELEMENT_NODE) {
            this.end.offset = this.end.node.childNodes.length;
        }
        else
            this.end.offset = 0;
    }
}

Range.prototype.isOffsetFree = function()
{
    if (this.start.node.nodeType == Node.ELEMENT_NODE)
        return ((this.start.offset == 0) && (this.end.offset == this.end.node.childNodes.length));
    else
        return true;
}

Range.prototype.getSelectedNodes = function()
{
    if (!this.isForwards())
        return new Array();

    if (!this.isOffsetFree()) {
        var copy = this.copy();
        copy.convertToOffsetFree();
        return copy.getSelectedNodes();
    }

    var result = new Array();
    var startNode = this.start.node;
    var endNode = this.end.node;

    // If the end node is contained within the start node, change the start node to the first
    // node in document order that is not an ancestor of the end node
    while (isAncestor(startNode,endNode) &&
           (startNode != endNode) &&
           (startNode.firstChild != null)) {
        startNode = startNode.firstChild;
    }

    if (startNode == endNode) {
        addNode(result,startNode);
        return result;
    }

    // Find common ancestor
    var commonAncestor = null;
    var startAncestor = null;
{'fraction_non_alphanumeric': 0.12189034029453366, 'fraction_numerical': 0.003244862301356186, 'mean_word_length': 2.0445795339412363, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5122390', 'n_tokens_mistral': 3642, 'n_tokens_neox': 3549, 'n_words': 819}