// source --> https://www.wessexdoors.co.uk/wp-content/themes/wessexdoors/js/svgxuse.js?ver=1 
/*!
 * @copyright Copyright (c) 2017 IcoMoon.io
 * @license   Licensed under MIT license
 *            See https://github.com/Keyamoon/svgxuse
 * @version   1.2.6
 */
/*jslint browser: true */
/*global XDomainRequest, MutationObserver, window */
(function () {
    "use strict";
    if (typeof window !== "undefined" && window.addEventListener) {
        var cache = Object.create(null); // holds xhr objects to prevent multiple requests
        var checkUseElems;
        var tid; // timeout id
        var debouncedCheck = function () {
            clearTimeout(tid);
            tid = setTimeout(checkUseElems, 100);
        };
        var unobserveChanges = function () {
            return;
        };
        var observeChanges = function () {
            var observer;
            window.addEventListener("resize", debouncedCheck, false);
            window.addEventListener("orientationchange", debouncedCheck, false);
            if (window.MutationObserver) {
                observer = new MutationObserver(debouncedCheck);
                observer.observe(document.documentElement, {
                    childList: true,
                    subtree: true,
                    attributes: true
                });
                unobserveChanges = function () {
                    try {
                        observer.disconnect();
                        window.removeEventListener("resize", debouncedCheck, false);
                        window.removeEventListener("orientationchange", debouncedCheck, false);
                    } catch (ignore) {}
                };
            } else {
                document.documentElement.addEventListener("DOMSubtreeModified", debouncedCheck, false);
                unobserveChanges = function () {
                    document.documentElement.removeEventListener("DOMSubtreeModified", debouncedCheck, false);
                    window.removeEventListener("resize", debouncedCheck, false);
                    window.removeEventListener("orientationchange", debouncedCheck, false);
                };
            }
        };
        var createRequest = function (url) {
            // In IE 9, cross origin requests can only be sent using XDomainRequest.
            // XDomainRequest would fail if CORS headers are not set.
            // Therefore, XDomainRequest should only be used with cross origin requests.
            function getOrigin(loc) {
                var a;
                if (loc.protocol !== undefined) {
                    a = loc;
                } else {
                    a = document.createElement("a");
                    a.href = loc;
                }
                return a.protocol.replace(/:/g, "") + a.host;
            }
            var Request;
            var origin;
            var origin2;
            if (window.XMLHttpRequest) {
                Request = new XMLHttpRequest();
                origin = getOrigin(location);
                origin2 = getOrigin(url);
                if (Request.withCredentials === undefined && origin2 !== "" && origin2 !== origin) {
                    Request = XDomainRequest || undefined;
                } else {
                    Request = XMLHttpRequest;
                }
            }
            return Request;
        };
        var xlinkNS = "http://www.w3.org/1999/xlink";
        checkUseElems = function () {
            var base;
            var bcr;
            var fallback = ""; // optional fallback URL in case no base path to SVG file was given and no symbol definition was found.
            var hash;
            var href;
            var i;
            var inProgressCount = 0;
            var isHidden;
            var Request;
            var url;
            var uses;
            var xhr;
            function observeIfDone() {
                // If done with making changes, start watching for chagnes in DOM again
                inProgressCount -= 1;
                if (inProgressCount === 0) { // if all xhrs were resolved
                    unobserveChanges(); // make sure to remove old handlers
                    observeChanges(); // watch for changes to DOM
                }
            }
            function attrUpdateFunc(spec) {
                return function () {
                    if (cache[spec.base] !== true) {
                        spec.useEl.setAttributeNS(xlinkNS, "xlink:href", "#" + spec.hash);
                        if (spec.useEl.hasAttribute("href")) {
                            spec.useEl.setAttribute("href", "#" + spec.hash);
                        }
                    }
                };
            }
            function onloadFunc(xhr) {
                return function () {
                    var body = document.body;
                    var x = document.createElement("x");
                    var svg;
                    xhr.onload = null;
                    x.innerHTML = xhr.responseText;
                    svg = x.getElementsByTagName("svg")[0];
                    if (svg) {
                        svg.setAttribute("aria-hidden", "true");
                        svg.style.position = "absolute";
                        svg.style.width = 0;
                        svg.style.height = 0;
                        svg.style.overflow = "hidden";
                        body.insertBefore(svg, body.firstChild);
                    }
                    observeIfDone();
                };
            }
            function onErrorTimeout(xhr) {
                return function () {
                    xhr.onerror = null;
                    xhr.ontimeout = null;
                    observeIfDone();
                };
            }
            unobserveChanges(); // stop watching for changes to DOM
            // find all use elements
            uses = document.getElementsByTagName("use");
            for (i = 0; i < uses.length; i += 1) {
                try {
                    bcr = uses[i].getBoundingClientRect();
                } catch (ignore) {
                    // failed to get bounding rectangle of the use element
                    bcr = false;
                }
                href = uses[i].getAttribute("href")
                        || uses[i].getAttributeNS(xlinkNS, "href")
                        || uses[i].getAttribute("xlink:href");
                if (href && href.split) {
                    url = href.split("#");
                } else {
                    url = ["", ""];
                }
                base = url[0];
                hash = url[1];
                isHidden = bcr && bcr.left === 0 && bcr.right === 0 && bcr.top === 0 && bcr.bottom === 0;
                if (bcr && bcr.width === 0 && bcr.height === 0 && !isHidden) {
                    // the use element is empty
                    // if there is a reference to an external SVG, try to fetch it
                    // use the optional fallback URL if there is no reference to an external SVG
                    if (fallback && !base.length && hash && !document.getElementById(hash)) {
                        base = fallback;
                    }
                    if (uses[i].hasAttribute("href")) {
                        uses[i].setAttributeNS(xlinkNS, "xlink:href", href);
                    }
                    if (base.length) {
                        // schedule updating xlink:href
                        xhr = cache[base];
                        if (xhr !== true) {
                            // true signifies that prepending the SVG was not required
                            setTimeout(attrUpdateFunc({
                                useEl: uses[i],
                                base: base,
                                hash: hash
                            }), 0);
                        }
                        if (xhr === undefined) {
                            Request = createRequest(base);
                            if (Request !== undefined) {
                                xhr = new Request();
                                cache[base] = xhr;
                                xhr.onload = onloadFunc(xhr);
                                xhr.onerror = onErrorTimeout(xhr);
                                xhr.ontimeout = onErrorTimeout(xhr);
                                xhr.open("GET", base);
                                xhr.send();
                                inProgressCount += 1;
                            }
                        }
                    }
                } else {
                    if (!isHidden) {
                        if (cache[base] === undefined) {
                            // remember this URL if the use element was not empty and no request was sent
                            cache[base] = true;
                        } else if (cache[base].onload) {
                            // if it turns out that prepending the SVG is not necessary,
                            // abort the in-progress xhr.
                            cache[base].abort();
                            delete cache[base].onload;
                            cache[base] = true;
                        }
                    } else if (base.length && cache[base]) {
                        setTimeout(attrUpdateFunc({
                            useEl: uses[i],
                            base: base,
                            hash: hash
                        }), 0);
                    }
                }
            }
            uses = "";
            inProgressCount += 1;
            observeIfDone();
        };
        var winLoad;
        winLoad = function () {
            window.removeEventListener("load", winLoad, false); // to prevent memory leaks
            tid = setTimeout(checkUseElems, 0);
        };
        if (document.readyState !== "complete") {
            // The load event fires when all resources have finished loading, which allows detecting whether SVG use elements are empty.
            window.addEventListener("load", winLoad, false);
        } else {
            // No need to add a listener if the document is already loaded, initialize immediately.
            winLoad();
        }
    }
}());
// source --> https://www.wessexdoors.co.uk/wp-content/themes/wessexdoors/js/ie11.js?ver=1 
/*! ie11CustomProperties.js v3.0.6 | MIT License | https://git.io/fjXMN */
!function () {
	'use strict';

	// check for support
	var testEl = document.createElement('i');
	testEl.style.setProperty('--x', 'y');
	if (testEl.style.getPropertyValue('--x') === 'y' || !testEl.msMatchesSelector) return;

	if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector;

    var listeners = [],
        root = document,
        Observer;

	function qsa(el, selector){
		try {
			return el.querySelectorAll(selector);
		} catch(e) {
			// console.warn('the Selector '+selector+' can not be parsed');
			return [];
		}
	}
    function onElement (selector, callback) {
        var listener = {
            selector: selector,
            callback: callback,
            elements: new WeakMap(),
        };
		var els = qsa(root, listener.selector), i=0, el;
		while (el = els[i++]) {
            listener.elements.set(el, true);
            listener.callback.call(el, el);
        }
        listeners.push(listener);
        if (!Observer) {
            Observer = new MutationObserver(checkMutations);
            Observer.observe(root, {
                childList: true,
                subtree: true
            });
        }
        checkListener(listener);
    };
    function checkListener(listener, target) {
        var i = 0, el, els = [];
		try {
			target && target.matches(listener.selector) && els.push(target);
		} catch(e) {}
        if (loaded) { // ok? check inside node on innerHTML - only when loaded
            Array.prototype.push.apply(els, qsa(target || root, listener.selector));
        }
        while (el = els[i++]) {
            if (listener.elements.has(el)) continue;
            listener.elements.set(el,true);
            listener.callback.call(el, el);
        }
    }
    function checkListeners(inside) {
        var i = 0, listener;
        while (listener = listeners[i++]) checkListener(listener, inside);
    }
    function checkMutations(mutations) {
		var j = 0, i, mutation, nodes, target;
        while (mutation = mutations[j++]) {
            nodes = mutation.addedNodes, i = 0;
            while (target = nodes[i++]) target.nodeType === 1 && checkListeners(target);
        }
    }

    var loaded = false;
    document.addEventListener('DOMContentLoaded', function () {
        loaded = true;
    });

	// svg polyfills
	function copyProperty(prop, from, to){
		var desc = Object.getOwnPropertyDescriptor(from, prop);
		Object.defineProperty(to, prop, desc);
	}
	if (!('classList' in Element.prototype)) {
		copyProperty('classList', HTMLElement.prototype, Element.prototype);
	}
	if (!('innerHTML' in Element.prototype)) {
		copyProperty('innerHTML', HTMLElement.prototype, Element.prototype);
	}
	if (!('sheet' in SVGStyleElement.prototype)) {
		Object.defineProperty(SVGStyleElement.prototype, 'sheet', {
			get:function(){
				var all = document.styleSheets, i=0, sheet;
				while (sheet=all[i++]) {
					if (sheet.ownerNode === this) return sheet;
				}

			}
		});
	}


	// main logic

	// cached regexps, better performance
	const regFindSetters = /([\s{;])(--([A-Za-z0-9-_]*)\s*:([^;!}{]+)(!important)?)(?=\s*([;}]|$))/g;
	const regFindGetters = /([{;]\s*)([A-Za-z0-9-_]+\s*:[^;}{]*var\([^!;}{]+)(!important)?(?=\s*([;}$]|$))/g;
	const regRuleIEGetters = /-ieVar-([^:]+):/g
	const regRuleIESetters = /-ie-([^};]+)/g
	//const regHasVar = /var\(/;
	const regPseudos = /:(hover|active|focus|target|:before|:after|:first-letter|:first-line)/;

	onElement('link[rel="stylesheet"]', function (el) {
		fetchCss(el.href, function (css) {
			var newCss = rewriteCss(css);
			if (css === newCss) return;
			newCss = relToAbs(el.href, newCss);
			el.disabled = true;
			var style = document.createElement('style');
			if (el.media) style.setAttribute('media', el.media);
			el.parentNode.insertBefore(style, el);
			activateStyleElement(style, newCss);
		});
	});

	function foundStyle(el){
		if (el.ieCP_polyfilled) return;
		if (el.ieCP_elementSheet) return;
		var css = el.innerHTML;
		var newCss = rewriteCss(css);
		if (css === newCss) return;
		activateStyleElement(el, newCss);
	}
	onElement('style', foundStyle);
	// immediate, to pass w3c-tests, bud its a bad idea
	// addEventListener('DOMNodeInserted',function(e){ e.target.tagName === 'STYLE' && foundStyle(e.target); });



	onElement('[ie-style]', function (el) {
		var newCss = rewriteCss('{'+el.getAttribute('ie-style')).substr(1);
		el.style.cssText += ';'+ newCss;
		var found = parseRewrittenStyle(el.style);
		if (found.getters) addGetterElement(el, found.getters, '%styleAttr');
		if (found.setters) addSetterElement(el, found.setters);
	});

	function relToAbs(base, css) {
		return css.replace(/url\(([^)]+)\)/g, function($0, $1){
			$1 = $1.trim().replace(/(^['"]|['"]$)/g,'');
			if ($1.match(/^([a-z]+:|\/)/)) return $0;
			base = base.replace(/\?.*/,'');
			return 'url('+ base + './../' + $1 +')';
		});
	}

	// ie has a bug, where unknown properties at pseudo-selectors are computed at the element
	// #el::after { -content:'x'; } => getComputedStyle(el)['-content'] == 'x'
	// should we add something like -ieVar-pseudo_after-content:'x'?
	function rewriteCss(css) {

		/* uncomment if spec finished and needed by someone
		css = css.replace(/@property ([^{]+){([^}]+)}/, function($0, prop, body){
			prop = prop.trim();
			const declaration = {name:prop};
			body.split(';').forEach(function(pair){
				const x = pair.split(':');
				if (x[1]) declaration[ x[0].trim() ] = x[1];
			});
			declaration['inherits'] = declaration['inherits'].trim()==='true' ? true : false;
			declaration['initialValue'] = declaration['initial-value'];
			CSS.registerProperty(declaration)
			return '/*\n @property ... removed \n*'+'/';
		});
		*/
		return css.replace(regFindSetters, function($0, $1, $2, $3, $4, important){
			return $1+'-ie-'+(important?'❗':'')+$3+':'+encodeValue($4);
		}).replace(regFindGetters, function($0, $1, $2, important){
			return $1+'-ieVar-'+(important?'❗':'')+$2+'; '+$2; // keep the original, so chaining works "--x:var(--y)"
		});
	}
	function encodeValue(value){
		return value;
		return value.replace(/ /g,'␣');
	}
	const keywords = {initial:1,inherit:1,revert:1,unset:1};
	function decodeValue(value){
		return value;
		if (value===undefined) return;
		value =  value.replace(/␣/g,' ');
		const trimmed = value.trim();
		if (keywords[trimmed]) return trimmed;
		return value;
	}

	// beta
	const styles_of_getter_properties = {};

	function parseRewrittenStyle(style) { // less memory then parameter cssText?

		// beta
		style['z-index']; // ie11 can access unknown properties in stylesheets only if accessed a dashed known property

		const cssText = style.cssText;
		var matchesGetters = cssText.match(regRuleIEGetters), j, match;
		if (matchesGetters) {
			var getters = []; // eg. [border,color]
			for (j = 0; match = matchesGetters[j++];) {
				let propName = match.slice(7, -1);
				if (propName[0] === '❗') propName = propName.substr(1);
				getters.push(propName);

				// beta
				if (!styles_of_getter_properties[propName]) styles_of_getter_properties[propName] = [];
				styles_of_getter_properties[propName].push(style);
			}
		}
		var matchesSetters = cssText.match(regRuleIESetters);
		if (matchesSetters) {
			var setters = {}; // eg. [--color:#fff, --padding:10px];
			for (j = 0; match = matchesSetters[j++];) {
				let x = match.substr(4).split(':');
				let propName = x[0];
				let propValue = x[1];
				if (propName[0] === '❗') propName = propName.substr(1);
				setters[propName] = propValue;
			}
		}
		return {getters:getters, setters:setters};
	}
	function activateStyleElement(style, css) {
		style.innerHTML = css;
		style.ieCP_polyfilled = true;
		var rules = style.sheet.rules, i=0, rule; // cssRules = CSSRuleList, rules = MSCSSRuleList
		while (rule = rules[i++]) {
			const found = parseRewrittenStyle(rule.style);
			if (found.getters) addGettersSelector(rule.selectorText, found.getters);
			if (found.setters) addSettersSelector(rule.selectorText, found.setters);

			// mediaQueries: redraw the hole document
			// better add events for each element?
			const media = rule.parentRule && rule.parentRule.media && rule.parentRule.media.mediaText;
			if (media && (found.getters || found.setters)) {
				matchMedia(media).addListener(function(){
					drawTree(document.documentElement)
				})
			}
		}

		// beta
		redrawStyleSheets()
	}

	function addGettersSelector(selector, properties) {
		selectorAddPseudoListeners(selector);
		onElement(unPseudo(selector), function (el) {
			addGetterElement(el, properties, selector);
			drawElement(el);
		});
	}
	function addGetterElement(el, properties, selector) {
		var i=0, prop, j;
		const selectors = selector.split(','); // split grouped selectors
		el.setAttribute('iecp-needed', true);
		if (!el.ieCPSelectors) el.ieCPSelectors = {};
		while (prop = properties[i++]) {
			for (j = 0; selector = selectors[j++];) {
				const parts = selector.trim().split('::');
				if (!el.ieCPSelectors[prop]) el.ieCPSelectors[prop] = [];
				el.ieCPSelectors[prop].push({
					selector: parts[0],
					pseudo: parts[1] ? '::' + parts[1] : ''
				});
			}
		}
	}
	function addSettersSelector(selector, propVals) {
		selectorAddPseudoListeners(selector);
		onElement(unPseudo(selector), function (el) {
			addSetterElement(el, propVals);
		});
	}
	function addSetterElement(el, propVals) {
		if (!el.ieCP_setters) el.ieCP_setters = {};
		for (var prop in propVals) { // eg. {foo:#fff, bar:baz}
			el.ieCP_setters['--' + prop] = 1;
		}
		drawTree(el);
	}

	//beta
	function redrawStyleSheets() {
		for (var prop in styles_of_getter_properties) {
			let styles = styles_of_getter_properties[prop];
			for (var i=0, style; style=styles[i++];) {
				if (style.owningElement) continue;
				var value = style['-ieVar-'+prop];
				if (!value) continue;
				value = styleComputeValueWidthVars(getComputedStyle(document.documentElement), value);
				if (value === '') continue;
				try {
					style[prop] = value;
				} catch(e) {}
			}
		}
	}


	const pseudos = {
		hover:{
			on:'mouseenter',
			off:'mouseleave'
		},
		focus:{
			on:'focusin',
			off:'focusout'
		},
		active:{
			on:'CSSActivate',
			off:'CSSDeactivate'
		},
	};
	function selectorAddPseudoListeners(selector){
		// ie11 has the strange behavoir, that groups of selectors are individual rules, but starting with the full selector:
		// td, th, button { color:red } results in this rules:
		// "td, th, button" | "th, th" | "th"
		selector = selector.split(',')[0];
		for (var pseudo in pseudos) {
			var parts = selector.split(':'+pseudo);
			if (parts.length > 1) {
				var ending = parts[1].match(/^[^\s]*/); // ending elementpart of selector (used for not(:active))
				let sel = unPseudo(parts[0]+ending);
				const listeners = pseudos[pseudo];
				onElement(sel, function (el) {
					el.addEventListener(listeners.on, drawTreeEvent);
					el.addEventListener(listeners.off, drawTreeEvent);
				});
			}
		}
	}
	let CSSActive = null;
	document.addEventListener('mousedown',function(e){
		setTimeout(function(){
			if (e.target === document.activeElement) {
				var evt = document.createEvent('Event');
				evt.initEvent('CSSActivate', true, true);
				CSSActive = e.target;
				CSSActive.dispatchEvent(evt);
			}
		})
	});
	document.addEventListener('mouseup',function(){
		if (CSSActive) {
			var evt = document.createEvent('Event');
			evt.initEvent('CSSDeactivate', true, true);
			CSSActive.dispatchEvent(evt);
			CSSActive = null;
		}
	});

	function unPseudo(selector){
		return selector.replace(regPseudos,'').replace(':not()','');
	}

	var uniqueCounter = 0;

	/* old *
	function _drawElement(el) {
		if (!el.ieCP_unique) { // use el.uniqueNumber? but needs class for the css-selector => test performance
			el.ieCP_unique = ++uniqueCounter;
			el.classList.add('iecp-u' + el.ieCP_unique);
		}
		var style = getComputedStyle(el);
		if (el.ieCP_sheet) while (el.ieCP_sheet.rules[0]) el.ieCP_sheet.deleteRule(0);
		for (var prop in el.ieCPSelectors) {
			var important = style['-ieVar-❗' + prop];
			let valueWithVar = important || style['-ieVar-' + prop];
			if (!valueWithVar) continue; // todo, what if '0'

			var details = {};
			var value = styleComputeValueWidthVars(style, valueWithVar, details);

			if (important) value += ' !important';
			for (var i=0, item; item=el.ieCPSelectors[prop][i++];) { // todo: split and use requestAnimationFrame?
				if (item.selector === '%styleAttr') {
					el.style[prop] = value;
				} else {

					// beta
					if (!important && details.allByRoot !== false) continue; // dont have to draw root-properties

					//let selector = item.selector.replace(/>? \.[^ ]+/, ' ', item.selector); // todo: try to equalize specificity
					let selector = item.selector;
					elementStyleSheet(el).insertRule(selector + '.iecp-u' + el.ieCP_unique + item.pseudo + ' {' + prop + ':' + value + '}', 0);
				}
			}
		}
	}
	function elementStyleSheet(el){
		if (!el.ieCP_sheet) {
			const styleEl = document.createElement('style');
			styleEl.ieCP_elementSheet = 1;
			//el.appendChild(styleEl); // yes! self-closing tags can have style as children, but - if i set innerHTML, the stylesheet is lost
			document.head.appendChild(styleEl);
			el.ieCP_sheet = styleEl.sheet;
		}
		return el.ieCP_sheet;
	}

	/* */
	function _drawElement(el) {
		if (!el.ieCP_unique) { // use el.uniqueNumber? but needs class for the css-selector => test performance
			el.ieCP_unique = ++uniqueCounter;
			el.classList.add('iecp-u' + el.ieCP_unique);
		}
		var style = getComputedStyle(el);
		let css = '';
		for (var prop in el.ieCPSelectors) {
			var important = style['-ieVar-❗' + prop];
			let valueWithVar = important || style['-ieVar-' + prop];
			if (!valueWithVar) continue; // todo, what if '0'
			var details = {};
			var value = styleComputeValueWidthVars(style, valueWithVar, details);
			//if (value==='initial') value = initials[prop];
			if (important) value += ' !important';
			for (var i=0, item; item=el.ieCPSelectors[prop][i++];) { // todo: split and use requestAnimationFrame?
				if (item.selector === '%styleAttr') {
					el.style[prop] = value;
				} else {

					// beta
					if (!important && details.allByRoot !== false) continue; // dont have to draw root-properties

					//let selector = item.selector.replace(/>? \.[^ ]+/, ' ', item.selector); // todo: try to equalize specificity
					let selector = item.selector;
					css += selector + '.iecp-u' + el.ieCP_unique + item.pseudo + '{' + prop + ':' + value + '}\n';
				}
			}
		}
		elementSetCss(el, css);
	}
	function elementSetCss(el, css){
		if (!el.ieCP_styleEl && css) {
			const styleEl = document.createElement('style');
			styleEl.ieCP_elementSheet = 1;
			//el.appendChild(styleEl); // yes! self-closing tags can have style as children, but - if i set innerHTML, the stylesheet is lost
			document.head.appendChild(styleEl);
			el.ieCP_styleEl = styleEl;
		}
		if (el.ieCP_styleEl) el.ieCP_styleEl.innerHTML = css;
	}
	/* */

	function drawTree(target) {
		if (!target) return;
		var els = target.querySelectorAll('[iecp-needed]');
		if (target.hasAttribute && target.hasAttribute('iecp-needed')) drawElement(target); // self
		for (var i = 0, el; el = els[i++];) drawElement(el); // tree
	}
	// draw queue
	let drawQueue = new Set();
	let collecting = false;
	let drawing = false;
	function drawElement(el){
		drawQueue.add(el);
		if (collecting) return;
		collecting = true;
		requestAnimationFrame(function(){
		//setImmediate(function(){
			collecting = false;
			drawing = true;
			drawQueue.forEach(_drawElement);
			drawQueue.clear();
			setTimeout(function(){ // mutationObserver will trigger delayed, requestAnimationFrame will miss some changes
				drawing = false;
			})
		})
	}


	function drawTreeEvent(e) {
		drawTree(e.target)
	}

	function findVars(str, cb){ // css value parser
		let level=0, openedLevel=null, lastPoint=0, newStr = '', i=0, char, insideCalc;
		while (char=str[i++]) {
			if (char === '(') {
				++level;
				if (openedLevel === null && str[i-4]+str[i-3]+str[i-2] === 'var') {
					openedLevel = level;
					newStr += str.substring(lastPoint, i-4);
					lastPoint = i;
				}
				if (str[i-5]+str[i-4]+str[i-3]+str[i-2] === 'calc') {
					insideCalc = level;
				}
			}
			if (char === ')' && openedLevel === level) {
				let variable = str.substring(lastPoint, i-1).trim(), fallback;
				let x = variable.indexOf(',');
				if (x!==-1) {
					fallback = variable.slice(x+1);
					variable = variable.slice(0,x);
				}
				newStr += cb(variable, fallback, insideCalc);
				lastPoint = i;
				openedLevel = null;
			}
			if (char === ')') {
				--level;
				if (insideCalc === level) insideCalc = null;
			}
		}
		newStr += str.substring(lastPoint);
		return newStr;
	}
	function styleComputeValueWidthVars(style, valueWithVars, details){
		return findVars(valueWithVars, function(variable, fallback, insideCalc){
			var value = style.getPropertyValue(variable);
			if (insideCalc) value = value.replace(/^calc\(/, '('); // prevent nested calc
			if (details && style.lastPropertyServedBy !== document.documentElement) details.allByRoot = false;
			if (value==='' && fallback) value = styleComputeValueWidthVars(style, fallback, details);
			return value;
		});
	}

	// mutation listener
	var observer = new MutationObserver(function(mutations) {
		if (drawing) return;
		for (var i=0, mutation; mutation=mutations[i++];) {
			if (mutation.attributeName === 'iecp-needed') continue; // why?
			// recheck all selectors if it targets new elements?
			drawTree(mutation.target);
		}
	});
	setTimeout(function(){
		observer.observe(document,{attributes: true, subtree: true });
	})

	// :target listener
	var oldHash = location.hash
	addEventListener('hashchange',function(e){
		var newEl = document.getElementById(location.hash.substr(1));
		if (newEl) {
			var oldEl = document.getElementById(oldHash.substr(1));
			drawTree(newEl);
			drawTree(oldEl);
		} else {
			drawTree(document);
		}
		oldHash = location.hash;
	});

	// add owningElement to Element.style
	var descriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'style');
	var styleGetter = descriptor.get;
	descriptor.get = function () {
		const style = styleGetter.call(this);
		style.owningElement = this;
		return style;
	}
	Object.defineProperty(HTMLElement.prototype, 'style', descriptor);

	// add computedFor to computed style-objects
	var originalGetComputed = getComputedStyle;
	window.getComputedStyle = function (el) {
		var style = originalGetComputed.apply(this, arguments);
		style.computedFor = el;
		//style.pseudoElt = pseudoElt; //not needed at the moment
		return style;
	}

	// getPropertyValue / setProperty hooks
	const StyleProto = CSSStyleDeclaration.prototype;

	const oldGetP = StyleProto.getPropertyValue;
	StyleProto.getPropertyValue = function (property) {
		this.lastPropertyServedBy = false;
		property = property.trim();

		/* *
		if (this.owningElement) {
			const ieProperty = '-ieVar-'+property;
			const iePropertyImportant = '-ieVar-❗'+property;
			let value = this[iePropertyImportant] || this[ieProperty];
			if (value !== undefined) {
				// todo, test if syntax valid
				return value;
			}
		}
		/* */

		if (property[0] !== '-' || property[1] !== '-') return oldGetP.apply(this, arguments);
		const undashed = property.substr(2);
		const ieProperty = '-ie-'+undashed;
		const iePropertyImportant = '-ie-❗'+undashed;
		let value = decodeValue(this[iePropertyImportant] || this[ieProperty]);

		if (this.computedFor) { // computedStyle
			if (value !== undefined && !inheritingKeywords[value]) {
				//if (regHasVar.test(value))  // todo: to i need this check?!!! i think its faster without
					value = styleComputeValueWidthVars(this, value);
				this.lastPropertyServedBy = this.computedFor;
			} else { // inherited
				if (inheritingKeywords[value] || !register[property] || register[property].inherits) {
					//let el = this.pseudoElt ? this.computedFor : this.computedFor.parentNode;
					let el = this.computedFor.parentNode;
					while (el.nodeType === 1) {
						// how slower would it be to getComputedStyle for every element, not just with defined ieCP_setters
						if (el.ieCP_setters && el.ieCP_setters[property]) {
							// i could make
							// value = el.nodeType ? getComputedStyle(this.computedFor.parentNode).getPropertyValue(property)
							// but i fear performance, stupid?
							var style = getComputedStyle(el);
							var tmpVal = decodeValue(style[iePropertyImportant] || style[ieProperty]);
							if (tmpVal !== undefined) {
								// calculated style from current element not from the element the value was inherited from! (style, value)
								//value = tmpVal; if (regHasVar.test(tmpVal))  // todo: to i need this check?!!! i think its faster without
									value = styleComputeValueWidthVars(this, tmpVal);
								this.lastPropertyServedBy = el;
								break;
							}
						}
						el = el.parentNode;
					}
				}
			}
			if (value==='initial') return '';
		}
		//if ((value === undefined || value === 'initial') && register[property]) value = register[property].initialValue; // todo?
		if (value === undefined && register[property]) value = register[property].initialValue;
		if (value === undefined) return '';
		return value;
	};
	const inheritingKeywords = {inherit:1,revert:1,unset:1};

	const oldSetP = StyleProto.setProperty;
	StyleProto.setProperty = function (property, value, prio) {
		if (property[0] !== '-' || property[1] !== '-') return oldSetP.apply(this, arguments);
		const el = this.owningElement;
		if (el) {
			if (!el.ieCP_setters) el.ieCP_setters = {};
			el.ieCP_setters[property] = 1;
		}
		property = '-ie-'+(prio==='important'?'❗':'') + property.substr(2);
		this.cssText += '; ' + property + ':' + encodeValue(value) + ';';
		//this[property] = value;
		el === document.documentElement && redrawStyleSheets();
		el && drawTree(el); // its delayed internal
	};


	/*
	var descriptor = Object.getOwnPropertyDescriptor(StyleProto, 'cssText');
	var cssTextGetter = descriptor.get;
	var cssTextSetter = descriptor.set;
	// descriptor.get = function () {
	// 	const style = styleGetter.call(this);
	// 	style.owningElement = this;
	// 	return style;
	// }
	descriptor.set = function (css) {
		var el = this.owningElement;
		if (el) {
			css = rewriteCss('{'+css).substr(1);
			cssTextSetter.call(this, css);
			var found = parseRewrittenStyle(this);
			if (found.getters) addGetterElement(el, found.getters, '%styleAttr');
			if (found.setters) addSetterElement(el, found.setters);
			return;
		}
		return cssTextSetter.call(this, css);
	}
	Object.defineProperty(StyleProto, 'cssText', descriptor);
	*/


	if (!window.CSS) window.CSS = {};
	const register = {}
	CSS.registerProperty = function(options){
		register[options.name] = options;
	}

	// fix "initial" keyword with generated custom properties, this is not supported ad all by ie, should i make a separate "inherit"-polyfill?
	/*
	const computed = getComputedStyle(document.documentElement)
	const initials = {};
	for (let i in computed) {
		initials[i.replace(/([A-Z])/, function(x){ return '-'+x.toLowerCase(x) })] = computed[i];
	}
	initials['display'] = 'inline';
	*/

	// utils
	function fetchCss(url, callback) {
		var request = new XMLHttpRequest();
		request.open('GET', url);
		request.overrideMimeType('text/css');
		request.onload = function () {
			if (request.status >= 200 && request.status < 400) {
				callback(request.responseText);
			}
		};
		request.send();
	}

}();