<?xml version="1.0"?>
<bindings xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="XBLDocument" applyauthorstyles="false">
	<implementation>
		<constructor>
			//<![CDATA[
			
				/******************************************
				
				Fixes Generated Content bugs on Firefox 2.0 and lower (Gecko 1.8 and lower)
				
				Use: Link this file through -moz-binding: url(moz-gcontent-fix.xml#XBLDocument) on
				selector you want to be fixed while using GC there.
				
				Example:
				
				--> #wrapper p { -moz-binding: ... }
				#wrapper p::after { generic CSS rules ... }
				...
				
				
				Author: Piotr 'Riddle' Petrus
				WWW: http://riddle.pl
				
				Licence: Creative Commons Attribution-ShareAlike 2.5
				http://creativecommons.org/licenses/by-sa/2.5/deed.en
				
				Version: 0.1
				
				Known bugs: 
					- inheritance bugs (spans' classnames need to be more specific)
					- combined GC selectors	+ separated = problem
				
				*******************************************/

				function  initMozGCFix(obj) {
					function reRender() {
						doMozGCFix(obj);
					}
					setTimeout(reRender, 0);
				}//initMozGCFix

				function insertAfter(newElement, targetElement) {
					var parent = targetElement.parentNode;
					if (parent.lastChild == targetElement) {
						parent.appendChild(newElement);
					} else {
						parent.insertBefore(newElement, targetElement.nextSibling);
					}
				}

				function trim(s){
					return s.replace(/^\s+|\s+$/g, '');
				}

				var str = /"[^"]+"/;
				var attr = /attr\([^\)]+\)/i;	
				var url = /url\([^\)]+\)/i;

				function parseContent(content, elem) {
					var results = new Array();
					var i = 0;

					while ((content.match(str)) || (content.match(attr)) || (content.match(url))) {
						//string?
						var strtest = /^"[^"]+"/i.exec(trim(content));
						if ((strtest) && (strtest.length > 0)) {
							results[i] = strtest[0];
							content = content.replace(str, '');
							i++;
							continue;
						}
						//attr()?
						var attrtest = /^attr\([^\)]+\)/i.exec(trim(content));
						if ((attrtest) && (attrtest.length > 0)) {
							results[i] = attrtest[0];
							content = content.replace(attr, '');
							i++;
							continue;
						}
						//url()?
						var urltest = /^url\([^\)]+\)/i.exec(trim(content));
						if ((urltest) && (urltest.length > 0)) {
							results[i] = urltest[0];
							content = content.replace(url, '');
							i++;
							continue;
						}
					}
					
					var c = '';
					
					for (var i = 0; i < results.length; i++) {
						if (results[i].match(str)) {
							results[i] = results[i].substring(1, results[i].length - 1);
						}
						if (results[i].match(attr)) {
							var atr = elem.getAttribute(results[i].substring(5, results[i].length - 1));
							if (atr) { results[i] = atr; } else { results[i] = ''; }
						}
						if (results[i].match(url)) {
							var uri = '<img src="' + results[i].substring(4, results[i].length - 1) + '" />';
							results[i] = uri;
						}
						c += results[i];
					}
					
					return c;
				}

				function doMozGCFix(currElem) {

					if (typeof document.parsed == 'undefined') document.parsed = false;

					// these will be used later to trick getPropertyValue
					var dirtyProp = '-moz-user-select';
					var dirtyValue = '-moz-all';
					var gcClassName = 'moz-generated-';

					currElemSelector = '';

					// take all stylesheets applied to document
					var allStylesheets = document.styleSheets;

					// seeking for currElem selector text
					for (var i = 0; i < allStylesheets.length; i++) {
						// check if stylesheet is available
						if (!allStylesheets.disabled) {
							// take all rules in current stylsheet
							var allRules = allStylesheets[i].cssRules;
							for (var j = 0; j < allRules.length; j++) {
								// check if current rule has any XBL Binding and is worth to be fully-checked
								if (allRules[j].style.getPropertyValue('-moz-binding') != '') {
									// apply rare and parser-compilant css property to actual css rule
									allStylesheets[i].insertRule(allRules[j].selectorText + ' { ' + dirtyProp + ': ' + dirtyValue  + '; }', j + 1);
									// check if this element has new property
									var xbltest = document.defaultView.getComputedStyle(currElem, null).getPropertyValue(dirtyProp);
									if (xbltest == dirtyValue) {
										// we have a winner! now remove extra rule
										allStylesheets[i].deleteRule(j + 1);
										// break loops, we have found currElem selector text
										currElemSelector = allRules[j].selectorText;
									}
								}
								if (currElemSelector != '') break;
							} // endfor
						}
						if (currElemSelector != '') break;
					} // endfor

					// seeking for GC selectors complementary with currElem selector
					for (var i = 0; i < allStylesheets.length; i++) {
						// check if stylesheet is available
						if (!allStylesheets.disabled) {
							// take all rules in current stylsheet
							var allRules = allStylesheets[i].cssRules;
							for (var j = 0; j < allRules.length; j++) {
								// reset GC selectors check
								var nAfter = { e: false, m: false };
								var nBefore = { e: false, m: false };
								// get all selectors in current rule
								var allSelectors = allRules[j].selectorText.split(',');
								var af = /\:?\:after/;
								var bf = /\:?\:before/;
								for (var k = 0; k < allSelectors.length; k++) {
									var currSelector = trim(allSelectors[k]);
									if ((currSelector != currElemSelector) && (currSelector.indexOf(gcClassName) == -1) && (currSelector.indexOf(currElemSelector) > -1)) {
										if (currSelector.match(af)) nAfter.e = true;
										if (currSelector.match(bf)) nBefore.e = true;
									}
								} // endfor
								var newCSS = '';
								if (nAfter.e && nBefore.e) {
									newCSS = allRules[j].cssText;
									newCSS = newCSS.replace(af, ' span.' + gcClassName + 'after' + i + j);
									newCSS = newCSS.replace(bf, ' span.' + gcClassName + 'before' + i + j);
									allStylesheets[i].deleteRule(j);
									allStylesheets[i].insertRule(newCSS, j);
								}
								if (nAfter.e && !nBefore.e) {
									newCSS = allRules[j].cssText;
									newCSS = newCSS.replace(af, ' span.' + gcClassName + 'after' + i + j);
									allStylesheets[i].deleteRule(j);
									allStylesheets[i].insertRule(newCSS, j);
								}
								if (!nAfter.e && nBefore.e) {
									newCSS = allRules[j].cssText;
									newCSS = newCSS.replace(bf, ' span.' + gcClassName + 'before' + i + j);
									allStylesheets[i].deleteRule(j);
									allStylesheets[i].insertRule(newCSS, j);
								}
							}
						}
					} // endfor
					
					// seeking for GC selectors complementary with currElem selector
					for (var i = 0; i < allStylesheets.length; i++) {
						// check if stylesheet is available
						if (!allStylesheets.disabled) {
							// take all rules in current stylsheet
							var allRules = allStylesheets[i].cssRules;
							for (var j = 0; j < allRules.length; j++) {
								var nAfter = { e: false, m: false };
								var nBefore = { e: false, m: false };
								// get all selectors in current rule
								var allSelectors = allRules[j].selectorText.split(',');
								var af = /-after/;
								var bf = /-before/;
								for (var k = 0; k < allSelectors.length; k++) {
									var currSelector = trim(allSelectors[k]);
									if ((currSelector != currElemSelector) && (currSelector.indexOf(currElemSelector) > -1)) {
										if (currSelector.match(af)) nAfter.m = true;
										if (currSelector.match(bf)) nBefore.m = true;
									}
								} // endfor
								if (nAfter.m && nBefore.m) {
									var node1 = document.createElement('span');
									var node2 = document.createElement('span');					
									content = parseContent(allRules[j].style.getPropertyValue('content'), currElem);
									/* node creation */
									node1.innerHTML = content;
									node2.innerHTML = content;					
									node1.className = gcClassName + 'before' + i + j;
									currElem.insertBefore(node1, currElem.firstChild);					
									node2.className = gcClassName + 'after' + i + j;
									insertAfter(node2, currElem.lastChild);
								}
								if (nAfter.m && !nBefore.m) {
									var node1 = document.createElement('span');					
									content = parseContent(allRules[j].style.getPropertyValue('content'), currElem);
									/* node creation */
									node1.innerHTML = content;
									node1.className = gcClassName + 'after' + i + j;
									insertAfter(node1, currElem.lastChild);
								}
								if (!nAfter.m && nBefore.m) {
									var node1 = document.createElement('span');
									content = parseContent(allRules[j].style.getPropertyValue('content'), currElem);
									/* node creation */
									node1.innerHTML = content;
									node1.className = gcClassName + 'before' + i + j;
									currElem.insertBefore(node1, currElem.firstChild);
								}
							}
						}
					} // endfor
				}//doMozGCFix
			
				initMozGCFix(this);
			
			//]]>
		</constructor>
	</implementation>
</binding>

</bindings>