/**
 * @event changeAttr Gets fired if an attribute(s) of an element changed.
 * @member jQuery
 * @since 6.11.0
 */

/*
 * @copyright		© Copyright 2006-2010, epages GmbH, All Rights Reserved.
 *
 * @module			ep.modify
 *
 * @revision		$Revision: 1.7 $
 */

define("ep/modify", [
	"jquery"
], function ($) {

// modify $.attr + $.removeAttr for event

		// save original methods
	var _attr = $.fn.attr,
		_removeAttr = $.fn.removeAttr
		_trigger = $.event.trigger,

		isVisible = $.expr.filters.visible,

		onEventExpr = /^on/,
		nativeEventExpr = /^(blur|change|click|dblclick|error|focus|keydown|keypress|keyup|load|mousedown|mousemove|mouseout|mouseover|mouseup|resize|select|unload)$/,
		mouseEventExpr = /^(click|dblclick|mousedown|mousemove|mouseout|mouseover|mouseup)$/;

	// handle for event trigger
	function attrEvent( elems, name, value ){
		var attr;

		if( typeof name === 'object' ){
			attr = name;
		}
		else if( value !== undefined ){
			(attr = {})[ name ] = value;
		}

		if( attr ){
			elems.trigger('changeAttr',attr);
		}
	}
//#JSCOVERAGE_IF false
	// overwrite trigger for dojo connect
	$.event.trigger = function( event, data, elem, onlyHandlers ){
		if( event.dojoconnect ){
			var type = event.type,
				eventObj;
			// break if no element given
			if( !elem ){
				return;
			}
			// remove exclusive option
			if ( type.indexOf("!") >= 0 ) {
				type = type.slice(0, -1);
			}
			// remove namespace
			if ( type.indexOf(".") >= 0 ) {
				type = type.split(".")[0];
			}
			// remove on prefix
			type = onEventExpr.test(type) ? type.slice(2) : type;

			if( nativeEventExpr.test(type) && !onlyHandlers ){
				if( elem.fireEvent ){
					try{
						elem.fireEvent( 'on' + type );
					}
					catch( error ){

					}
				}
				else{
					if( mouseEventExpr.test(type) ){ // create mouse events
						eventObj = document.createEvent('MouseEvents');
						eventObj.initMouseEvent(
							type,
							!!event.canBubble,
							!!event.cancelable,
							window,
							event.detail || 0,
							event.screenX || 0,
							event.screenY || 0,
							event || 0,
							event.clientY || 0,
							!!event.ctrlKey,
							!!event.altKey,
							!!event.shiftKey,
							!!event.metaKey,
							event.button || 0,
							event.relatedTarget || null
						);
					}
					else{										// create html events
						eventObj = document.createEvent('HTMLEvents');
						eventObj.initEvent(
							type,
							!!event.canBubble,
							!!event.cancelable
						);
					}
					elem.dispatchEvent(eventObj);
				}
				return;
			}
		}

		return _trigger.apply( this, arguments );
	};
//#JSCOVERAGE_ENDIF

	// overwrite methods
	$.fn.extend({
		/**
		 * Set one or more attributes for the set of matched elements.
		 * 
		 * The ep.modify plugin overwrites the `.attr()` method, is works like the original method but fire an
		 * event named changeAttr.
		 * 
		 * If only one parameter is given, the parameter is interpreted as a map, where the keys denote the `attributeName`s and the values denote the `value`s.
		 * 
		 * ### Examples
		 * Bind changeAttr event and change an attribute.
		 * 
		 * JavaScript:
		 * 
		 *     $(':checkbox').on( 'changeAttr', function( event ){
		 *         var elem = $(this);
		 *         if( elem.is(':selected') ){
		 *             elem.next().hide();
		 *         }
		 *         else{
		 *             elem.next().show();
		 *         }
		 *     });
		 *     
		 *     $(':checkbox').attr( 'selected', true );
		 * 
		 * HTML:
		 * 
		 *     <div>
		 *         <input type="checkbox" name="foo"/>
		 *         <span>This field is required.</span>
		 *     </span>
		 * 
		 * Results:
		 * 
		 *     <div>
		 *         <input type="checkbox" name="foo" selected="true"/>
		 *         <span style="display:none;">This field is required.</span>
		 *     </span>
		 * 
		 * 
		 * @param {String} attributeName The name of the attribute to set.
		 * @param {String} [value] A value to set for the attribute.
		 * 
		 * @fires changeAttr
		 * 
		 * @method attr
		 * @member jQuery
		 * 
		 * @since 6.11.0
		 */
		attr: function( name, value ){
			var elems = $(this),
				ret = _attr.apply(elems, arguments);

			attrEvent( elems, name, value );

			return ret;
		},
		/**
		 * Remove an attribute from each element in the set of matched elements.
		 * 
		 * The ep.modify plugin overwrites the `.removeAttr()` method, is works like the original method but fire an
		 * event named changeAttr.
		 * 
		 * ### Examples
		 * Bind changeAttr event and remove an attribute.
		 * 
		 * JavaScript:
		 * 
		 *     $(':checkbox').on( 'changeAttr', function( event ){
		 *         var elem = $(this);
		 *         if( elem.is(':selected') ){
		 *             elem.next().hide();
		 *         }
		 *         else{
		 *             elem.next().show();
		 *         }
		 *     });
		 *     
		 *     $(':checkbox').removeAttr( 'selected' );
		 * 
		 * HTML:
		 * 
		 *     <div>
		 *       <input type="checkbox" name="foo" selected="true"/>
		 *       <span style="display:none;">This field is required.</span>
		 *     </span>
		 * 
		 * Results:
		 * 
		 *     <div>
		 *       <input type="checkbox" name="foo"/>
		 *       <span>This field is required.</span>
		 *     </span>
		 * 
		 * 
		 * @param {String} attributeName The name of the attribute to set.
		 * 
		 * @fires changeAttr
		 * 
		 * @method removeAttr
		 * @member jQuery
		 * 
		 * @since 6.11.0
		 */
		removeAttr: function( name ){
			var elems = $(this),
				ret = _removeAttr.apply(elems, arguments);

			attrEvent( this, name, null );

			return ret;
		}
	});

	// special pseudo selector for input fields
	$.extend( $.expr[':'],{
		'invalid': function( elem ){
			return !!elem.formInvalid && !elem.disabled && (elem.type==='hidden' || isVisible(elem));
		}
	});

	return $;

});