/*

Siesta 5.6.1
Copyright(c) 2009-2022 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license

*/
/**

 @class Siesta.Test.TextSelection

 This is a mixin providing text selection functionality.

 */
Role('Siesta.Test.TextSelection', {

    methods : {
        /**
         * Utility method which returns the selected text in the passed element or in the document
         * @param {Siesta.Test.ActionTarget} el The element
         * @return {String} The selected text
         */
        getSelectedText : function (el) {
            el      = this.normalizeElement(el);

            if ('selectionStart' in el) {
                try {
                    return el.value.substring(el.selectionStart, el.selectionEnd);
                } catch (e) {
                    // the "email" and "number" input fields (possibly some other too) does not allow to access
                    // the "selectionStart/End" properties and throws exceptions
                }
            }

            var win = this.global,
                doc = win.document;

            if (win.getSelection) {
                return win.getSelection().toString();
            } else if (doc.getSelection) {
                return doc.getSelection();
            } else if (doc.selection) {
                return doc.selection.createRange().text;
            }
        },

        /**
         * Utility method which selects text in the passed element (should be an <input> element).
         * @param {Siesta.Test.ActionTarget} el The element
         * @param {Int} start (optional) The selection start index
         * @param {Int} end (optional) The selection end index
         */
        selectText : function (el, start, end, callback) {
            el          = this.normalizeElement(el, true);

            if (el && this.elementSupportsSelection(el)) {

                var v       = el.value || el.innerHTML,
                    doFocus = true;

                if (v.length > 0) {
                    start   = start === undefined ? 0 : start;
                    end     = end === undefined ? v.length : end;

                    if (el.setSelectionRange) {
                        try {
                            // can throw exception in IE9 (if element is not visible)
                            el.setSelectionRange(start, end);
                        } catch (e) {
                        }
                    } else if (el.createTextRange) {
                        var R = el.createTextRange();

                        R.moveStart('character', start);
                        R.moveEnd('character', end - v.length);

                        R.select();
                    }

                    doFocus = bowser.gecko || bowser.opera;
                }

                if (doFocus) {
                    this.focus(el);
                }
            }

            callback && callback.call(this);
        },

        // Returns undefined if caret pos cannot be determined (older IEs with contentEditable)
        getCaretPosition : function (el) {
            var pos;

            var win         = this.global;
            var document    = win.document;

            if ('selectionStart' in el) {
                try {
                    // the exception can be thrown in case of manipulating with input#type=email fields
                    // and possibly some other input types
                    pos     = el.selectionStart;
                } catch (e) {
                    pos     = null
                }
            } else if (win.getSelection && this.isEditableNode(el)){
                var sel     = win.getSelection();

                if (sel.rangeCount) {
                    var range = sel.getRangeAt(0);

                    if (range.commonAncestorContainer.parentNode == el) {
                        pos = range.endOffset;
                    }
                }
            } else if (document.selection) { // Legacy IE
                this.focus(el)

                var oSel    = document.selection.createRange();

                oSel.moveStart('character', -el.value.length);

                pos         = oSel.text.length;
            }

            return pos;
        },


        setCaretPosition : function (el, caretPos) {
            if (el.createTextRange) {
                // seems range methods throws sometimes in IE
                try {
                    var range = el.createTextRange();

                    range.move('character', caretPos);
                    range.select();
                } catch (e) {
                }
            } else {

                if (el.setSelectionRange) {
                    this.focus(el)

                    try {
                        // the exception can be thrown in case of manipulating with input#type=email fields
                        // and possibly some other input types
                        el.setSelectionRange(caretPos, caretPos);
                    } catch (e) {
                    }

                } else {
                    this.focus(el)
                }
            }
        },

        moveCaretPosition : function (el, distance) {
            var pos = Math.min(Math.max(0, this.getCaretPosition(el) + distance), el.value.length);

            this.setCaretPosition(el, pos);
        },

        elementSupportsSelection : function (el) {
            var tagName    = el.tagName.toLowerCase();
            var inputTypes = /^text|password|tel|url|search$/i;

            return tagName === 'textarea' || tagName === 'input' && inputTypes.test(el.getAttribute('type'));
        }
    }
})