/**
 * jQuery.Rule - Css Rules manipulation, the jQuery way.
 * Copyright (c) 2007 Ariel Flesler - aflesler(at)gmail(dot)com
 * Licensed under GPL license (http://www.opensource.org/licenses/gpl-license.php).
 * Date: 11/23/2007
 * Compatible with jQuery 1.2, tested on Firefox 2.0.0.9, and IE 6, both on Windows.
 *
 * @author Ariel Flesler
 * @version 0.9.2
 *
 * @id jQuery.rule
 * @param {String} The rules, can be a selector, or literal CSS rules. Many can be given, comma separated.
 * @param {Undefined|String|DOMElement|jQuery) The context stylesheets, all of them by default.
 * @return {jQuery.Rule} Returns a jQuery.Rule object.
 *
 * @example $.rule('p,div').remove();
 *
 * @example $.rule('div{ padding:20px;background-color:#CCCCCC},p{border:1px red solid; }').appendTo('style');
 *
 * @example $.rule('div{}').append('margin:40px').css('margin-left',0).appendTo('link:eq(1)');
 *
 * Notes:
 *	- $.rule.queue (and thus animate) don't work in IE yet.
 **/
(function( $ ){	
	
	var storageNode = $('<style rel="alternate stylesheet" type="text/css">').appendTo('head')[0],//we must append to get a stylesheet
		sheet = storageNode.sheet ? 'sheet' : 'styleSheet',
		storage = storageNode[sheet],//css rules must remain in a stylesheet for IE and FF
		rules = storage.rules ? 'rules' : 'cssRules',
		remove = storage.deleteRule ? 'deleteRule' : 'removeRule',
		owner = storage.ownerNode ? 'ownerNode' : 'owningElement',		
		reRule = /^([^{]+)\{([^}]*)\}.*/m,
		reStyle = /([^:]+):([^;}]+)/;	

	storage.disabled = true;//let's ignore your rules 
	
	$.rule = function( r, c ){
		if(!(this instanceof $.rule))
			return new $.rule( r, c );

		this.sheets = $.rule.sheets(c);
		if( r && reRule.test(r) )
			r = $.rule.clean( r );
		if( typeof r == 'object' && !r.exec )
			return this.setArray( r.get ? r.get() : r.splice ? r : [r] );
		this.setArray( this.sheets.cssRules().get() );
		return r ? this.filter( r ) : this;
	};
	
	$.extend( $.rule, {
		sheets:function( c ){
			var o = c;
			if( typeof o != 'object' )
				o = $.makeArray(document.styleSheets);
			o = $(o);
			if( typeof c == 'string' )
				o = o.ownerNode().filter(c).sheet();
			return o.not(storage);//skip our stylesheet
		},
		rule:function( str ){
			if( str.selectorText )
				return [ '', str.selectorText, str.style.cssText ];
			return reRule.exec( str );
		},
		appendTo:function( r, ss, skip ){
			switch( typeof ss ){//find the desired stylesheet
				case 'string': ss = this.sheets(ss);
				case 'object':
					if( ss[0] ) ss = ss[0];
					if( ss[sheet] ) ss = ss[sheet];
					if( ss[rules] ) break;//only if the stylesheet is valid
				default:
					if( typeof r == 'object' ) return r;//let's not waist time, it is parsed
					ss = storage;
			}
			var p;
			if( !skip && (p = this.parent(r)) )//if this is an actual rule, and it's appended.
				r = this.remove( r, p );
				
			var rule = this.rule( r );
			//Safari will need more hacking
			if( ss.addRule )
				ss.addRule( rule[1], rule[2]||'x:y' );//IE won't allow empty rules
			else if( ss.insertRule )
				ss.insertRule( rule[1] + '{'+ rule[2] +'}', ss[rules].length);
			
			return ss[rules][ ss[rules].length - 1 ];//return the added/parsed rule
		},
		remove:function( r, p ){
			p = p || this.parent(r);
			if( p != storage ){//let's save some unnecesary cycles.
				var i = p ? $.inArray( r, p[rules] ) : -1;
				if( i != -1 ){//if not stored before removal, IE will crash eventually, and some rules in FF get messed up
					r = this.appendTo( r, storage, true );
					p[remove](i);
				}
			}
			return r;
		},
		clean:function( r ){
			return $.map( r.split(/\s*,\s*/), function( txt ){
				return $.rule.appendTo( txt, storage );//parse the string.
			});
		},
		parent:function( r ){//CSS rules in IE don't have parentStyleSheet attribute
			if( typeof r == 'string' || !$.browser.msie )//if it's a string, just return undefined.
				return r.parentStyleSheet;

			var par;
			this.sheets().each(function(){
				if( $.inArray(r, this[rules]) != -1 ){
					par = this;	
					return false;
				}
			});
			return par;
		},
		outerText:function( rule ){
			return !rule ? '' : [rule.selectorText+'{', '\t'+rule.style.cssText,'}'].join('\n').toLowerCase();
		},
		text:function( rule, txt ){
			if( txt !== undefined )
				rule.style.cssText = txt;
			return !rule ? '' : rule.style.cssText.toLowerCase();
		}
	});
	
	$.rule.fn = $.rule.prototype = {
		pushStack:function( rs, sh ){
			var ret = $.rule( rs, sh || this.sheets );
			ret.prevObject = this;
			return ret;
		},
		end:function(){
			return this.prevObject || $.rule(null,[]);
		},
		filter:function( s ){
			var re;
			if( !s )
				s = 'true';//just keep them all.
			else if( s.split || s.exec ){//string regex, or actual regex
				re = s.exec ? s : new RegExp( '(?:^|,)\\s*(?:'+s.replace(/,/gi,'|')+')\\s*(?:,|$)','i' );
				s = function(r){ return re.test(r.selectorText); };
			}
			return this.pushStack($.grep(this, s ));
		},
		add:function( rs, c ){
			return this.pushStack( $.merge(this.get(), $.rule(rs, c)) );	
		},
		is:function( s ){
			return !!(s && this.filter( s ).length);
		},
		not:function( n, c ){
			n = $.rule( n, c );
			return this.filter(function(){
				return $.inArray( this, n ) == -1;
			});
		},
		append:function( s ){
			var rules = this, rule;
			$.each( s.split(/\s*;\s*/),function(i,v){
				if(( rule = reStyle.exec( v ) ))
					rules.css( rule[1], rule[2] );
			});
			return this;
		},
		text:function( txt ){
			return !arguments.length ? $.rule.text( this[0] )
				: this.each(function(){	$.rule.text( this, txt ); });
		},
		outerText:function(){
			return $.rule.outerText(this[0]);	
		}
		
	};
	
	$.each({
		ownerNode:owner,//when having the stylesheet, get the node that contains it
		sheet:sheet, //get the stylesheet from the node
		cssRules:rules //get the rules from the stylesheet.
	},function( m, a ){
		var many = a == rules;//the rules need some more processing
		$.fn[m] = function(){
			return this.map(function(){
				return many ? $.makeArray(this[a]) : this[a];
			});
		};
	});
	
	$.fn.cssText = function(){
		return this.filter('link,style').eq(0).sheet().cssRules().map(function(){
			return $.rule.outerText(this);							   
		}).get().join('\n');
	};
	
	$.each('remove,appendTo,parent'.split(','),function( k, f ){
		$.rule.fn[f] = function(){
			var args = $.makeArray(arguments), that = this;
			args.unshift(null);
			return this.each(function( i ){
				args[0] = this;
				that[i] = $.rule[f].apply( $.rule, args ) || that[i];
			});
		};
	});
		
	$.each(('each,index,setArray,get,size,not,eq,slice,map,attr,andSelf,css,queue,dequeue,stop,animate').split(','),function( k, f ){
		$.rule.fn[f] = $.fn[f];																				  
	});
	
	var curCSS = $.curCSS;
	$.curCSS = function( e, a, f ){
		return e.selectorText ? e.style[a] : curCSS.call($,e,a,f);
	};
	
})( jQuery );
