Alloy UI

aui-char-counter  1.0.1

 
Filters
AUI.add('aui-char-counter', function(A) {
/**
 * The CharCounter Utility
 *
 * @module aui-char-counter
 */

var L = A.Lang,
	isNumber = L.isNumber,

	CHAR_COUNTER = 'char-counter',
	COUNTER = 'counter',
	INPUT = 'input',
	MAX_LENGTH = 'maxLength',
	SCROLL_LEFT = 'scrollLeft',
	SCROLL_TOP = 'scrollTop';

/**
 * <p><img src="assets/images/aui-char-counter/main.png"/></p>
 *
 * A base class for CharCounter, providing:
 * <ul>
 *    <li>Limit the number of characters allowed in an input box</li>
 *    <li>Display the number of characters left</li>
 * </ul>
 *
 * Quick Example:<br/>
 *
 * <pre><code>var instance = new A.CharCounter({
 *  input: '#elementId',
 *  counter: '#counterDisplayId',
 *  maxLength: 10
 * });
 * </code></pre>
 *
 * Check the list of <a href="CharCounter.html#configattributes">Configuration Attributes</a> available for
 * CharCounter.
 *
 * @class CharCounter
 * @uses AUI~input~handle
 * @param config {Object} Object literal specifying widget configuration properties.
 * @constructor
 * @extends Base
 */
var CharCounter = A.Component.create(
	{
		/**
		 * Static property provides a string to identify the class.
		 *
		 * @property CharCounter.NAME
		 * @type String
		 * @static
		 */
		NAME: CHAR_COUNTER,

		/**
		 * Static property used to define the default attribute
		 * configuration for the CharCounter.
		 *
		 * @property CharCounter.ATTRS
		 * @type Object
		 * @static
		 */
		ATTRS: {
			/**
			 * Node or Selector to display the information of the counter.
			 *
			 * @attribute counter
			 * @default null
			 * @type {Node | String}
			 */
			counter: {
				setter: A.one
			},

			/**
			 * Max number of characters the <a
	         * href="CharCounter.html#config_input">input</a> can have.
			 *
			 * @attribute maxLength
			 * @default Infinity
			 * @type Number
			 */
			maxLength: {
				lazyAdd: false,
				setter: function(v) {
					return this._setMaxLength(v);
				},
				value: Infinity,
				validator: isNumber
			},

			/**
			 * Node or Selector for the input field. Required.
			 *
			 * @attribute input
			 * @default null
			 * @type {Node | String}
			 */
			input: {
				setter: A.one
			}
		},

		EXTENDS: A.Base,

		prototype: {
			/**
			 * Event handler for the input <a
		     * href="module_aui-event.html">aui-event</a> event.
			 *
			 * @property handler
			 * @type EventHandle
			 * @protected
			 */
			handler: null,

			/**
			 * 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) {
					// use cross browser input-handler event
					instance.handler = 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);

				if (counter) {
					var value = instance.get(INPUT).val();

					counter.html(
						instance.get(MAX_LENGTH) - value.length
					);
				}
			},

			/**
			 * Descructor lifecycle implementation for the CharCounter class.
			 * Purges events attached to the node (and all child nodes).
			 *
			 * @method destroy
			 * @protected
			 */
			destroy: function() {
				var instance = this;

				if (instance.handler) {
					instance.handler.detach();
				}
			},

			/**
			 * Check the current value of the <a
		     * href="CharCounter.html#config_input">input</a>, truncate the data if
		     * needed, and re-sync the UI. Fired from <a
		     *  href="CharCounter.html#method__onInputChange">_onInputChange</a>.
			 *
			 * @method checkLength
			 */
			checkLength: function() {
				var instance = this;
				var input = instance.get(INPUT);
				var maxLength = instance.get(MAX_LENGTH);

				if (!input) {
					return false; // NOTE: return
				}

				var value = input.val();
				var scrollTop = input.get(SCROLL_TOP);
				var scrollLeft = input.get(SCROLL_LEFT);

				if (value.length > maxLength) {
					input.val(
						value.substring(0, maxLength)
					);
				}

				if (value.length == maxLength) {
					instance.fire('maxLength');
				}

				input.set(SCROLL_TOP, scrollTop);
				input.set(SCROLL_LEFT, scrollLeft);

				instance.syncUI();
			},

			/**
			 * Fired on input value change.
			 *
			 * @method _onInputChange
			 * @param {EventFacade} event
			 * @protected
			 */
			_onInputChange: function(event) {
				var instance = this;

				instance.checkLength();
			},

			/**
			 * Setter for <a href="CharCounter.html#config_maxLength">maxLength</a>.
			 *
			 * @method _setMaxLength
			 * @param {Number} v Value of the new <a
		     * href="CharCounter.html#config_maxLenght">maxLenght</a>.
			 * @protected
			 * @return {Number}
			 */
			_setMaxLength: function(v) {
				var instance = this;
				var input = instance.get(INPUT);

				if (input && (v < Infinity)) {
					input.set(MAX_LENGTH, v);
				}

				return v;
			}
		}
	}
);

A.CharCounter = CharCounter;

}, '@VERSION@' ,{skinnable:false, requires:['aui-base','aui-event-input']});