wikimedia/mediawiki-extensions-Translate

View on GitHub
resources/lib/jquery.autosize.js

Summary

Maintainability
C
1 day
Test Coverage
/*!
    autosize 4.0.2
    license: MIT
    http://www.jacklmoore.com/autosize
*/
( function ( global, factory ) {
    if ( typeof define === 'function' && define.amd ) {
        define( [ 'module', 'exports' ], factory );
    } else if ( typeof exports !== 'undefined' ) {
        factory( module, exports );
    } else {
        var mod = {
            exports: {}
        };
        factory( mod, mod.exports );
        global.autosize = mod.exports;
    }
}( this, function ( module, exports ) {
    'use strict';

    var map = typeof Map === 'function' ? new Map() : ( function () {
            var keys = [],
         values = [];

            return {
                has: function has( key ) {
                    return keys.indexOf( key ) > -1;
                },
                get: function get( key ) {
                    return values[ keys.indexOf( key ) ];
                },
                set: function set( key, value ) {
                    if ( keys.indexOf( key ) === -1 ) {
                        keys.push( key );
                        values.push( value );
                    }
                },
                delete: function _delete( key ) {
                    var index = keys.indexOf( key );
                    if ( index > -1 ) {
                        keys.splice( index, 1 );
                        values.splice( index, 1 );
                    }
                }
            };
        }() ),

     createEvent = function createEvent( name ) {
            return new Event( name, { bubbles: true } );
        };
    try {
        new Event( 'test' );
    } catch ( e ) {
        // IE does not support `new Event()`
        createEvent = function createEvent( name ) {
            var evt = document.createEvent( 'Event' );
            evt.initEvent( name, true, false );
            return evt;
        };
    }

    function assign( ta ) {
        if ( !ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has( ta ) ) { return; }

        var heightOffset = null,
         clientWidth = null,
         cachedHeight = null;

        function init() {
            var style = window.getComputedStyle( ta, null );

            if ( style.resize === 'vertical' ) {
                ta.style.resize = 'none';
            } else if ( style.resize === 'both' ) {
                ta.style.resize = 'horizontal';
            }

            if ( style.boxSizing === 'content-box' ) {
                heightOffset = -( parseFloat( style.paddingTop ) + parseFloat( style.paddingBottom ) );
            } else {
                heightOffset = parseFloat( style.borderTopWidth ) + parseFloat( style.borderBottomWidth );
            }
            // Fix when a textarea is not on document body and heightOffset is Not a Number
            if ( isNaN( heightOffset ) ) {
                heightOffset = 0;
            }

            update();
        }

        function changeOverflow( value ) {
            {
                // Chrome/Safari-specific fix:
                // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
                // made available by removing the scrollbar. The following forces the necessary text reflow.
                var width = ta.style.width;
                ta.style.width = '0px';
                // Force reflow:
                /* jshint ignore:start */
                ta.offsetWidth;
                /* jshint ignore:end */
                ta.style.width = width;
            }

            ta.style.overflowY = value;
        }

        function getParentOverflows( el ) {
            var arr = [];

            while ( el && el.parentNode && el.parentNode instanceof Element ) {
                if ( el.parentNode.scrollTop ) {
                    arr.push( {
                        node: el.parentNode,
                        scrollTop: el.parentNode.scrollTop
                    } );
                }
                el = el.parentNode;
            }

            return arr;
        }

        function resize() {
            if ( ta.scrollHeight === 0 ) {
                // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
                return;
            }

            var overflows = getParentOverflows( ta ),
             docTop = document.documentElement && document.documentElement.scrollTop; // Needed for Mobile IE (ticket #240)

            ta.style.height = '';
            ta.style.height = ta.scrollHeight + heightOffset + 'px';

            // used to check if an update is actually necessary on window.resize
            clientWidth = ta.clientWidth;

            // prevents scroll-position jumping
            overflows.forEach( function ( el ) {
                el.node.scrollTop = el.scrollTop;
            } );

            if ( docTop ) {
                document.documentElement.scrollTop = docTop;
            }
        }

        function update() {
            resize();

            var styleHeight = Math.round( parseFloat( ta.style.height ) ),
             computed = window.getComputedStyle( ta, null ),

                // Using offsetHeight as a replacement for computed.height in IE, because IE does not account use of border-box
             actualHeight = computed.boxSizing === 'content-box' ? Math.round( parseFloat( computed.height ) ) : ta.offsetHeight;

            // The actual height not matching the style height (set via the resize method) indicates that
            // the max-height has been exceeded, in which case the overflow should be allowed.
            if ( actualHeight < styleHeight ) {
                if ( computed.overflowY === 'hidden' ) {
                    changeOverflow( 'scroll' );
                    resize();
                    actualHeight = computed.boxSizing === 'content-box' ? Math.round( parseFloat( window.getComputedStyle( ta, null ).height ) ) : ta.offsetHeight;
                }
            } else {
                // Normally keep overflow set to hidden, to avoid flash of scrollbar as the textarea expands.
                if ( computed.overflowY !== 'hidden' ) {
                    changeOverflow( 'hidden' );
                    resize();
                    actualHeight = computed.boxSizing === 'content-box' ? Math.round( parseFloat( window.getComputedStyle( ta, null ).height ) ) : ta.offsetHeight;
                }
            }

            if ( cachedHeight !== actualHeight ) {
                cachedHeight = actualHeight;
                var evt = createEvent( 'autosize:resized' );
                try {
                    ta.dispatchEvent( evt );
                } catch ( err ) {
                    // Firefox will throw an error on dispatchEvent for a detached element
                    // https://bugzilla.mozilla.org/show_bug.cgi?id=889376
                }
            }
        }

        var pageResize = function pageResize() {
                if ( ta.clientWidth !== clientWidth ) {
                    update();
                }
            },

         destroy = function ( style ) {
                window.removeEventListener( 'resize', pageResize, false );
                ta.removeEventListener( 'input', update, false );
                ta.removeEventListener( 'keyup', update, false );
                ta.removeEventListener( 'autosize:destroy', destroy, false );
                ta.removeEventListener( 'autosize:update', update, false );

                Object.keys( style ).forEach( function ( key ) {
                    ta.style[ key ] = style[ key ];
                } );

                map.delete( ta );
            }.bind( ta, {
                height: ta.style.height,
                resize: ta.style.resize,
                overflowY: ta.style.overflowY,
                overflowX: ta.style.overflowX,
                wordWrap: ta.style.wordWrap
            } );

        ta.addEventListener( 'autosize:destroy', destroy, false );

        // IE9 does not fire onpropertychange or oninput for deletions,
        // so binding to onkeyup to catch most of those events.
        // There is no way that I know of to detect something like 'cut' in IE9.
        if ( 'onpropertychange' in ta && 'oninput' in ta ) {
            ta.addEventListener( 'keyup', update, false );
        }

        window.addEventListener( 'resize', pageResize, false );
        ta.addEventListener( 'input', update, false );
        ta.addEventListener( 'autosize:update', update, false );
        ta.style.overflowX = 'hidden';
        ta.style.wordWrap = 'break-word';

        map.set( ta, {
            destroy: destroy,
            update: update
        } );

        init();
    }

    function destroy( ta ) {
        var methods = map.get( ta );
        if ( methods ) {
            methods.destroy();
        }
    }

    function update( ta ) {
        var methods = map.get( ta );
        if ( methods ) {
            methods.update();
        }
    }

    var autosize = null;

    // Do nothing in Node.js environment and IE8 (or lower)
    if ( typeof window === 'undefined' || typeof window.getComputedStyle !== 'function' ) {
        autosize = function autosize( el ) {
            return el;
        };
        autosize.destroy = function ( el ) {
            return el;
        };
        autosize.update = function ( el ) {
            return el;
        };
    } else {
        autosize = function autosize( el, options ) {
            if ( el ) {
                Array.prototype.forEach.call( el.length ? el : [ el ], function ( x ) {
                    return assign( x, options );
                } );
            }
            return el;
        };
        autosize.destroy = function ( el ) {
            if ( el ) {
                Array.prototype.forEach.call( el.length ? el : [ el ], destroy );
            }
            return el;
        };
        autosize.update = function ( el ) {
            if ( el ) {
                Array.prototype.forEach.call( el.length ? el : [ el ], update );
            }
            return el;
        };
    }

    exports.default = autosize;
    module.exports = exports.default;
} ) );