Show:
                            /**
                             * The CharCounter Utility
                             *
                             * @module aui-char-counter
                             */
                            
                            var L = A.Lang,
                                isNumber = L.isNumber,
                                isString = L.isString;
                            
                                A.Node.DOM_EVENTS.compositionend = 1;
                                A.Node.DOM_EVENTS.compositionstart = 1;
                            
                            /**
                             * A base class for CharCounter, providing:
                             *
                             * - Limit the number of characters allowed in an input box
                             * - Display the number of characters left
                             *
                             * Check the [live demo](http://alloyui.com/examples/char-counter/).
                             *
                             * @class A.CharCounter
                             * @extends Base
                             * @param {Object} config Object literal specifying widget configuration
                             *     properties.
                             * @constructor
                             * @example
                            ```
                            <form class="form-inline">
                                <input id="myInput" class="form-control" type="text">
                                <label class="control-label">
                                    <span id="myCounter"></span> character(s) remaining
                                </label>
                            </form>
                            ```
                             * @example
                            ```
                            YUI().use(
                              'aui-char-counter',
                              function(Y) {
                                new Y.CharCounter(
                                  {
                                    counter: '#myCounter',
                                    input: '#myInput',
                                    maxLength: 10
                                  }
                                );
                              }
                            );
                            ```
                             */
                            var CharCounter = A.Component.create({
                                /**
                                 * Static property provides a string to identify the class.
                                 *
                                 * @property NAME
                                 * @type String
                                 * @static
                                 */
                                NAME: 'char-counter',
                            
                                /**
                                 * Static property used to define the default attribute
                                 * configuration for the CharCounter.
                                 *
                                 * @property ATTRS
                                 * @type Object
                                 * @static
                                 */
                                ATTRS: {
                                    /**
                                     * ARIA atomic attribute that describes assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the aria-relevant attribute.
                                     *
                                     * @attribute atomic
                                     * @default true
                                     * @type {Boolean}
                                     */
                                    atomic: {
                                        value: true
                                    },
                            
                                    /**
                                     * Node or Selector to display the information of the counter.
                                     *
                                     * @attribute counter
                                     * @default null
                                     * @type {Node | String}
                                     */
                                    counter: {
                                        setter: A.one
                                    },
                            
                                    /**
                                     * ARIA describedby attribute that describes the current element.
                                     *
                                     * @attribute describedby
                                     * @default ''
                                     * @type {String}
                                     */
                                    describedby: {
                                        value: ''
                                    },
                            
                                    /**
                                     * Node or Selector for the input field. Required.
                                     *
                                     * @attribute input
                                     * @default null
                                     * @type {Node | String}
                                     */
                                    input: {
                                        setter: A.one
                                    },
                            
                                    /**
                                     * ARIA live attribute to help assistive technology properly read updates
                                     * to the number of characters remaining.
                                     *
                                     * @attribute live
                                     * @default 'polite'
                                     * @type {String}
                                     */
                                    live: {
                                        validator: isString,
                                        value: 'polite'
                                    },
                            
                                    /**
                                     * Max number of characters the [input](A.CharCounter.html#attr_input)
                                     * can have.
                                     *
                                     * @attribute maxLength
                                     * @default Infinity
                                     * @type Number
                                     */
                                    maxLength: {
                                        lazyAdd: false,
                                        setter: function(v) {
                                            return this._setMaxLength(v);
                                        },
                                        validator: isNumber,
                                        value: Infinity
                                    },
                            
                                    /**
                                     * Boolean indicating if use of the WAI-ARIA Roles and States
                                     * should be enabled.
                                     *
                                     * @attribute useARIA
                                     * @default true
                                     * @type Boolean
                                     */
                                    useARIA: {
                                        value: true,
                                        validator: L.isBoolean,
                                        writeOnce: 'initOnly'
                                    }
                                },
                            
                                /**
                                 * Static property used to define which component it extends.
                                 *
                                 * @property EXTENDS
                                 * @type Object
                                 * @static
                                 */
                                EXTENDS: A.Base,
                            
                                prototype: {
                            
                                    /**
                                     * Holds the event handles for any bind event from the internal
                                     * implementation.
                                     *
                                     * @property _eventHandles
                                     * @type {Array}
                                     * @protected
                                     */
                                    _eventHandles: null,
                            
                                    /**
                                     * Tracks whether input is being manipulated by an IME tool.
                                     *
                                     * @property _inputComposition
                                     * @type {Boolean}
                                     * @protected
                                     */
                                    _inputComposition: false,
                            
                                    /**
                                     * Construction logic executed during CharCounter instantiation.
                                     * Lifecycle.
                                     *
                                     * @method initializer
                                     * @protected
                                     */
                                    initializer: function() {
                                        var instance = this;
                            
                                        instance.bindUI();
                            
                                        instance.checkLength();
                                    },
                            
                                    /**
                                     * Bind the events on the CharCounter UI. Lifecycle.
                                     *
                                     * @method bindUI
                                     * @protected
                                     */
                                    bindUI: function() {
                                        var instance = this;
                            
                                        var input = instance.get('input');
                            
                                        instance.publish('maxLength');
                            
                                        instance.after('maxLengthChange', instance.checkLength);
                            
                                        if (input) {
                                            instance._eventHandles = [
                                                input.on('compositionend', A.bind(instance._onInputCompositionEnd, instance)),
                                                input.on('compositionstart', A.bind(instance._onInputCompositionStart, instance)),
                                                // use cross browser input-handler event
                                                input.on('input', A.bind(instance._onInputChange, instance))
                                            ];
                                        }
                                    },
                            
                                    /**
                                     * Sync the CharCounter UI. Lifecycle.
                                     *
                                     * @method syncUI
                                     * @protected
                                     */
                                    syncUI: function() {
                                        var instance = this;
                            
                                        var counter = instance.get('counter');
                                        var useAria = instance.get('useARIA');
                            
                                        if (counter) {
                                            var value = instance.get('input').val();
                            
                                            var counterValue = instance.get('maxLength') - instance._getNormalizedLength(value);
                            
                                            counter.html(counterValue);
                                        }
                            
                                        if (useAria) {
                                            this._syncAriaControlsUI();
                                        }
                                    },
                            
                                    /**
                                     * Destructor lifecycle implementation for the `CharCounter` class.
                                     * Purges events attached to the node (and all child nodes).
                                     *
                                     * @method destroy
                                     * @protected
                                     */
                                    destroy: function() {
                                        var instance = this;
                            
                                        (new A.EventHandle(instance._eventHandles)).detach();
                                    },
                            
                                    /**
                                     * Check the current value of the
                                     * [input](A.CharCounter.html#attr_input), truncate the data if needed,
                                     * and re-sync the UI. Fired from
                                     * [_onInputChange](A.CharCounter.html#method__onInputChange).
                                     *
                                     * @method checkLength
                                     * @return {Boolean | String} Returns the final value if it was changed.
                                     *   Otherwise returns either true, when the input value was checked, or
                                     *   false if there was no input to check the value for.
                                     */
                                    checkLength: function() {
                                        var instance = this;
                            
                                        var input = instance.get('input');
                            
                                        var returnValue = false;
                            
                                        if (input) {
                                            var maxLength = instance.get('maxLength');
                                            var value = input.val();
                            
                                            var normalizedLength = instance._getNormalizedLength(value);
                            
                                            returnValue = true;
                            
                                            if (normalizedLength > maxLength) {
                                                var scrollTop = input.get('scrollTop');
                                                var scrollLeft = input.get('scrollLeft');
                            
                                                var trimLength = maxLength - (normalizedLength - value.length);
                            
                                                value = value.substring(0, trimLength);
                            
                                                input.val(value);
                            
                                                input.set('scrollTop', scrollTop);
                                                input.set('scrollLeft', scrollLeft);
                            
                                                returnValue = value;
                                            }
                            
                                            instance.syncUI();
                            
                                            if (normalizedLength >= maxLength) {
                                                instance.fire('maxLength');
                                            }
                                        }
                            
                                        return returnValue;
                                    },
                            
                                    /**
                                     * Normalize reported length between browsers.
                                     *
                                     * @method _getNormalizedLength
                                     * @param {String} value.
                                     * @protected
                                     * @return {Number}
                                     */
                                    _getNormalizedLength: function(value) {
                                        var newLines = value.match(/(\r\n|\n|\r)/g);
                            
                                        var newLinesCorrection = 0;
                            
                                        if (newLines !== null) {
                                            newLinesCorrection = newLines.length;
                                        }
                            
                                        return value.length + newLinesCorrection;
                                    },
                            
                                    /**
                                     * Fired on input value change.
                                     *
                                     * @method _onInputChange
                                     * @param {EventFacade} event
                                     * @protected
                                     */
                                    _onInputChange: function() {
                                        var instance = this;
                            
                                        if (!instance._inputComposition) {
                                            instance.checkLength();
                                        }
                                    },
                            
                                    /**
                                     * Fired on input when `compositionend` event occurs.
                                     *
                                     * @method _onInputCompositionEnd
                                     * @param {EventFacade} event
                                     * @protected
                                     */
                                    _onInputCompositionEnd: function() {
                                        var instance = this;
                            
                                        instance._inputComposition = false;
                            
                                        instance.checkLength();
                                    },
                            
                                    /**
                                     * Fired on input when `compositionstart` event occurs.
                                     *
                                     * @method _onInputCompositionStart
                                     * @param {EventFacade} event
                                     * @protected
                                     */
                                    _onInputCompositionStart: function() {
                                        var instance = this;
                            
                                        instance._inputComposition = true;
                                    },
                            
                                    /**
                                     * Setter for [maxLength](A.CharCounter.html#attr_maxLength).
                                     *
                                     * @method _setMaxLength
                                     * @param {Number} v Value of the new
                                     *     [maxLength](A.CharCounter.html#attr_maxLength).
                                     * @protected
                                     * @return {Number}
                                     */
                                    _setMaxLength: function(v) {
                                        var instance = this;
                            
                                        var input = instance.get('input');
                            
                                        if (input && (v < Infinity)) {
                                            input.set('maxLength', v);
                                        }
                            
                                        return v;
                                    },
                            
                                     /**
                                     * Updates the aria attribute for the component.
                                     *
                                     * @method _syncAriaControlsUI
                                     * @protected
                                     */
                                    _syncAriaControlsUI: function() {
                                        var instance = this;
                            
                                        instance.plug(
                                            A.Plugin.Aria,
                                            {
                                                attributes: {
                                                    describedby: 'describedby',
                                                },
                                                attributeNode: instance.get('input')
                                            }
                                        );
                            
                                        var describedBy = instance.get('describedby');
                            
                                        describedBy = A.one('#' + describedBy);
                            
                                        if (describedBy) {
                                            var atomic = instance.get('atomic');
                                            var live = instance.get('live');
                            
                                            this.aria.setAttributes(
                                                [
                                                    {
                                                        name: 'atomic',
                                                        node: describedBy,
                                                        value: atomic
                                                    },
                                                    {
                                                        name: 'live',
                                                        node: describedBy,
                                                        value: live
                                                    }
                                                ]
                                            );
                                        }
                                    }
                                }
                            });
                            
                            A.CharCounter = CharCounter;