<commit_before>/*jshint expr:true, onevar:false */

// Fun fact! The YUI Gallery currently doesn't support conditional loading of
// modules based on feature detection like YUI core does, so that's why all the
// legacy compat code is baked in here instead of in a separate conditionally
// loaded module.

/**
Provides the `Range` class, which normalizes Range behavior across browsers.

@module gallery-sm-selection
@submodule gallery-sm-range
**/

/**
Provides a friendly cross-browser Range API similar to the API defined in the
DOM Range specification.

@class Range
@param {window.Range} [range] Native Range object to wrap. If not provided, a
    new Range will be created.
@constructor
**/

var doc = Y.config.doc,
    win = Y.config.win,

    isHTML5 = !!(win && win.Range && doc.createRange),

    ELEMENT_NODE = 1,
    TEXT_NODE = 3;

var Range = isHTML5 ? function (range) {
    this._range = range || doc.createRange();
} : function (range) {
    this._range = range || doc.body.createTextRange();
};

Range.prototype = {
    // -- Public Methods -------------------------------------------------------

    /**
    Returns a new Range object with the same boundary points as this range.

    The returned Range is a copy, not a reference, so modifying it will not
    affect this range (and vice versa).

    @method clone
    @return {Range} New Range object with the same boundary points as this
        range.
    **/
    clone: isHTML5 ? function () {
        return new Y.Range(this._range.cloneRange());
    } : function () {
        throw new Error('Not yet implemented.');
    },

    /**
    Returns a Node instance containing a document fragment with a copy of this
    range's contents.

    Event listeners are not copied.

    Element ids _are_ copied. This could lead to duplicate ids, so be careful.

    Partially selected nodes will include parent tags to ensure that the
    fragment is valid.

    @method cloneContents
    @return {Node} Node instance containing a document fragment with a copy of
        this range's contents.
    **/
    cloneContents: isHTML5 ? function () {
        return Y.one(this._range.cloneContents());
    } : function () {
        throw new Error('Not yet implemented.');
    },

    /**
    Collapses this range by setting the start and end points to the same
    position, thus resulting in an empty range.

    @method collapse
    @param {Object} [options] Options.
        @param {Boolean} [options.toStart=false] If `true`, this range will be
            collapsed by moving the end point to the start point. Otherwise, the
            start point will be moved to the end point.
    @chainable
    **/
    collapse: function (options) {
        this._range.collapse(options && options.toStart);
        return this;
    },

    /**
    Compares the start or end boundary of this range with the start or end
    boundary of another range.

    @method compare
    @param {Range} otherRange Range to compare to.
    @param {Object} [options] Options.

        @param {String} [options.myPoint='start'] Specifies which boundary point
            on this range should be used for the comparison. Valid values are
            'start' to use this range's start point for the comparison, or 'end'
            to use this range's end point.

        @param {String} [options.otherPoint='start'] Specifies which boundary
            point on _otherRange_ should be used for the comparison. Valid
            values are 'start' to use _otherRange_'s start point for the
            comparison, or 'end' to use _otherRange_'s end point.

    @return {Number} -1, 0, or 1, indicating whether this range's boundary is
        respectively before, equal to, or after the other range's boundary.
    **/
    compare: isHTML5 ? function (otherRange, options) {
        // Make sure we're working with a native range, not a YUI range.
        if (otherRange._range) {
            otherRange = otherRange._range;
        }

        var myPoint    = (options && options.myPoint) || 'start',
            otherPoint = (options && options.otherPoint) || 'start';

        var how = win.Range[otherPoint.toUpperCase() + '_TO_' +
                    myPoint.toUpperCase()];

        return this._range.compareBoundaryPoints(how, otherRange);
    } : function () {
        throw new Error('Not yet implemented.');
    },

    /**
    Removes the contents of this range from the DOM.

    @method deleteContents
    @chainable
    **/
    deleteContents: isHTML5 ? function () {
        this._range.deleteContents();
        return this;
    } : function () {
        throw new Error('Not yet implemented.');
    },

    /**
    Gets or sets the node that contains the end point of this range.

    When specifying an _offset_, you may specify either a number or the string
    "before" or "after".

    A numerical offset will position the end point at that offset inside the
    _node_. If _node_ is a text node, the offset will represent a character
    position. If _node_ can contain child nodes, then the offset will represent
    a child index.

    The offset "before" will cause the end point to be placed immediately before
    _node_ (not inside it).

    The offset "after" will cause the end point to be placed immediately after
    _node_ (not inside it).

    @method endNode
    @param {HTMLElement|Node|String} [node] Node to set the end point to. May be
        specified as a Node instance, HTMLElement, or selector string. If not
        specified, the current end point will be returned.
    @param {Number|String} [offset=0] Offset position of the new end point
        relative to the _node_. If this is a number, it will be used as an
        offset position inside _node_. To specify a position immediately before
        _node_, use the string "before". To specify a position immediately after
        _node_, use the string "after".
    @return {Node} Node that contains the end point of this range.
    **/
    endNode: isHTML5 ? function (node, offset) {
        if (node) {
            var el = Y.one(node)._node;

            offset || (offset = 0);

            if (typeof offset === 'number') {
                this._range.setEnd(el, offset);
            } else if (offset === 'before') {
                this._range.setEndBefore(el);
            } else if (offset === 'after') {
                this._range.setEndAfter(el);
            }
        }

        return Y.one(this._range.endContainer);
    } : function (node, offset) {
        throw new Error('Not yet implemented.');
    },

    /**
    Gets or sets the offset of this selection's end position inside the end
    node.

    If the end node is a text node, the offset represents a character position.
    If the end node can contain child nodes, then the offset represents a child
    index.

    When setting an offset, you may use a numerical offset (which behaves as
    described above) or the string "before" or "after".

    The offset "before" will cause the end point to be placed immediately before
    the current end node (not inside it).

    The offset "after" will cause the end point to be placed immediately after
    the current end node (not inside it).

    Note that setting a "before" or "after" offset will change the end node. To
    get the new end node, call `endNode()`.

    @method endOffset
    @param {Number|String} [offset] Offset position of the new end point
        relative to the current end node. If this is a number, it will be used
        as an offset position inside the node. To specify a position immediately
        before the node, use the string "before". To specify a position
        immediately after the node, use the string "after".
    @return {Number} Offset of this selection's end position inside the end
        node.
    **/
    endOffset: isHTML5 ? function (offset) {
        if (offset || offset === 0) {
            this.endNode(this._range.endContainer, offset);
        }

        return this._range.endOffset;
    } : function (offset) {
        throw new Error('Not yet implemented.');
    },

    /**
    Attempts to expand the range to include element nodes while
    still being equivalent to the current range. Start and End nodes
    are not guaranteed to be element nodes after this method is executed.

    Examples:

        [] = startContainer, {} = endContainer, s:e = start/end offset

    given the HTML

        <div><span>Lorem</span></div>

    an initial range of

        <div><span>[{Lorem]}</span></div>  s0:e5

    after expansion would be

        [{<div><span>Lorem</span></div>}]  s0:e1

    @method expand
    @param {HTMLElement|Node} [container] If provided, do not expand the range
        past this container
    @chainable
    **/
    expand: function(container) {
        return this.expandStart(container).expandEnd(container);
    },

    /**
    Attempts to expand the range end to include element nodes while
    still being equivalent to the current range. End node is not
    guaranteed to be an element node after this method is executed.

    @method expandEnd
    @param {HTMLElement|Node} [container] If provided, do not expand the range
        past this container
    @chainable
    **/
    expandEnd: function(container) {
        var endNode = this.endNode()._node,
            offset = this.endOffset(),
            nodeType = endNode.nodeType,
            parentNode = endNode.parentNode;

        container = Y.one(container);

        if (TEXT_NODE === nodeType && endNode.length === offset && !endNode.nextSibling
                && (container && container._node !== parentNode)) {
            this.endNode(parentNode, 'after');
        }

        return this;
    },

    /**
    Attempts to expand the range start to include element nodes while
    still being equivalent to the current range. Start node is not
    guaranteed to be an element node after this method is executed.

    @method expandStart
    @param {HTMLElement|Node} [container] If provided, do not expand the range
        past this container
    @chainable
    **/
    expandStart: function(container) {
        var startNode = this.startNode()._node,
            offset = this.startOffset(),
            nodeType = startNode.nodeType,
            parentNode = startNode.parentNode;

        container = Y.one(container);

        if (TEXT_NODE === nodeType && 0 === offset && !startNode.previousSibling
                && (container && container._node !== parentNode)) {
            this.startNode(parentNode, 'before');
        }

        return this;
    },

    /**
    Moves this range's contents into a document fragment and returns a Node
    instance containing that fragment.

    Event listeners are not retained.

    If this range splits a non-text element, the resulting fragment will include
    a clone of that element, including its id (if it has one). This could lead
    to duplicate ids, so be careful.

    @method extractContents<commit_msg>editor: don't expand the range past the editor input container. for reals this time.<commit_after>/*jshint expr:true, onevar:false */

// Fun fact! The YUI Gallery currently doesn't support conditional loading of
// modules based on feature detection like YUI core does, so that's why all the
// legacy compat code is baked in here instead of in a separate conditionally
// loaded module.

/**
Provides the `Range` class, which normalizes Range behavior across browsers.

@module gallery-sm-selection
@submodule gallery-sm-range
**/

/**
Provides a friendly cross-browser Range API similar to the API defined in the
DOM Range specification.

@class Range
@param {window.Range} [range] Native Range object to wrap. If not provided, a
    new Range will be created.
@constructor
**/

var doc = Y.config.doc,
    win = Y.config.win,

    isHTML5 = !!(win && win.Range && doc.createRange),

    ELEMENT_NODE = 1,
    TEXT_NODE = 3;

var Range = isHTML5 ? function (range) {
    this._range = range || doc.createRange();
} : function (range) {
    this._range = range || doc.body.createTextRange();
};

Range.prototype = {
    // -- Public Methods -------------------------------------------------------

    /**
    Returns a new Range object with the same boundary points as this range.

    The returned Range is a copy, not a reference, so modifying it will not
    affect this range (and vice versa).

    @method clone
    @return {Range} New Range object with the same boundary points as this
        range.
    **/
    clone: isHTML5 ? function () {
        return new Y.Range(this._range.cloneRange());
    } : function () {
        throw new Error('Not yet implemented.');
    },

    /**
    Returns a Node instance containing a document fragment with a copy of this
    range's contents.

    Event listeners are not copied.

    Element ids _are_ copied. This could lead to duplicate ids, so be careful.

    Partially selected nodes will include parent tags to ensure that the
    fragment is valid.

    @method cloneContents
    @return {Node} Node instance containing a document fragment with a copy of
        this range's contents.
    **/
    cloneContents: isHTML5 ? function () {
        return Y.one(this._range.cloneContents());
    } : function () {
        throw new Error('Not yet implemented.');
    },

    /**
    Collapses this range by setting the start and end points to the same
    position, thus resulting in an empty range.

    @method collapse
    @param {Object} [options] Options.
        @param {Boolean} [options.toStart=false] If `true`, this range will be
            collapsed by moving the end point to the start point. Otherwise, the
            start point will be moved to the end point.
    @chainable
    **/
    collapse: function (options) {
        this._range.collapse(options && options.toStart);
        return this;
    },

    /**
    Compares the start or end boundary of this range with the start or end
    boundary of another range.

    @method compare
    @param {Range} otherRange Range to compare to.
    @param {Object} [options] Options.

        @param {String} [options.myPoint='start'] Specifies which boundary point
            on this range should be used for the comparison. Valid values are
            'start' to use this range's start point for the comparison, or 'end'
            to use this range's end point.

        @param {String} [options.otherPoint='start'] Specifies which boundary
            point on _otherRange_ should be used for the comparison. Valid
            values are 'start' to use _otherRange_'s start point for the
            comparison, or 'end' to use _otherRange_'s end point.

    @return {Number} -1, 0, or 1, indicating whether this range's boundary is
        respectively before, equal to, or after the other range's boundary.
    **/
    compare: isHTML5 ? function (otherRange, options) {
        // Make sure we're working with a native range, not a YUI range.
        if (otherRange._range) {
            otherRange = otherRange._range;
        }

        var myPoint    = (options && options.myPoint) || 'start',
            otherPoint = (options && options.otherPoint) || 'start';

        var how = win.Range[otherPoint.toUpperCase() + '_TO_' +
                    myPoint.toUpperCase()];

        return this._range.compareBoundaryPoints(how, otherRange);
    } : function () {
        throw new Error('Not yet implemented.');
    },

    /**
    Removes the contents of this range from the DOM.

    @method deleteContents
    @chainable
    **/
    deleteContents: isHTML5 ? function () {
        this._range.deleteContents();
        return this;
    } : function () {
        throw new Error('Not yet implemented.');
    },

    /**
    Gets or sets the node that contains the end point of this range.

    When specifying an _offset_, you may specify either a number or the string
    "before" or "after".

    A numerical offset will position the end point at that offset inside the
    _node_. If _node_ is a text node, the offset will represent a character
    position. If _node_ can contain child nodes, then the offset will represent
    a child index.

    The offset "before" will cause the end point to be placed immediately before
    _node_ (not inside it).

    The offset "after" will cause the end point to be placed immediately after
    _node_ (not inside it).

    @method endNode
    @param {HTMLElement|Node|String} [node] Node to set the end point to. May be
        specified as a Node instance, HTMLElement, or selector string. If not
        specified, the current end point will be returned.
    @param {Number|String} [offset=0] Offset position of the new end point
        relative to the _node_. If this is a number, it will be used as an
        offset position inside _node_. To specify a position immediately before
        _node_, use the string "before". To specify a position immediately after
        _node_, use the string "after".
    @return {Node} Node that contains the end point of this range.
    **/
    endNode: isHTML5 ? function (node, offset) {
        if (node) {
            var el = Y.one(node)._node;

            offset || (offset = 0);

            if (typeof offset === 'number') {
                this._range.setEnd(el, offset);
            } else if (offset === 'before') {
                this._range.setEndBefore(el);
            } else if (offset === 'after') {
                this._range.setEndAfter(el);
            }
        }

        return Y.one(this._range.endContainer);
    } : function (node, offset) {
        throw new Error('Not yet implemented.');
    },

    /**
    Gets or sets the offset of this selection's end position inside the end
    node.

    If the end node is a text node, the offset represents a character position.
    If the end node can contain child nodes, then the offset represents a child
    index.

    When setting an offset, you may use a numerical offset (which behaves as
    described above) or the string "before" or "after".

    The offset "before" will cause the end point to be placed immediately before
    the current end node (not inside it).

    The offset "after" will cause the end point to be placed immediately after
    the current end node (not inside it).

    Note that setting a "before" or "after" offset will change the end node. To
    get the new end node, call `endNode()`.

    @method endOffset
    @param {Number|String} [offset] Offset position of the new end point
        relative to the current end node. If this is a number, it will be used
        as an offset position inside the node. To specify a position immediately
        before the node, use the string "before". To specify a position
        immediately after the node, use the string "after".
    @return {Number} Offset of this selection's end position inside the end
        node.
    **/
    endOffset: isHTML5 ? function (offset) {
        if (offset || offset === 0) {
            this.endNode(this._range.endContainer, offset);
        }

        return this._range.endOffset;
    } : function (offset) {
        throw new Error('Not yet implemented.');
    },

    /**
    Attempts to expand the range to include element nodes while
    still being equivalent to the current range. Start and End nodes
    are not guaranteed to be element nodes after this method is executed.

    Examples:

        [] = startContainer, {} = endContainer, s:e = start/end offset

    given the HTML

        <div><span>Lorem</span></div>

    an initial range of

        <div><span>[{Lorem]}</span></div>  s0:e5

    after expansion would be

        [{<div><span>Lorem</span></div>}]  s0:e1

    @method expand
    @param {HTMLElement|Node} [container] If provided, do not expand the range
        past this container
    @chainable
    **/
    expand: function(container) {
        return this.expandStart(container).expandEnd(container);
    },

    /**
    Attempts to expand the range end to include element nodes while
    still being equivalent to the current range. End node is not
    guaranteed to be an element node after this method is executed.

    @method expandEnd
    @param {HTMLElement|Node} [container] If provided, do not expand the range
        past this container
    @chainable
    **/
    expandEnd: function(container) {
        var endNode = this.endNode()._node,
            offset = this.endOffset(),
            nodeType = endNode.nodeType,
            parentNode = endNode.parentNode;

        container = Y.one(container);

        if (TEXT_NODE === nodeType && endNode.length === offset && !endNode.nextSibling
                && (!container || container._node !== parentNode)) {
            this.endNode(parentNode, 'after');
        }

        return this;
    },

    /**
    Attempts to expand the range start to include element nodes while
    still being equivalent to the current range. Start node is not
    guaranteed to be an element node after this method is executed.

    @method expandStart
    @param {HTMLElement|Node} [container] If provided, do not expand the range
        past this container
    @chainable
    **/
    expandStart: function(container) {
        var startNode = this.startNode()._node,
            offset = this.startOffset(),
            nodeType = startNode.nodeType,
            parentNode = startNode.parentNode;

        container = Y.one(container);

        if (TEXT_NODE === nodeType && 0 === offset && !startNode.previousSibling
                && (!container || container._node !== parentNode)) {
            this.startNode(parentNode, 'before');
        }

        return this;
    },

    /**
    Moves this range's contents into a document fragment and returns a Node
    instance containing that fragment.

    Event listeners are not retained.

    If this range splits a non-text element, the resulting fragment will include
    a clone of that element, including its id (if it has one). This could lead
    to duplicate ids, so be careful.

    @method extractContents
{'fraction_non_alphanumeric': 0.08960232167959008, 'fraction_numerical': 0.0019045027887362263, 'mean_word_length': 2.453492013780144, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '6512600', 'n_tokens_mistral': 5793, 'n_tokens_neox': 5637, 'n_words': 2504}