balsamine.2018-2019
clone your own copy | download snapshot

Snapshots | iceberg

Inside this repository

css-regions-polyfill-custom-medor.js
application/javascript

Download raw (245.9 KB)

/*! CSS-REGIONS-POLYFILL - v3.0.0 - 2015-12-13 - https://github.com/FremyCompany/css-regions-polyfill - Copyright (c) 2015 François REMY; MIT-Licensed !*/

!(function() { 'use strict';
    var module = { exports:{} };
    var require = (function() { var modules = {}; var require = function(m) { return modules[m]; }; require.define = function(m) { modules[m]=module.exports; module.exports={}; }; return require; })();

////////////////////////////////////////

!(function(window, document) { "use strict";

    //
    // some code for console polyfilling
    //
    if(!window.console) {
            
        window.console = {
            backlog: '',
            
            log: function(x) { this.backlog+=x+'\n'; if(window.debug) alert(x); },
            
            dir: function(x) { try { 
                
                var elm = function(e) {
                    if(e.innerHTML) {
                        return {
                            tagName: e.tagName,
                            className: e.className,
                            id: e.id,
                            innerHTML: e.innerHTML.substr(0,100)
                        }
                    } else {
                        return {
                            nodeName: e.nodeName,
                            nodeValue: e.nodeValue
                        }
                    }
                };
                
                var jsonify = function(o) {
                    var seen=[];
                    var jso=JSON.stringify(o, function(k,v){
                        if (typeof v =='object') {
                            if ( !seen.indexOf(v) ) { return '__cycle__'; }
                            if ( v instanceof window.Node) { return elm(v); }
                            seen.push(v);
                        } return v;
                    });
                    return jso;
                };
                
                this.log(jsonify(x)); 
                
            } catch(ex) { this.log(x) } },
            
            warn: function(x) { this.log(x) },
            
            error: function(x) { this.log("ERROR:"); this.log(x); }
            
        };
        
        if(!window.onerror) {
            window.onerror = function() {
                console.log([].slice.call(arguments,0).join("\n"))
            };
        }
        
    }

    //
    // this special console is used as a proxy emulating the CSS console of browsers
    //
    window.cssConsole = {
        enabled: (!!window.debug), warnEnabled: (true),
        log: function(x) { if(this.enabled) console.log(x) },
        dir: function(x) { if(this.enabled) console.dir(x) },
        warn: function(x) { if(this.warnEnabled) console.warn(x) },
        error: function(x) { console.error(x); }
    }

})(window, document);
require.define('src/core/polyfill-dom-console.js');

////////////////////////////////////////

module.exports = (function(window, document) { "use strict";

    require('src/core/polyfill-dom-console.js');

    //
    // some other basic om code
    //
    var domEvents = {
        
        //
        // the following functions are about event cloning
        //
        cloneMouseEvent: function cloneMouseEvent(e) {
            var evt = document.createEvent("MouseEvent");
            evt.initMouseEvent( 
                e.type, 
                e.canBubble||e.bubbles, 
                e.cancelable, 
                e.view, 
                e.detail, 
                e.screenX, 
                e.screenY, 
                e.clientX, 
                e.clientY, 
                e.ctrlKey, 
                e.altKey, 
                e.shiftKey, 
                e.metaKey, 
                e.button, 
                e.relatedTarget
            );
            return evt;
        },
        
        cloneKeyboardEvent: function cloneKeyboardEvent(e) {
            // TODO: this doesn't work cross-browser...
            // see https://gist.github.com/termi/4654819/ for the huge code
            return domEvents.cloneCustomEvent(e);
        },
        
        cloneCustomEvent: function cloneCustomEvent(e) {
            var ne = document.createEvent("CustomEvent");
            ne.initCustomEvent(e.type, e.canBubble||e.bubbles, e.cancelable, "detail" in e ? e.detail : e);
            for(var prop in e) {
                try {
                    if(e[prop] != ne[prop] && e[prop] != e.target) {
                        try { ne[prop] = e[prop]; }
                        catch (ex) { Object.defineProperty(ne,prop,{get:function() { return e[prop]} }) }
                    }
                } catch(ex) {}
            }
            return ne;
        },
        
        cloneEvent: function cloneEvent(e) {
            
            if(e instanceof MouseEvent) {
                return domEvents.cloneMouseEvent(e);
            } else if(e instanceof KeyboardEvent) {
                return domEvents.cloneKeyboardEvent(e);
            } else {
                return domEvents.cloneCustomEvent(e);
            }
            
        },
        
        //
        // allows you to drop event support to any class easily
        //
        EventTarget: {
            implementsIn: function(eventClass, static_class) {
                
                if(!static_class && typeof(eventClass)=="function") eventClass=eventClass.prototype;
                
                eventClass.dispatchEvent = domEvents.EventTarget.prototype.dispatchEvent;
                eventClass.addEventListener = domEvents.EventTarget.prototype.addEventListener;
                eventClass.removeEventListener = domEvents.EventTarget.prototype.removeEventListener;
                
            },
            prototype: {}
        }
        
    };

    domEvents.EventTarget.prototype.addEventListener = function(eventType,f) {
        if(!this.eventListeners) this.eventListeners=[];
        
        var ls = (this.eventListeners[eventType] || (this.eventListeners[eventType]=[]));
        if(ls.indexOf(f)==-1) {
            ls.push(f);
        }
        
    }

    domEvents.EventTarget.prototype.removeEventListener = function(eventType,f) {
        if(!this.eventListeners) this.eventListeners=[];

        var ls = (this.eventListeners[eventType] || (this.eventListeners[eventType]=[])), i;
        if((i=ls.indexOf(f))!==-1) {
            ls.splice(i,1);
        }
        
    }

    domEvents.EventTarget.prototype.dispatchEvent = function(event_or_type) {
        if(!this.eventListeners) this.eventListeners=[];
        
        // abort quickly when no listener has been set up
        if(typeof(event_or_type) == "string") {
            if(!this.eventListeners[event_or_type] || this.eventListeners[event_or_type].length==0) {
                return;
            }
        } else {
            if(!this.eventListeners[event_or_type.type] || this.eventListeners[event_or_type.type].length==0) {
                return;
            }
        }
        
        // convert the event
        var event = event_or_type;
        function setUpPropertyForwarding(e,ee,key) {
            Object.defineProperty(ee,key,{
                get:function() {
                    var v = e[key]; 
                    if(typeof(v)=="function") {
                        return v.bind(e);
                    } else {
                        return v;
                    }
                },
                set:function(v) {
                    e[key] = v;
                }
            });
        }
        function setUpTarget(e,v) {
            try { Object.defineProperty(e,"target",{get:function() {return v}}); }
            catch(ex) {}
            finally {
                
                if(e.target !== v) {
                    
                    var ee = Object.create(Object.getPrototypeOf(e));
                    ee = setUpTarget(ee,v);
                    for(key in e) {
                        if(key != "target") setUpPropertyForwarding(e,ee,key);
                    }
                    return ee;
                    
                } else {
                    
                    return e;
                    
                }
                
            }
        }
        
        // try to set the target
        if(typeof(event)=="object") {
            try { event=setUpTarget(event,this); } catch(ex) {}
            
        } else if(typeof(event)=="string") {
            event = document.createEvent("CustomEvent");
            event.initCustomEvent(event_or_type, /*canBubble:*/ true, /*cancelable:*/ false, /*detail:*/this);
            try { event=setUpTarget(event,this); } catch(ex) {}
            
        } else {
            throw new Error("dispatchEvent expect an Event object or a string containing the event type");
        }
        
        // call all listeners
        var ls = (this.eventListeners[event.type] || (this.eventListeners[event.type]=[]));
        for(var i=ls.length; i--;) {
            try { 
                ls[i](event);
            } catch(ex) {
                setImmediate(function() { throw ex; });
            }
        }
        
        return event.isDefaultPrevented;
    }
    
    return domEvents;
    
})(window, document);
require.define('src/core/dom-events.js');

////////////////////////////////////////

//
// note: this file is based on Tab Atkins's CSS Parser
// please include him (@tabatkins) if you open any issue for this file
// 
module.exports = (function(window, document) { "use strict";

// 
// exports
//
var cssSyntax = { 
    tokenize: function(string) {/*filled later*/}, 
    parse: function(tokens) {/*filled later*/}
};

//
// css tokenizer
//

// Add support for token lists (superclass of array)
function TokenList() {
    var array = []; 
    array.toCSSString=TokenListToCSSString;
    return array;
}
function TokenListToCSSString(sep) {
    if(sep) {
        return this.map(function(o) { return o.toCSSString(); }).join(sep);
    } else {
        return this.asCSSString || (this.asCSSString = (
            this.map(function(o) { return o.toCSSString(); }).join("/**/")
                .replace(/( +\/\*\*\/ *| * | *\/\*\*\/ +)/g," ")
                .replace(/( +\/\*\*\/ *| * | *\/\*\*\/ +)/g," ")
                .replace(/(\!|\:|\;|\@|\.|\,|\*|\=|\&|\\|\/|\<|\>|\[|\{|\(|\]|\}|\)|\|)\/\*\*\//g,"$1")
                .replace(/\/\*\*\/(\!|\:|\;|\@|\.|\,|\*|\=|\&|\\|\/|\<|\>|\[|\{|\(|\]|\}|\)|\|)/g,"$1")
        ));
    }
}
cssSyntax.TokenList = TokenList;
cssSyntax.TokenListToCSSString = TokenListToCSSString;

function between(num, first, last) { return num >= first && num <= last; }
function digit(code) { return between(code, 0x30,0x39); }
function hexdigit(code) { return digit(code) || between(code, 0x41,0x46) || between(code, 0x61,0x66); }
function uppercaseletter(code) { return between(code, 0x41,0x5a); }
function lowercaseletter(code) { return between(code, 0x61,0x7a); }
function letter(code) { return uppercaseletter(code) || lowercaseletter(code); }
function nonascii(code) { return code >= 0x80; }
function namestartchar(code) { return letter(code) || nonascii(code) || code == 0x5f; }
function namechar(code) { return namestartchar(code) || digit(code) || code == 0x2d; }
function nonprintable(code) { return between(code, 0,8) || code == 0xb || between(code, 0xe,0x1f) || code == 0x7f; }
function newline(code) { return code == 0xa; }
function whitespace(code) { return newline(code) || code == 9 || code == 0x20; }
function badescape(code) { return newline(code) || isNaN(code); }

var maximumallowedcodepoint = 0x10ffff;

function InvalidCharacterError(message) {
    this.message = message;
};
InvalidCharacterError.prototype = new Error;
InvalidCharacterError.prototype.name = 'InvalidCharacterError';

function preprocess(str) {
    // Turn a string into an array of code points,
    // following the preprocessing cleanup rules.
    var codepoints = [];
    for(var i = 0; i < str.length; i++) {
        var code = str.charCodeAt(i);
        if(code == 0xd && str.charCodeAt(i+1) == 0xa) {
            code = 0xa; i++;
        }
        if(code == 0xd || code == 0xc) code = 0xa;
        if(code == 0x0) code = 0xfffd;
        if(between(code, 0xd800, 0xdbff) && between(str.charCodeAt(i+1), 0xdc00, 0xdfff)) {
            // Decode a surrogate pair into an astral codepoint.
            var lead = code - 0xd800;
            var trail = str.charCodeAt(i+1) - 0xdc00;
            code = Math.pow(2, 21) + lead * Math.pow(2, 10) + trail;
        }
        codepoints.push(code);
    }
    return codepoints;
}

function stringFromCode(code) {
    if(code <= 0xffff) return String.fromCharCode(code);
    // Otherwise, encode astral char as surrogate pair.
    code -= Math.pow(2, 21);
    var lead = Math.floor(code/Math.pow(2, 10)) + 0xd800;
    var trail = code % Math.pow(2, 10); + 0xdc00;
    return String.fromCharCode(lead) + String.fromCharCode(trail);
}

function tokenize(str) {
    str = preprocess(str);
    var i = -1;
    var tokens = new TokenList();
    var code;

    // Line number information.
    var line = 0;
    var column = 0;
    // The only use of lastLineLength is in reconsume().
    var lastLineLength = 0;
    var incrLineno = function() {
        line += 1;
        lastLineLength = column;
        column = 0;
    };
    var locStart = {line:line, column:column};

    var codepoint = function(i) {
        if(i >= str.length) {
            return -1;
        }
        return str[i];
    }
    var next = function(num) {
        if(num === undefined) { num = 1; }
        if(num > 3) { throw "Spec Error: no more than three codepoints of lookahead."; }
        return codepoint(i+num);
    };
    var consume = function(num) {
        if(num === undefined)
            num = 1;
        i += num;
        code = codepoint(i);
        if(newline(code)) incrLineno();
        else column += num;
        //console.log('Consume '+i+' '+String.fromCharCode(code) + ' 0x' + code.toString(16));
        return true;
    };
    var reconsume = function() {
        i -= 1;
        if (newline(code)) {
            line -= 1;
            column = lastLineLength;
        } else {
            column -= 1;
        }
        locStart.line = line;
        locStart.column = column;
        return true;
    };
    var eof = function(codepoint) {
        if(codepoint === undefined) codepoint = code;
        return codepoint == -1;
    };
    var donothing = function() {};
    var tokenizeerror = function() { console.log("Parse error at index " + i + ", processing codepoint 0x" + code.toString(16) + ".");return true; };

    var consumeAToken = function() {
        consumeComments();
        consume();
        if(whitespace(code)) {
            while(whitespace(next())) consume();
            return new WhitespaceToken;
        }
        else if(code == 0x22) return consumeAStringToken();
        else if(code == 0x23) {
            if(namechar(next()) || areAValidEscape(next(1), next(2))) {
                var token = new HashToken();
                if(wouldStartAnIdentifier(next(1), next(2), next(3))) token.type = "id";
                token.value = consumeAName();
                return token;
            } else {
                return new DelimToken(code);
            }
        }
        else if(code == 0x24) {
            if(next() == 0x3d) {
                consume();
                return new SuffixMatchToken();
            } else {
                return new DelimToken(code);
            }
        }
        else if(code == 0x27) return consumeAStringToken();
        else if(code == 0x28) return new OpenParenToken();
        else if(code == 0x29) return new CloseParenToken();
        else if(code == 0x2a) {
            if(next() == 0x3d) {
                consume();
                return new SubstringMatchToken();
            } else {
                return new DelimToken(code);
            }
        }
        else if(code == 0x2b) {
            if(startsWithANumber()) {
                reconsume();
                return consumeANumericToken();
            } else {
                return new DelimToken(code);
            }
        }
        else if(code == 0x2c) return new CommaToken();
        else if(code == 0x2d) {
            if(startsWithANumber()) {
                reconsume();
                return consumeANumericToken();
            } else if(next(1) == 0x2d && next(2) == 0x3e) {
                consume(2);
                return new CDCToken();
            } else if(startsWithAnIdentifier()) {
                reconsume();
                return consumeAnIdentlikeToken();
            } else {
                return new DelimToken(code);
            }
        }
        else if(code == 0x2e) {
            if(startsWithANumber()) {
                reconsume();
                return consumeANumericToken();
            } else {
                return new DelimToken(code);
            }
        }
        else if(code == 0x3a) return new ColonToken;
        else if(code == 0x3b) return new SemicolonToken;
        else if(code == 0x3c) {
            if(next(1) == 0x21 && next(2) == 0x2d && next(3) == 0x2d) {
                consume(3);
                return new CDOToken();
            } else {
                return new DelimToken(code);
            }
        }
        else if(code == 0x40) {
            if(wouldStartAnIdentifier(next(1), next(2), next(3))) {
                return new AtKeywordToken(consumeAName());
            } else {
                return new DelimToken(code);
            }
        }
        else if(code == 0x5b) return new OpenSquareToken();
        else if(code == 0x5c) {
            if(startsWithAValidEscape()) {
                reconsume();
                return consumeAnIdentlikeToken();
            } else {
                tokenizeerror();
                return new DelimToken(code);
            }
        }
        else if(code == 0x5d) return new CloseSquareToken();
        else if(code == 0x5e) {
            if(next() == 0x3d) {
                consume();
                return new PrefixMatchToken();
            } else {
                return new DelimToken(code);
            }
        }
        else if(code == 0x7b) return new OpenCurlyToken();
        else if(code == 0x7c) {
            if(next() == 0x3d) {
                consume();
                return new DashMatchToken();
            } else if(next() == 0x7c) {
                consume();
                return new ColumnToken();
            } else {
                return new DelimToken(code);
            }
        }
        else if(code == 0x7d) return new CloseCurlyToken();
        else if(code == 0x7e) {
            if(next() == 0x3d) {
                consume();
                return new IncludeMatchToken();
            } else {
                return new DelimToken(code);
            }
        }
        else if(digit(code)) {
            reconsume();
            return consumeANumericToken();
        }
        else if(namestartchar(code)) {
            reconsume();
            return consumeAnIdentlikeToken();
        }
        else if(eof()) return new EOFToken();
        else return new DelimToken(code);
    };

    var consumeComments = function() {
        while(next(1) == 0x2f && next(2) == 0x2a) {
            consume(2);
            while(true) {
                consume();
                if(code == 0x2a && next() == 0x2f) {
                    consume();
                    break;
                } else if(eof()) {
                    tokenizeerror();
                    return;
                }
            }
        }
    };

    var consumeANumericToken = function() {
        var num = consumeANumber();
        if(wouldStartAnIdentifier(next(1), next(2), next(3))) {
            var token = new DimensionToken();
            token.value = num.value;
            token.repr = num.repr;
            token.type = num.type;
            token.unit = consumeAName();
            return token;
        } else if(next() == 0x25) {
            consume();
            var token = new PercentageToken();
            token.value = num.value;
            token.repr = num.repr;
            return token;
        } else {
            var token = new NumberToken();
            token.value = num.value;
            token.repr = num.repr;
            token.type = num.type;
            return token;
        }
    };

    var consumeAnIdentlikeToken = function() {
        var str = consumeAName();
        if(str.toLowerCase() == "url" && next() == 0x28) {
            consume();
            while(whitespace(next(1)) && whitespace(next(2))) consume();
            if(next() == 0x22 || next() == 0x27) {
                return new FunctionToken(str);
            } else if(whitespace(next()) && (next(2) == 0x22 || next(2) == 0x27)) {
                return new FunctionToken(str);
            } else {
                return consumeAURLToken();
            }
        } else if(next() == 0x28) {
            consume();
            return new FunctionToken(str);
        } else {
            return new IdentifierToken(str);
        }
    };

    var consumeAStringToken = function(endingCodePoint) {
        if(endingCodePoint === undefined) endingCodePoint = code;
        var string = "";
        while(consume()) {
            if(code == endingCodePoint || eof()) {
                return new StringToken(string);
            } else if(newline(code)) {
                tokenizeerror();
                reconsume();
                return new BadStringToken();
            } else if(code == 0x5c) {
                if(eof(next())) {
                    donothing();
                } else if(newline(next())) {
                    consume();
                } else {
                    string += stringFromCode(consumeEscape())
                }
            } else {
                string += stringFromCode(code);
            }
        }
    };

    var consumeAURLToken = function() {
        var token = new URLToken("");
        while(whitespace(next())) consume();
        if(eof(next())) return token;
        while(consume()) {
            if(code == 0x29 || eof()) {
                return token;
            } else if(whitespace(code)) {
                while(whitespace(next())) consume();
                if(next() == 0x29 || eof(next())) {
                    consume();
                    return token;
                } else {
                    consumeTheRemnantsOfABadURL();
                    return new BadURLToken();
                }
            } else if(code == 0x22 || code == 0x27 || code == 0x28 || nonprintable(code)) {
                tokenizeerror();
                consumeTheRemnantsOfABadURL();
                return new BadURLToken();
            } else if(code == 0x5c) {
                if(startsWithAValidEscape()) {
                    token.value += stringFromCode(consumeEscape());
                } else {
                    tokenizeerror();
                    consumeTheRemnantsOfABadURL();
                    return new BadURLToken();
                }
            } else {
                token.value += stringFromCode(code);
            }
        }
    };

    var consumeEscape = function() {
        // Assume the the current character is the \
        // and the next code point is not a newline.
        consume();
        if(hexdigit(code)) {
            // Consume 1-6 hex digits
            var digits = [code];
            for(var total = 0; total < 5; total++) {
                if(hexdigit(next())) {
                    consume();
                    digits.push(code);
                } else {
                    break;
                }
            }
            if(whitespace(next())) consume();
            var value = parseInt(digits.map(function(x){return String.fromCharCode(x);}).join(''), 16);
            if( value > maximumallowedcodepoint ) value = 0xfffd;
            return value;
        } else if(eof()) {
            return 0xfffd;
        } else {
            return code;
        }
    };

    var areAValidEscape = function(c1, c2) {
        if(c1 != 0x5c) return false;
        if(newline(c2)) return false;
        return true;
    };
    var startsWithAValidEscape = function() {
        return areAValidEscape(code, next());
    };

    var wouldStartAnIdentifier = function(c1, c2, c3) {
        if(c1 == 0x2d) {
            return namestartchar(c2) || c2 == 0x2d || areAValidEscape(c2, c3);
        } else if(namestartchar(c1)) {
            return true;
        } else if(c1 == 0x5c) {
            return areAValidEscape(c1, c2);
        } else {
            return false;
        }
    };
    var startsWithAnIdentifier = function() {
        return wouldStartAnIdentifier(code, next(1), next(2));
    };

    var wouldStartANumber = function(c1, c2, c3) {
        if(c1 == 0x2b || c1 == 0x2d) {
            if(digit(c2)) return true;
            if(c2 == 0x2e && digit(c3)) return true;
            return false;
        } else if(c1 == 0x2e) {
            if(digit(c2)) return true;
            return false;
        } else if(digit(c1)) {
            return true;
        } else {
            return false;
        }
    };
    var startsWithANumber = function() {
        return wouldStartANumber(code, next(1), next(2));
    };

    var consumeAName = function() {
        var result = "";
        while(consume()) {
            if(namechar(code)) {
                result += stringFromCode(code);
            } else if(startsWithAValidEscape()) {
                result += stringFromCode(consumeEscape());
            } else {
                reconsume();
                return result;
            }
        }
    };

    var consumeANumber = function() {
        var repr = '';
        var type = "integer";
        if(next() == 0x2b || next() == 0x2d) {
            consume();
            repr += stringFromCode(code);
        }
        while(digit(next())) {
            consume();
            repr += stringFromCode(code);
        }
        if(next(1) == 0x2e && digit(next(2))) {
            consume();
            repr += stringFromCode(code);
            consume();
            repr += stringFromCode(code);
            type = "number";
            while(digit(next())) {
                consume();
                repr += stringFromCode(code);
            }
        }
        var c1 = next(1), c2 = next(2), c3 = next(3);
        if((c1 == 0x45 || c1 == 0x65) && digit(c2)) {
            consume();
            repr += stringFromCode(code);
            consume();
            repr += stringFromCode(code);
            type = "number";
            while(digit(next())) {
                consume();
                repr += stringFromCode(code);
            }
        } else if((c1 == 0x45 || c1 == 0x65) && (c2 == 0x2b || c2 == 0x2d) && digit(c3)) {
            consume();
            repr += stringFromCode(code);
            consume();
            repr += stringFromCode(code);
            consume();
            repr += stringFromCode(code);
            type = "number";
            while(digit(next())) {
                consume();
                repr += stringFromCode(code);
            }
        }
        var value = convertAStringToANumber(repr);
        return {type:type, value:value, repr:repr};
    };

    var convertAStringToANumber = function(string) {
        // CSS's number rules are identical to JS, afaik.
        return +string;
    };

    var consumeTheRemnantsOfABadURL = function() {
        while(consume()) {
            if(code == 0x2d || eof()) {
                return;
            } else if(startsWithAValidEscape()) {
                consumeEscape();
                donothing();
            } else {
                donothing();
            }
        }
    };



    var iterationCount = 0;
    while(!eof(next())) {
        tokens.push(consumeAToken());
        if(iterationCount++ > str.length*2) throw new Error("The CSS Tokenizer is infinite-looping");
    }
    return tokens;
}

function CSSParserToken() { return this; }
CSSParserToken.prototype.toJSON = function() {
    return {token: this.tokenType};
}
CSSParserToken.prototype.toString = function() { return this.tokenType; }
CSSParserToken.prototype.toCSSString = function() { return ''+this; }

function BadStringToken() { return this; }
BadStringToken.prototype = new CSSParserToken;
BadStringToken.prototype.tokenType = "BADSTRING";
BadStringToken.prototype.toCSSString = function() { return "'"; }

function BadURLToken() { return this; }
BadURLToken.prototype = new CSSParserToken;
BadURLToken.prototype.tokenType = "BADURL";
BadURLToken.prototype.toCSSString = function() { return "url("; }

function WhitespaceToken() { return this; }
WhitespaceToken.prototype = new CSSParserToken;
WhitespaceToken.prototype.tokenType = "WHITESPACE";
WhitespaceToken.prototype.toString = function() { return "WS"; }
WhitespaceToken.prototype.toCSSString = function() { return " "; }

function CDOToken() { return this; }
CDOToken.prototype = new CSSParserToken;
CDOToken.prototype.tokenType = "CDO";
CDOToken.prototype.toCSSString = function() { return "<!--"; }

function CDCToken() { return this; }
CDCToken.prototype = new CSSParserToken;
CDCToken.prototype.tokenType = "CDC";
CDCToken.prototype.toCSSString = function() { return "-->"; }

function ColonToken() { return this; }
ColonToken.prototype = new CSSParserToken;
ColonToken.prototype.tokenType = ":";

function SemicolonToken() { return this; }
SemicolonToken.prototype = new CSSParserToken;
SemicolonToken.prototype.tokenType = ";";

function CommaToken() { return this; }
CommaToken.prototype = new CSSParserToken;
CommaToken.prototype.tokenType = ",";
CommaToken.prototype.value = ";"; // backwards-compat with DELIM token

function GroupingToken() { return this; }
GroupingToken.prototype = new CSSParserToken;

function OpenCurlyToken() { this.value = "{"; this.mirror = "}"; return this; }
OpenCurlyToken.prototype = new GroupingToken;
OpenCurlyToken.prototype.tokenType = "{";

function CloseCurlyToken() { this.value = "}"; this.mirror = "{"; return this; }
CloseCurlyToken.prototype = new GroupingToken;
CloseCurlyToken.prototype.tokenType = "}";

function OpenSquareToken() { this.value = "["; this.mirror = "]"; return this; }
OpenSquareToken.prototype = new GroupingToken;
OpenSquareToken.prototype.tokenType = "[";

function CloseSquareToken() { this.value = "]"; this.mirror = "["; return this; }
CloseSquareToken.prototype = new GroupingToken;
CloseSquareToken.prototype.tokenType = "]";

function OpenParenToken() { this.value = "("; this.mirror = ")"; return this; }
OpenParenToken.prototype = new GroupingToken;
OpenParenToken.prototype.tokenType = "(";

function CloseParenToken() { this.value = ")"; this.mirror = "("; return this; }
CloseParenToken.prototype = new GroupingToken;
CloseParenToken.prototype.tokenType = ")";

function IncludeMatchToken() { return this; }
IncludeMatchToken.prototype = new CSSParserToken;
IncludeMatchToken.prototype.tokenType = "~=";

function DashMatchToken() { return this; }
DashMatchToken.prototype = new CSSParserToken;
DashMatchToken.prototype.tokenType = "|=";

function PrefixMatchToken() { return this; }
PrefixMatchToken.prototype = new CSSParserToken;
PrefixMatchToken.prototype.tokenType = "^=";

function SuffixMatchToken() { return this; }
SuffixMatchToken.prototype = new CSSParserToken;
SuffixMatchToken.prototype.tokenType = "$=";

function SubstringMatchToken() { return this; }
SubstringMatchToken.prototype = new CSSParserToken;
SubstringMatchToken.prototype.tokenType = "*=";

function ColumnToken() { return this; }
ColumnToken.prototype = new CSSParserToken;
ColumnToken.prototype.tokenType = "||";

function EOFToken() { return this; }
EOFToken.prototype = new CSSParserToken;
EOFToken.prototype.tokenType = "EOF";
EOFToken.prototype.toCSSString = function() { return ""; }

function DelimToken(code) {
    this.value = stringFromCode(code);
    return this;
}
DelimToken.prototype = new CSSParserToken;
DelimToken.prototype.tokenType = "DELIM";
DelimToken.prototype.toString = function() { return "DELIM("+this.value+")"; }
DelimToken.prototype.toCSSString = function() {
    return (this.value == "\\") ? "\\\n" : this.value;
}

function StringValuedToken() { return this; }
StringValuedToken.prototype = new CSSParserToken;
StringValuedToken.prototype.ASCIIMatch = function(str) {
    return this.value.toLowerCase() == str.toLowerCase();
}

function IdentifierToken(val) {
    this.value = val;
}
IdentifierToken.prototype = new StringValuedToken;
IdentifierToken.prototype.tokenType = "IDENT";
IdentifierToken.prototype.toString = function() { return "IDENT("+this.value+")"; }
IdentifierToken.prototype.toCSSString = function() {
    return escapeIdent(this.value);
}

function FunctionToken(val) {
    this.value = val;
    this.mirror = ")";
}
FunctionToken.prototype = new StringValuedToken;
FunctionToken.prototype.tokenType = "FUNCTION";
FunctionToken.prototype.toString = function() { return "FUNCTION("+this.value+")"; }
FunctionToken.prototype.toCSSString = function() {
    return escapeIdent(this.value) + "(";
}
    
function AtKeywordToken(val) {
    this.value = val;
}
AtKeywordToken.prototype = new StringValuedToken;
AtKeywordToken.prototype.tokenType = "AT-KEYWORD";
AtKeywordToken.prototype.toString = function() { return "AT("+this.value+")"; }
AtKeywordToken.prototype.toCSSString = function() {
    return "@" + escapeIdent(this.value);
}

function HashToken(val) {
    this.value = val;
    this.type = "unrestricted";
}
HashToken.prototype = new StringValuedToken;
HashToken.prototype.tokenType = "HASH";
HashToken.prototype.toString = function() { return "HASH("+this.value+")"; }
HashToken.prototype.toCSSString = function() {
    var escapeValue = (this.type == "id") ? escapeIdent : escapeHash;
    return "#" + escapeValue(this.value);
}

function StringToken(val) {
this.value = val;
}
StringToken.prototype = new StringValuedToken;
StringToken.prototype.tokenType = "STRING";
StringToken.prototype.toString = function() {
    return '"' + escapeString(this.value) + '"';
}

function URLToken(val) {
    this.value = val;
}
URLToken.prototype = new StringValuedToken;
URLToken.prototype.tokenType = "URL";
URLToken.prototype.toString = function() { return "URL("+this.value+")"; }
URLToken.prototype.toCSSString = function() {
    return 'url("' + escapeString(this.value) + '")';
}

function NumberToken() {
    this.value = null;
    this.type = "integer";
    this.repr = "";
}
NumberToken.prototype = new CSSParserToken;
NumberToken.prototype.tokenType = "NUMBER";
NumberToken.prototype.toString = function() {
    if(this.type == "integer")
        return "INT("+this.value+")";
    return "NUMBER("+this.value+")";
}
NumberToken.prototype.toJSON = function() {
    var json = this.constructor.prototype.constructor.prototype.toJSON.call(this);
    json.value = this.value;
    json.type = this.type;
    json.repr = this.repr;
    return json;
}
NumberToken.prototype.toCSSString = function() { return this.repr; };

function PercentageToken() {
    this.value = null;
    this.repr = "";
}
PercentageToken.prototype = new CSSParserToken;
PercentageToken.prototype.tokenType = "PERCENTAGE";
PercentageToken.prototype.toString = function() { return "PERCENTAGE("+this.value+")"; }
PercentageToken.prototype.toCSSString = function() { return this.repr + "%"; }

function DimensionToken() {
    this.value = null;
    this.type = "integer";
    this.repr = "";
    this.unit = "";
}
DimensionToken.prototype = new CSSParserToken;
DimensionToken.prototype.tokenType = "DIMENSION";
DimensionToken.prototype.toString = function() { return "DIM("+this.value+","+this.unit+")"; }
DimensionToken.prototype.toCSSString = function() {
    var source = this.repr;
    var unit = escapeIdent(this.unit);
    if(unit[0].toLowerCase() == "e" && (unit[1] == "-" || between(unit.charCodeAt(1), 0x30, 0x39))) {
        // Unit is ambiguous with scinot
        // Remove the leading "e", replace with escape.
        unit = "\\65 " + unit.slice(1, unit.length);
    }
    return source+unit;
}

function escapeIdent(string) {
    string = ''+string;
    var result = '';
    var firstcode = string.charCodeAt(0);
    for(var i = 0; i < string.length; i++) {
        var code = string.charCodeAt(i);
        if(code == 0x0) {
            throw new InvalidCharacterError('Invalid character: the input contains U+0000.');
        }

        if(
            between(code, 0x1, 0x1f) || code == 0x7f ||
            (i == 0 && between(code, 0x30, 0x39)) ||
            (i == 1 && between(code, 0x30, 0x39) && firstcode == 0x2d)
        ) {
            result += '\\' + code.toString(16) + ' ';
        } else if(
            code >= 0x80 ||
            code == 0x2d ||
            code == 0x5f ||
            between(code, 0x30, 0x39) ||
            between(code, 0x41, 0x5a) ||
            between(code, 0x61, 0x7a)
        ) {
            result += string[i];
        } else {
            result += '\\' + string[i];
        }
    }
    return result;
}

function escapeHash(string) {
    // Escapes the contents of "unrestricted"-type hash tokens.
    // Won't preserve the ID-ness of "id"-type hash tokens;
    // use escapeIdent() for that.
    string = ''+string;
    var result = '';
    var firstcode = string.charCodeAt(0);
    for(var i = 0; i < string.length; i++) {
        var code = string.charCodeAt(i);
        if(code == 0x0) {
            throw new InvalidCharacterError('Invalid character: the input contains U+0000.');
        }

        if(
            code >= 0x80 ||
            code == 0x2d ||
            code == 0x5f ||
            between(code, 0x30, 0x39) ||
            between(code, 0x41, 0x5a) ||
            between(code, 0x61, 0x7a)
        ) {
            result += string[i];
        } else {
            result += '\\' + code.toString(16) + ' ';
        }
    }
    return result;
}

function escapeString(string) {
    string = ''+string;
    var result = '';
    for(var i = 0; i < string.length; i++) {
        var code = string.charCodeAt(i);

        if(code == 0x0) {
            throw new InvalidCharacterError('Invalid character: the input contains U+0000.');
        }

        if(between(code, 0x1, 0x1f) || code == 0x7f) {
            result += '\\' + code.toString(16) + ' ';
        } else if(code == 0x22 || code == 0x5c) {
            result += '\\' + string[i];
        } else {
            result += string[i];
        }
    }
    return result;
}

// Exportation.
cssSyntax.tokenize = tokenize;
cssSyntax.IdentToken = IdentifierToken;
cssSyntax.IdentifierToken = IdentifierToken;
cssSyntax.FunctionToken = FunctionToken;
cssSyntax.AtKeywordToken = AtKeywordToken;
cssSyntax.HashToken = HashToken;
cssSyntax.StringToken = StringToken;
cssSyntax.BadStringToken = BadStringToken;
cssSyntax.URLToken = URLToken;
cssSyntax.BadURLToken = BadURLToken;
cssSyntax.DelimToken = DelimToken;
cssSyntax.NumberToken = NumberToken;
cssSyntax.PercentageToken = PercentageToken;
cssSyntax.DimensionToken = DimensionToken;
cssSyntax.IncludeMatchToken = IncludeMatchToken;
cssSyntax.DashMatchToken = DashMatchToken;
cssSyntax.PrefixMatchToken = PrefixMatchToken;
cssSyntax.SuffixMatchToken = SuffixMatchToken;
cssSyntax.SubstringMatchToken = SubstringMatchToken;
cssSyntax.ColumnToken = ColumnToken;
cssSyntax.WhitespaceToken = WhitespaceToken;
cssSyntax.CDOToken = CDOToken;
cssSyntax.CDCToken = CDCToken;
cssSyntax.ColonToken = ColonToken;
cssSyntax.SemicolonToken = SemicolonToken;
cssSyntax.CommaToken = CommaToken;
cssSyntax.OpenParenToken = OpenParenToken;
cssSyntax.CloseParenToken = CloseParenToken;
cssSyntax.OpenSquareToken = OpenSquareToken;
cssSyntax.CloseSquareToken = CloseSquareToken;
cssSyntax.OpenCurlyToken = OpenCurlyToken;
cssSyntax.CloseCurlyToken = CloseCurlyToken;
cssSyntax.EOFToken = EOFToken;
cssSyntax.CSSParserToken = CSSParserToken;
cssSyntax.GroupingToken = GroupingToken;

//
// css parser
//

function TokenStream(tokens) {
    // Assume that tokens is an array.
    this.tokens = tokens;
    this.i = -1;
}
TokenStream.prototype.tokenAt = function(i) {
    if(i < this.tokens.length)
        return this.tokens[i];
    return new EOFToken();
}
TokenStream.prototype.consume = function(num) {
    if(num === undefined) num = 1;
    this.i += num;
    this.token = this.tokenAt(this.i);
    //console.log(this.i, this.token);
    return true;
}
TokenStream.prototype.next = function() {
    return this.tokenAt(this.i+1);
}
TokenStream.prototype.reconsume = function() {
    this.i--;
}

function parseerror(s, msg) {
    console.log("Parse error at token " + s.i + ": " + s.token + ".\n" + msg);
    return true;
}
function donothing(){ return true; };

function consumeAListOfRules(s, topLevel) {
    var rules = new TokenList();
    var rule;
    while(s.consume()) {
        if(s.token instanceof WhitespaceToken) {
            continue;
        } else if(s.token instanceof EOFToken) {
            return rules;
        } else if(s.token instanceof CDOToken || s.token instanceof CDCToken) {
            if(topLevel == "top-level") continue;
            s.reconsume();
            if(rule = consumeAStyleRule(s)) rules.push(rule);
        } else if(s.token instanceof AtKeywordToken) {
            s.reconsume();
            if(rule = consumeAnAtRule(s)) rules.push(rule);
        } else {
            s.reconsume();
            if(rule = consumeAStyleRule(s)) rules.push(rule);
        }
    }
}

function consumeAnAtRule(s) {
    s.consume();
    var rule = new AtRule(s.token.value);
    while(s.consume()) {
        if(s.token instanceof SemicolonToken || s.token instanceof EOFToken) {
            return rule;
        } else if(s.token instanceof OpenCurlyToken) {
            rule.value = consumeASimpleBlock(s);
            return rule;
        } else if(s.token instanceof SimpleBlock && s.token.name == "{") {
            rule.value = s.token;
            return rule;
        } else {
            s.reconsume();
            rule.prelude.push(consumeAComponentValue(s));
        }
    }
}

function consumeAStyleRule(s) {
    var rule = new StyleRule();
    while(s.consume()) {
        if(s.token instanceof EOFToken) {
            parseerror(s, "Hit EOF when trying to parse the prelude of a qualified rule.");
            return;
        } else if(s.token instanceof OpenCurlyToken) {
            rule.value = consumeASimpleBlock(s);
            return rule;
        } else if(s.token instanceof SimpleBlock && s.token.name == "{") {
            rule.value = s.token;
            return rule;
        } else {
            s.reconsume();
            rule.prelude.push(consumeAComponentValue(s));
        }
    }
}

function consumeAListOfDeclarations(s) {
    var decls = new TokenList();
    while(s.consume()) {
        if(s.token instanceof WhitespaceToken || s.token instanceof SemicolonToken) {
            donothing();
        } else if(s.token instanceof EOFToken) {
            return decls;
        } else if(s.token instanceof AtKeywordToken) {
            s.reconsume();
            decls.push(consumeAnAtRule(s));
        } else if(s.token instanceof IdentifierToken) {
            var temp = [s.token];
            while(!(s.next() instanceof SemicolonToken || s.next() instanceof EOFToken))
                temp.push(consumeAComponentValue(s));
            var decl;
            if(decl = consumeADeclaration(new TokenStream(temp))) decls.push(decl);
        } else {
            parseerror(s);
            s.reconsume();
            while(!(s.next() instanceof SemicolonToken || s.next() instanceof EOFToken))
                consumeAComponentValue(s);
        }
    }
}

function consumeADeclaration(s) {
    // Assumes that the next input token will be an ident token.
    s.consume();
    var decl = new Declaration(s.token.value);
    while(s.next() instanceof WhitespaceToken) s.consume();
    if(!(s.next() instanceof ColonToken)) {
        parseerror(s);
        return;
    } else {
        s.consume();
    }
    while(!(s.next() instanceof EOFToken)) {
        decl.value.push(consumeAComponentValue(s));
    }
    var foundImportant = false;
    for(var i = decl.value.length - 1; i >= 0; i--) {
        if(decl.value[i] instanceof WhitespaceToken) {
            continue;
        } else if(decl.value[i] instanceof IdentifierToken && decl.value[i].ASCIIMatch("important")) {
            foundImportant = true;
        } else if(foundImportant && decl.value[i] instanceof DelimToken && decl.value[i].value == "!") {
            decl.value.splice(i, decl.value.length);
            decl.important = true;
            break;
        } else {
            break;
        }
    }
    return decl;
}

function consumeAComponentValue(s) {
    s.consume();
    if(s.token instanceof OpenCurlyToken || s.token instanceof OpenSquareToken || s.token instanceof OpenParenToken)
        return consumeASimpleBlock(s);
    if(s.token instanceof FunctionToken)
        return consumeAFunction(s);
    return s.token;
}

function consumeASimpleBlock(s) {
    var mirror = s.token.mirror;
    var block = new SimpleBlock(s.token.value);
    while(s.consume()) {
        if(s.token instanceof EOFToken || (s.token instanceof GroupingToken && s.token.value == mirror))
            return block;
        else {
            s.reconsume();
            block.value.push(consumeAComponentValue(s));
        }
    }
}

function consumeAFunction(s) {
    var func = new Func(s.token.value);
    while(s.consume()) {
        if(s.token instanceof EOFToken || s.token instanceof CloseParenToken)
            return func;
        else {
            s.reconsume();
            func.value.push(consumeAComponentValue(s));
        }
    }
}

function normalizeInput(input) {
    if(typeof input == "string")
        return new TokenStream(tokenize(input));
    if(input instanceof TokenStream)
        return input;
    if(input.length !== undefined)
        return new TokenStream(input);
    else throw SyntaxError(input);
}

function parseAStylesheet(s) {
    s = normalizeInput(s);
    var sheet = new Stylesheet();
    sheet.value = consumeAListOfRules(s, "top-level");
    return sheet;
}

function parseAListOfRules(s) {
    s = normalizeInput(s);
    return consumeAListOfRules(s);
}

function parseARule(s) {
    s = normalizeInput(s);
    while(s.next() instanceof WhitespaceToken) s.consume();
    if(s.next() instanceof EOFToken) throw SyntaxError();
    if(s.next() instanceof AtKeywordToken) {
        var rule = consumeAnAtRule(s);
    } else {
        var rule = consumeAStyleRule(s);
        if(!rule) throw SyntaxError();
    }
    while(s.next() instanceof WhitespaceToken) s.consume();
    if(s.next() instanceof EOFToken)
        return rule;
    throw SyntaxError();
}

function parseADeclaration(s) {
    s = normalizeInput(s);
    while(s.next() instanceof WhitespaceToken) s.consume();
    if(!(s.next() instanceof IdentifierToken)) throw SyntaxError();
    var decl = consumeADeclaration(s);
    if(!decl) { throw new SyntaxError() }
    return decl;
}

function parseAListOfDeclarations(s) {
    s = normalizeInput(s);
    return consumeAListOfDeclarations(s);
}

function parseAComponentValue(s) {
    s = normalizeInput(s);
    while(s.next() instanceof WhitespaceToken) s.consume();
    if(s.next() instanceof EOFToken) throw SyntaxError();
    var val = consumeAComponentValue(s);
    if(!val) throw SyntaxError();
    while(s.next() instanceof WhitespaceToken) s.consume();
    if(!(s.next() instanceof EOFToken)) throw new SyntaxError();
    return val;
}

function parseAListOfComponentValues(s) {
    s = normalizeInput(s);
    var vals = new TokenList();
    while(true) {
        var val = consumeAComponentValue(s);
        if(val instanceof EOFToken)
            return vals
        else
            vals.push(val);
    }
}

function parseACommaSeparatedListOfComponentValues(s) {
    s = normalizeInput(s);
    var listOfCVLs = new TokenList();
    while(true) {
        var vals = new TokenList();
        while(true) {
            var val = consumeAComponentValue(s);
            if(val instanceof EOFToken) {
                listOfCVLs.push(vals);
                return listOfCVLs;
            } else if(val instanceof CommaToken) {
                listOfCVLs.push(vals);
                break;
            } else {
                vals.push(val);
            }
        }
    }
}

function CSSParserRule() { return this; }
CSSParserRule.prototype.toString = function(indent) {
    return JSON.stringify(this,null,indent);
}

function Stylesheet() {
    this.value = new TokenList();
    return this;
}
Stylesheet.prototype = new CSSParserRule;
Stylesheet.prototype.type = "STYLESHEET";
Stylesheet.prototype.toCSSString = function() { return this.value.toCSSString("\n"); }

function AtRule(name) {
    this.name = name;
    this.prelude = new TokenList();
    this.value = null;
    return this;
}
AtRule.prototype = new CSSParserRule;
AtRule.prototype.toCSSString = function() { 
    if(this.value) {
        return "@" + escapeIdent(this.name) + " " + this.prelude.toCSSString() + this.value.toCSSString(); 
    } else {
        return "@" + escapeIdent(this.name) + " " + this.prelude.toCSSString() + '; '; 
    }
}
AtRule.prototype.toStylesheet = function() {
    return this.asStylesheet || (this.asStylesheet = this.value ? parseAStylesheet(this.value.value) : new Stylesheet());
}

function StyleRule() {
    this.prelude = new TokenList(); this.selector = this.prelude;
    this.value = null;
    return this;
}
StyleRule.prototype = new CSSParserRule;
StyleRule.prototype.type = "STYLE-RULE";
StyleRule.prototype.toCSSString = function() {
    return this.prelude.toCSSString() + this.value.toCSSString();
}
StyleRule.prototype.getSelector = function() {
    return this.prelude;
}
StyleRule.prototype.getDeclarations = function() {
    if(!(this.value instanceof SimpleBlock)) { return new TokenList(); }
    var value = this.value.value; return parseAListOfDeclarations(value);
}


function Declaration(name) {
    this.name = name;
    this.value = new TokenList();
    this.important = false;
    return this;
}
Declaration.prototype = new CSSParserRule;
Declaration.prototype.type = "DECLARATION";
Declaration.prototype.toCSSString = function() {
    return this.name + ':' + this.value.toCSSString() + '; ';
}

function SimpleBlock(type) {
    this.name = type;
    this.value = new TokenList();
    return this;
}
SimpleBlock.prototype = new CSSParserRule;
SimpleBlock.prototype.type = "BLOCK";
SimpleBlock.prototype.toCSSString = function() {
    switch(this.name) {
        case "(":
            return "(" + this.value.toCSSString() + ")";
            
        case "[":
            return "[" + this.value.toCSSString() + "]";
            
        case "{":
            return "{" + this.value.toCSSString() + "}";
        
        default: //best guess
            return this.name + this.value.toCSSString() + this.name;
    }
}

function Func(name) {
    this.name = name;
    this.value = new TokenList();
    return this;
}
Func.prototype = new CSSParserRule;
Func.prototype.type = "FUNCTION";
Func.prototype.toCSSString = function() {
    return this.name+'('+this.value.toCSSString()+')';
}
Func.prototype.getArguments = function() {
    var args = new TokenList(); var arg = new TokenList(); var value = this.value;
    for(var i = 0; i<value.length; i++) {
        if(value[i].tokenType == ',') {
            args.push(arg); arg = new TokenList();
        } else {
            arg.push(value[i])
        }
    }
    if(args.length > 0 || arg.length > 0) { args.push(arg); }
    return args;
}

function FuncArg() {
    this.value = new TokenList();
    return this;
}
FuncArg.prototype = new CSSParserRule;
FuncArg.prototype.type = "FUNCTION-ARG";
FuncArg.prototype.toCSSString = function() {
    return this.value.toCSSString()+', ';
}

// Exportation.
cssSyntax.CSSParserRule = CSSParserRule;
cssSyntax.Stylesheet = Stylesheet;
cssSyntax.AtRule = AtRule;
cssSyntax.StyleRule = StyleRule;
cssSyntax.Declaration = Declaration;
cssSyntax.SimpleBlock = SimpleBlock;
cssSyntax.Func = Func;
cssSyntax.parseAStylesheet = parseAStylesheet;
cssSyntax.parseAListOfRules = parseAListOfRules;
cssSyntax.parseARule = parseARule;
cssSyntax.parseADeclaration = parseADeclaration;
cssSyntax.parseAListOfDeclarations = parseAListOfDeclarations;
cssSyntax.parseAComponentValue = parseAComponentValue;
cssSyntax.parseAListOfComponentValues = parseAListOfComponentValues;
cssSyntax.parseACommaSeparatedListOfComponentValues = parseACommaSeparatedListOfComponentValues;
cssSyntax.parse = parseAStylesheet;
cssSyntax.parseCSSValue = parseAListOfComponentValues;

return cssSyntax;

}());

require.define('src/core/css-syntax.js');

////////////////////////////////////////

void function() {
    
    // request animation frame
    var vendors = ['webkit', 'moz', 'ms', 'o'];
    for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
        var vp = vendors[i];
        window.requestAnimationFrame = window[vp+'RequestAnimationFrame'];
        window.cancelAnimationFrame = (window[vp+'CancelAnimationFrame'] || window[vp+'CancelRequestAnimationFrame']);
    }
    if (!window.requestAnimationFrame || !window.cancelAnimationFrame) {
        
        // tick every 16ms
        var listener_index = 0; var listeners = []; var tmp = []; var tick = function() {
            var now = +(new Date()); var callbacks = listeners; listeners = tmp;
            for(var i = 0; i<callbacks.length; i++) { callbacks[i](now); }
            listener_index += callbacks.length; callbacks.length = 0; tmp = callbacks;
            setTimeout(tick, 16);
        }; tick();
        
        // add a listener
        window.requestAnimationFrame = function(callback) {
            return listener_index + listeners.push(callback);
        };
        
        // remove a listener
        window.cancelAnimationFrame = function(index) {
            index -= listener_index; if(index >= 0 && index < listeners.length) {
                listeners[index] = function() {};
            }
        };
        
    }
    
    // setImmediate
    if(!window.setImmediate) {
        window.setImmediate = function(f) { return setTimeout(f, 0) };
        window.cancelImmediate = clearTimeout;
    }
    
}();

require.define('src/core/polyfill-dom-requestAnimationFrame.js');

////////////////////////////////////////

/////////////////////////////////////////////////////////////////
////                                                         ////
////                 prerequirements of qSL                  ////
////                                                         ////
/////////////////////////////////////////////////////////////////
////                                                         ////
////   Please note that I require querySelectorAll to work   ////
////                                                         ////
////   See http://github.com/termi/CSS_selector_engine/      ////
////   for a polyfill for older browsers                     ////
////                                                         ////
/////////////////////////////////////////////////////////////////

// TODO: improve event streams
// - look for a few optimizations ideas in gecko/webkit
// - use arrays in CompositeEventStream to avoid nested debouncings
module.exports = (function(window, document) { "use strict";

    ///
    /// event stream implementation
    /// please note this is required to 'live update' the qSA requests
    ///
    function EventStream(connect, disconnect, reconnect) {
        var self=this;
        
        // validate arguments
        if(!disconnect) disconnect=function(){};
        if(!reconnect) reconnect=connect;
        
        // high-level states
        var isConnected=false;
        var isDisconnected=false;
        var shouldDisconnect=false;
        
        // global variables
        var callback=null;
        var yieldEvent = function() {
            
            // call the callback function, and pend disposal
            shouldDisconnect=true;
            try { callback && callback(self); } catch(ex) { setImmediate(function() { throw ex; }); }
            
            // if no action was taken, dispose
            if(shouldDisconnect) { dispose(); }
            
        }
        
        // export the interface
        var schedule = this.schedule = function(newCallback) {
        
            // do not allow to schedule on disconnected event streams
            if(isDisconnected) { throw new Error("Cannot schedule on a disconnected event stream"); }
            
            // do not allow to schedule on already scheduled event streams
            if(isConnected && !shouldDisconnect) { throw new Error("Cannot schedule on an already-scheduled event stream"); }
            
            // schedule the new callback
            callback=newCallback; shouldDisconnect=false;
            
            // reconnect to the stream
            if(isConnected) {
                reconnect(yieldEvent);
            } else {
                connect(yieldEvent);
                isConnected=true;
            }
        }
        
        var dispose = this.dispose = function() {
        
            // do not allow to dispose non-connected streams
            if(isConnected) {
            
                // disconnect & save resources
                disconnect(); 
                self=null; yieldEvent=null; callback=null; 
                isConnected=false; isDisconnected=true; shouldDisconnect=false;
                
            }
        }
    }

    ///
    /// call a function every frame
    ///
    function AnimationFrameEventStream(options) {
        
        // flag that says whether the observer is still needed or not
        var rid = 0;
            
        // start the event stream
        EventStream.call(
            this, 
            function connect(yieldEvent) { rid = requestAnimationFrame(yieldEvent); },
            function disconnect() { cancelAnimationFrame(rid); }
        );
        
    }

    ///
    /// call a function every timeout
    ///
    function TimeoutEventStream(options) {
        
        // flag that says whether the observer is still needed or not
        var rid = 0; var timeout=(typeof(options)=="number") ? (+options) : ("timeout" in options ? +options.timeout : 333);
            
        // start the event stream
        EventStream.call(
            this, 
            function connect(yieldEvent) { rid = setTimeout(yieldEvent, timeout); },
            function disconnect() { clearTimeout(rid); }
        );
        
    }

    ///
    /// call a function every time the mouse moves
    ///
    function MouseEventStream() {
        var self=this; var pointermove = (("PointerEvent" in window) ? "pointermove" : (("MSPointerEvent" in window) ? "MSPointerMove" : "mousemove"));

        // flag that says whether the event is still observed or not
        var scheduled = false; var interval=0;
        
        // handle the synchronous nature of mutation events
        var yieldEvent=null;
        var yieldEventDelayed = function() {
            if(scheduled) return;
            window.removeEventListener(pointermove, yieldEventDelayed, true);
            scheduled = requestAnimationFrame(yieldEvent);
        }
        
        // start the event stream
        EventStream.call(
            this, 
            function connect(newYieldEvent) {
                yieldEvent=newYieldEvent;
                //window.addEventListener(pointermove, yieldEventDelayed, true);
            },
            function disconnect() { 
                //window.removeEventListener(pointermove, yieldEventDelayed, true);
                cancelAnimationFrame(scheduled); yieldEventDelayed=null; yieldEvent=null; scheduled=false;
            },
            function reconnect(newYieldEvent) { 
                yieldEvent=newYieldEvent; scheduled=false;
                //window.addEventListener(pointermove, yieldEventDelayed, true);
            }
        );
        
    }

    ///
    /// call a function every time the mouse is clicked/unclicked
    ///
    function MouseButtonEventStream() {
        var self=this; 
        var pointerup = (("PointerEvent" in window) ? "pointerup" : (("MSPointerEvent" in window) ? "MSPointerUp" : "mouseup"));
        var pointerdown = (("PointerEvent" in window) ? "pointerdown" : (("MSPointerEvent" in window) ? "MSPointerDown" : "mousedown"));

        // flag that says whether the event is still observed or not
        var scheduled = false; var interval=0;
        
        // handle the synchronous nature of mutation events
        var yieldEvent=null;
        var yieldEventDelayed = function() {
            if(scheduled) return;
            window.removeEventListener(pointerup, yieldEventDelayed, true);
            window.removeEventListener(pointerdown, yieldEventDelayed, true);
            scheduled = requestAnimationFrame(yieldEvent);
        }
        
        // start the event stream
        EventStream.call(
            this, 
            function connect(newYieldEvent) {
                yieldEvent=newYieldEvent;
                //window.addEventListener(pointerup, yieldEventDelayed, true);
                //window.addEventListener(pointerdown, yieldEventDelayed, true);
            },
            function disconnect() { 
                //window.removeEventListener(pointerup, yieldEventDelayed, true);
                //window.removeEventListener(pointerdown, yieldEventDelayed, true);
                cancelAnimationFrame(scheduled); yieldEventDelayed=null; yieldEvent=null; scheduled=false;
            },
            function reconnect(newYieldEvent) { 
                yieldEvent=newYieldEvent; scheduled=false;
                //window.addEventListener(pointerup, yieldEventDelayed, true);
                //window.addEventListener(pointerdown, yieldEventDelayed, true);
            }
        );
        
    }

    ///
    /// call a function whenever the DOM is modified
    ///
    var DOMUpdateEventStream;
    if("MutationObserver" in window) {
        DOMUpdateEventStream = function DOMUpdateEventStream(options) {
             
            // configuration of the observer
            if(options) {
                var target = "target" in options ? options.target : document.documentElement;
                var config = { 
                    subtree: "subtree" in options ? !!options.subtree : true, 
                    attributes: "attributes" in options ? !!options.attributes : true, 
                    childList: "childList" in options ? !!options.childList : true, 
                    characterData: "characterData" in options ? !!options.characterData : false
                };
            } else {
                var target = document.documentElement;
                var config = { 
                    subtree: true, 
                    attributes: true, 
                    childList: true, 
                    characterData: false
                };
            }
                                
            // start the event stream
            var observer = null;
            EventStream.call(
                this, 
                function connect(yieldEvent) { if(config) { observer=new MutationObserver(yieldEvent); observer.observe(target,config); target=null; config=null; } },
                function disconnect() { observer && observer.disconnect(); observer=null; },
                function reconnect() { observer.takeRecords(); }
            );

        }
    } else if("MutationEvent" in window) {
        DOMUpdateEventStream = function DOMUpdateEventStream(options) {
            var self=this;

            // flag that says whether the event is still observed or not
            var scheduled = false;
            
            // configuration of the observer
            if(options) {
                var target = "target" in options ? options.target : document.documentElement;
            } else {
                var target = document.documentElement;
            }
            
            // handle the synchronous nature of mutation events
            var yieldEvent=null;
            var yieldEventDelayed = function() {
                if(scheduled || !yieldEventDelayed) return;
                document.removeEventListener("DOMContentLoaded", yieldEventDelayed, false);
                document.removeEventListener("DOMContentLoaded", yieldEventDelayed, false);
                target.removeEventListener("DOMSubtreeModified", yieldEventDelayed, false);
                scheduled = requestAnimationFrame(yieldEvent);
            }
            
            // start the event stream
            EventStream.call(
                this, 
                function connect(newYieldEvent) {
                    yieldEvent=newYieldEvent;
                    document.addEventListener("DOMContentLoaded", yieldEventDelayed, false);
                    //target.addEventListener("DOMSubtreeModified", yieldEventDelayed, false);
                },
                function disconnect() { 
                    document.removeEventListener("DOMContentLoaded", yieldEventDelayed, false);
                    //target.removeEventListener("DOMSubtreeModified", yieldEventDelayed, false);
                    cancelAnimationFrame(scheduled); yieldEventDelayed=null; yieldEvent=null; scheduled=false;
                },
                function reconnect(newYieldEvent) { 
                    yieldEvent=newYieldEvent; scheduled=false;
                    //target.addEventListener("DOMSubtreeModified", yieldEventDelayed, false);
                }
            );
            
        }
    } else {
        DOMUpdateEventStream = AnimationFrameEventStream;
    }

    ///
    /// call a function every time the focus shifts
    ///
    function FocusEventStream() {
        var self=this;
        
        // handle the filtering nature of focus events
        var yieldEvent=null; var previousActiveElement=null; var previousHasFocus=false; var rid=0;
        var yieldEventDelayed = function() {
            
            // if the focus didn't change
            if(previousActiveElement==document.activeElement && previousHasFocus==document.hasFocus()) {
                
                // then do not generate an event
                setTimeout(yieldEventDelayed, 333); // focus that didn't move is expected to stay
                
            } else {
                
                // else, generate one & save config
                previousActiveElement=document.activeElement;
                previousHasFocus=document.hasFocus();
                yieldEvent();
                
            }
        }
        
        // start the event stream
        EventStream.call(
            this, 
            function connect(newYieldEvent) {
                yieldEvent=newYieldEvent;
                rid=setTimeout(yieldEventDelayed, 500); // let the document load
            },
            function disconnect() { 
                clearTimeout(rid); yieldEventDelayed=null; yieldEvent=null; rid=0;
            },
            function reconnect(newYieldEvent) { 
                yieldEvent=newYieldEvent;
                rid=setTimeout(yieldEventDelayed, 100); // focus by tab navigation moves fast
            }
        );
        
    }

    ///
    /// composite event stream
    /// because sometimes you need more than one event source
    ///
    function CompositeEventStream(stream1, stream2) {
        var self=this;
        
        // fields
        var yieldEvent=null; var s1=false, s2=false;
        var yieldEventWrapper=function(s) { 
            if(s==stream1) s1=true;
            if(s==stream2) s2=true;
            if(s1&&s2) return;
            yieldEvent(self);
        }
        
        // start the event stream
        EventStream.call(
            this, 
            function connect(newYieldEvent) {
                yieldEvent=newYieldEvent;
                stream1.schedule(yieldEventWrapper);
                stream2.schedule(yieldEventWrapper);
            },
            function disconnect() { 
                stream1.dispose();
                stream2.dispose();
            },
            function reconnect(newYieldEvent) { 
                yieldEvent=newYieldEvent;
                s1 && stream1.schedule(yieldEventWrapper);
                s2 && stream2.schedule(yieldEventWrapper);
                s1 = s2 = false;
            }
        );
    }
    
    return {
        EventStream:                EventStream,
        AnimationFrameEventStream:  AnimationFrameEventStream,
        TimeoutEventStream:         TimeoutEventStream,
        MouseEventStream:           MouseEventStream,
        MouseButtonEventStream:     MouseButtonEventStream,
        DOMUpdateEventStream:       DOMUpdateEventStream,
        FocusEventStream:           FocusEventStream,
        CompositeEventStream:       CompositeEventStream
    };

})(window, document);
require.define('src/core/dom-experimental-event-streams.js');

////////////////////////////////////////

/////////////////////////////////////////////////////////////////
////                                                         ////
////                  Implementation of qSL                  ////
////                                                         ////
/////////////////////////////////////////////////////////////////
////                                                         ////
////   Please note that I require querySelectorAll to work   ////
////                                                         ////
////   See http://github.com/termi/CSS_selector_engine/      ////
////   for a polyfill for older browsers                     ////
////                                                         ////
/////////////////////////////////////////////////////////////////

module.exports = (function(window, document) { "use strict";

    // import dependencies
    var eventStreams = require('src/core/dom-experimental-event-streams.js'),
        DOMUpdateEventStream = eventStreams.DOMUpdateEventStream,
        AnimationFrameEventStream = eventStreams.AnimationFrameEventStream,
        CompositeEventStream = eventStreams.CompositeEventStream,
        FocusEventStream = eventStreams.FocusEventStream,
        MouseButtonEventStream = eventStreams.MouseButtonEventStream,
        TimeoutEventStream = eventStreams.TimeoutEventStream,
        MouseEventStream = eventStreams.MouseEventStream;

    ///
    /// the live querySelectorAll implementation
    ///
    function querySelectorLive(selector, handler, root) {
        
        // restrict the selector coverage to some part of the DOM only
        var root = root || document;
        
        // TODO: make use of "mutatedAncestorElement" to update only elements inside the mutated zone
        
        var currentElms = [];
        var loop = function loop(eventStream) {
            
            // schedule next run
            eventStream.schedule(loop);
            
            // update elements matching the selector
            var newElms = [];
            var oldElms = currentElms.slice(0);
            var temps = root.querySelectorAll(selector);
            for(var i=newElms.length=temps.length; i;) { newElms.push(temps[--i]); }
            currentElms = newElms.slice(0); temps=null;
            
            // first let's clear all elements that have been removed from the document
            oldElms = oldElms.filter(function(e) {
                
                // check whether the current element is still there
                var isStillInDocument = (
                    e===document.documentElement 
                    || document.documentElement.contains(e)
                );
                
                if(isStillInDocument) {
                    
                    // NEED_COMPARE: we will compare this element to the new list
                    return true;
                    
                } else {
                    
                    // DELETE: raise onremoved, pop old elements
                    try { handler.onremoved && handler.onremoved(e); } catch(ex) { setImmediate(function() {throw ex})}
                    return false;
                    
                }
                
            });
            
            // now pop and match until both lists are exhausted
            // (we use the fact the returned elements are in document order)
            var el1 = oldElms.pop();
            var el2 = newElms.pop();
            while(el1 || el2) {
                if(el1===el2) {
                
                    // MATCH: pop both elements
                    el1 = oldElms.pop();
                    el2 = newElms.pop();
                    
                } else if (el2 && /*el1 is after el2*/(!el1||(el2.compareDocumentPosition(el1) & (1|2|8|32))===0)) {
                    
                    // INSERT: raise onadded, pop new elements
                    try { handler.onadded && handler.onadded(el2); } catch(ex) { setImmediate(function() {throw ex})}
                    el2 = newElms.pop();
                    
                } else {
                
                    // DELETE: raise onremoved, pop old elements
                    try { handler.onremoved && handler.onremoved(el1); } catch(ex) { setImmediate(function() {throw ex})}
                    el1 = oldElms.pop();
                    
                }
            }
            
        };
        
        // use the event stream that best matches our needs
        var simpleSelector = selector.replace(/:(dir|lang|root|empty|blank|nth-child|nth-last-child|first-child|last-child|only-child|nth-of-type|nth-last-of-child|fist-of-type|last-of-type|only-of-type|not|matches|default)\b/gi,'')
        var eventStream; if(simpleSelector.indexOf(':') == -1) {
            
            // static stuff only
            eventStream = new DOMUpdateEventStream({target:root}); 
            
        } else {
            
            // dynamic stuff too
            eventStream = new DOMUpdateEventStream({target:root}); 
            if(DOMUpdateEventStream != AnimationFrameEventStream) {
            
                // detect the presence of focus-related pseudo-classes
                var reg = /:(focus|active)\b/gi;
                if(reg.test(simpleSelector)) {
                    
                    // mouse events should be listened
                    eventStream = new CompositeEventStream(
                        new FocusEventStream(),
                        eventStream
                    );
                    
                    // simplify simpleSelector
                    var reg = /:(focus)\b/gi;
                    simpleSelector = simpleSelector.replace(reg, ''); // :active has other hooks
                    
                }
                
                // detect the presence of mouse-button-related pseudo-classes
                var reg = /:(active)\b/gi;
                if(reg.test(simpleSelector)) {
                    
                    // mouse events should be listened
                    eventStream = new CompositeEventStream(
                        new MouseButtonEventStream(),
                        eventStream
                    );
                    
                    // simplify simpleSelector
                    simpleSelector = simpleSelector.replace(reg, '');
                    
                }

                // detect the presence of user input pseudo-classes
                var reg = /:(target|checked|indeterminate|valid|invalid|in-range|out-of-range|user-error)\b/gi;
                if(reg.test(simpleSelector)) {
                    
                    // slowly dynamic stuff do happen
                    eventStream = new CompositeEventStream(
                        new TimeoutEventStream(250),
                        eventStream
                    );
                    
                    // simplify simpleSelector
                    simpleSelector = simpleSelector.replace(reg, '');

                    var reg = /:(any-link|link|visited|local-link|enabled|disabled|read-only|read-write|required|optional)\b/gi;
                    // simplify simpleSelector
                    simpleSelector = simpleSelector.replace(reg, '');
                    
                }
                
                // detect the presence of nearly-static pseudo-classes
                var reg = /:(any-link|link|visited|local-link|enabled|disabled|read-only|read-write|required|optional)\b/gi;
                if(reg.test(simpleSelector)) {
                    
                    // nearly static stuff do happen
                    eventStream = new CompositeEventStream(
                        new TimeoutEventStream(333),
                        eventStream
                    );
                    
                    // simplify simpleSelector
                    simpleSelector = simpleSelector.replace(reg, '');
                    
                }
                
                // detect the presence of mouse-related pseudo-classes
                var reg = /:(hover)\b/gi;
                if(reg.test(simpleSelector)) {
                    
                    // mouse events should be listened
                    eventStream = new CompositeEventStream(
                        new MouseEventStream(),
                        eventStream
                    );
                    
                    // simplify simpleSelector
                    simpleSelector = simpleSelector.replace(reg, '');
                    
                }
                
                // detect the presence of unknown pseudo-classes
                if(simpleSelector.indexOf(':') !== -1) {
                    
                    // other stuff do happen, too (let's give up on events)
                    eventStream = new AnimationFrameEventStream(); 
                    
                }
                
            }
            
        }
        
        // start handling changes
        loop(eventStream);
        
    }
    
    return querySelectorLive;
    
})(window, document);
require.define('src/core/dom-query-selector-live.js');

////////////////////////////////////////

// TODO: comment about the 'no_auto_stylesheet_detection' flag?

module.exports = (function(window, document) { "use strict";
    
    // import dependencies
    require('src/core/polyfill-dom-console.js');
    require('src/core/polyfill-dom-requestAnimationFrame.js');
    var cssSyntax = require('src/core/css-syntax.js');
    var domEvents = require('src/core/dom-events.js');
    var querySelectorLive = require('src/core/dom-query-selector-live.js');
    
    // define the module
    var cssCascade = {
        
        //
        // returns the priority of a unique selector (NO COMMA!)
        // { the return value is an integer, with the same formula as webkit }
        //
        computeSelectorPriorityOf: function computeSelectorPriorityOf(selector) {
            if(typeof selector == "string") selector = cssSyntax.parse(selector.trim()+"{}").value[0].selector;
            
            var numberOfIDs = 0;
            var numberOfClasses = 0;
            var numberOfTags = 0;
            
            // TODO: improve this parser, or find one on the web
            for(var i = 0; i < selector.length; i++) {
                
                if(selector[i] instanceof cssSyntax.IdentifierToken) {
                    numberOfTags++;
                    
                } else if(selector[i] instanceof cssSyntax.DelimToken) {
                    if(selector[i].value==".") {
                        numberOfClasses++; i++;
                    }
                    
                } else if(selector[i] instanceof cssSyntax.ColonToken) {
                    if(selector[++i] instanceof cssSyntax.ColonToken) {
                        numberOfTags++; i++;
                        
                    } else if((selector[i] instanceof cssSyntax.Func) && (/^(not|matches)$/i).test(selector[i].name)) {
                        var nestedPriority = this.computeSelectorPriorityOf(selector[i].value);
                        numberOfTags += nestedPriority % 256; nestedPriority /= 256;
                        numberOfClasses += nestedPriority % 256; nestedPriority /= 256;
                        numberOfIDs += nestedPriority;
                        
                    } else {
                        numberOfClasses++;
                        
                    }
                    
                } else if(selector[i] instanceof cssSyntax.SimpleBlock) {
                    if(selector[i].name=="[") {
                        numberOfClasses++;
                    }
                    
                } else if(selector[i] instanceof cssSyntax.HashToken) {
                    numberOfIDs++;
                    
                } else {
                    // TODO: stop ignoring unknown symbols?
                    
                }
                
            }
            
            if(numberOfIDs>255) numberOfIds=255;
            if(numberOfClasses>255) numberOfClasses=255;
            if(numberOfTags>255) numberOfTags=255;
            
            return ((numberOfIDs*256)+numberOfClasses)*256+numberOfTags;
            
        },
        
        //
        // returns an array of the css rules matching an element
        //
        findAllMatchingRules: function findAllMatchingRules(element) {
            return this.findAllMatchingRulesWithPseudo(element);
        },
        
        //
        // returns an array of the css rules matching a pseudo-element
        //
        findAllMatchingRulesWithPseudo: function findAllMatchingRules(element,pseudo) {
            pseudo = pseudo ? (''+pseudo).toLowerCase() : pseudo;
            
            // let's look for new results if needed...
            var results = [];
            
            // walk the whole stylesheet...
            var visit = function(rules) {
                try {
                    for(var r = rules.length; r--; ) {
                        var rule = rules[r]; 
                        
                        // media queries hook
                        if(rule.disabled) continue;
                        
                        if(rule instanceof cssSyntax.StyleRule) {
                            
                            // consider each selector independently
                            var subrules = rule.subRules || cssCascade.splitRule(rule);
                            for(var sr = subrules.length; sr--; ) {
                                
                                var selector = subrules[sr].selector.toCSSString().replace(/ *(\/\*\*\/|  ) */g,' ').trim();
                                if(pseudo) {
                                    // WE ONLY ACCEPT SELECTORS ENDING WITH THE PSEUDO
                                    var selectorLow = selector.toLowerCase();
                                    var newLength = selector.length-pseudo.length-1;
                                    if(newLength<=0) continue;
                                    
                                    if(selectorLow.lastIndexOf('::'+pseudo)==newLength-1) {
                                        selector = selector.substr(0,newLength-1);
                                    } else if(selectorLow.lastIndexOf(':'+pseudo)==newLength) {
                                        selector = selector.substr(0,newLength);
                                    } else {
                                        continue;
                                    }
                                    
                                    // fix selectors like "#element > :first-child ~ ::before"
                                    if(selector.trim().length == 0) { selector = '*' }
                                    else if(selector[selector.length-1] == ' ') { selector += '*' }
                                    else if(selector[selector.length-1] == '+') { selector += '*' }
                                    else if(selector[selector.length-1] == '>') { selector += '*' }
                                    else if(selector[selector.length-1] == '~') { selector += '*' }
                                    
                                }
                                
                                // look if the selector matches
                                var isMatching = false;
                                try {
                                    if(element.matches) isMatching=element.matches(selector)
                                    else if(element.matchesSelector) isMatching=element.matchesSelector(selector)
                                    else if(element.oMatchesSelector) isMatching=element.oMatchesSelector(selector)
                                    else if(element.msMatchesSelector) isMatching=element.msMatchesSelector(selector)
                                    else if(element.mozMatchesSelector) isMatching=element.mozMatchesSelector(selector)
                                    else if(element.webkitMatchesSelector) isMatching=element.webkitMatchesSelector(selector)
                                    else { throw new Error("no element.matches?") }
                                } catch(ex) { /*debugger; setImmediate(function() { throw ex; })*/ }
                                
                                // if yes, add it to the list of matched selectors
                                if(isMatching) { results.push(subrules[sr]); }
                                
                            }
                            
                        } else if(rule instanceof cssSyntax.AtRule && rule.name=="media") {
                            
                            // visit them
                            visit(rule.toStylesheet().value);
                            
                        }
                        
                    }
                } catch (ex) {
                    setImmediate(function() { throw ex; });
                }
            }
            
            for(var s=cssCascade.stylesheets.length; s--; ) {
                var rules = cssCascade.stylesheets[s];
                visit(rules);
            }
            
            return results;
        },
        
        //
        // a list of all properties supported by the current browser
        //
        allCSSProperties: null,
        getAllCSSProperties: function getAllCSSProperties() {
            
            if(this.allCSSProperties) return this.allCSSProperties;
            
            // get all claimed properties
            var s = getComputedStyle(document.documentElement); var ps = new Array(s.length);
            for(var i=s.length; i--; ) {
                ps[i] = s[i];
            }
            
            // FIX A BUG WHERE WEBKIT DOESN'T REPORT ALL PROPERTIES
            if(ps.indexOf('content')==-1) {ps.push('content');}
            if(ps.indexOf('counter-reset')==-1) {
                
                ps.push('counter-reset');
                ps.push('counter-increment');
                
                // FIX A BUG WHERE WEBKIT RETURNS SHIT FOR THE COMPUTED VALUE OF COUNTER-RESET
                cssCascade.computationUnsafeProperties['counter-reset']=true;
                
            }
            
            // save in a cache for faster access the next times
            return this.allCSSProperties = ps;
            
        },
        
        // 
        // those properties are not safe for computation->specified round-tripping
        // 
        computationUnsafeProperties: {
            "bottom"          : true,
            "direction"       : true,
            "display"         : true,
            "font-size"       : true,
            "height"          : true,
            "left"            : true,
            "line-height"     : true,
            "margin-left"     : true,
            "margin-right"    : true,
            "margin-bottom"   : true,
            "margin-top"      : true,
            "max-height"      : true,
            "max-width"       : true,
            "min-height"      : true,
            "min-width"       : true,
            "padding-left"    : true,
            "padding-right"   : true,
            "padding-bottom"  : true,
            "padding-top"     : true,
            "right"           : true,
            "text-align"      : true,
            "text-align-last" : true,
            "top"             : true,
            "width"           : true,
            __proto__         : null,
        },
        
        //
        // a list of property we should inherit...
        //
        inheritingProperties: {
            "border-collapse"       : true,
            "border-spacing"        : true,
            "caption-side"          : true,
            "color"                 : true,
            "cursor"                : true,
            "direction"             : true,
            "empty-cells"           : true,
            "font-family"           : true,
            "font-size"             : true,
            "font-style"            : true,
            "font-variant"          : true,
            "font-weight"           : true,
            "font"                  : true,
            "letter-spacing"        : true,
            "line-height"           : true,
            "list-style-image"      : true,
            "list-style-position"   : true,
            "list-style-type"       : true,
            "list-style"            : true,
            "orphans"               : true,
            "quotes"                : true,
            "text-align"            : true,
            "text-indent"           : true,
            "text-transform"        : true,
            "visibility"            : true,
            "white-space"           : true,
            "widows"                : true,
            "word-break"            : true,
            "word-spacing"          : true,
            "word-wrap"             : true,
            __proto__               : null,
        },
        
        //
        // returns the default style for a tag
        //
        defaultStylesForTag: Object.create ? Object.create(null) : {},
        getDefaultStyleForTag: function getDefaultStyleForTag(tagName) {
            
            // get result from cache
            var result = this.defaultStylesForTag[tagName];
            if(result) return result;
            
            // create dummy virtual element
            var element = document.createElement(tagName);
            var style = this.defaultStylesForTag[tagName] = getComputedStyle(element);
            if(style.display) return style;
            
            // webkit fix: insert the dummy element anywhere (head -> display:none)
            document.head.insertBefore(element, document.head.firstChild);
            return style;
        },
        
        // 
        // returns the specified style of an element. 
        // REMARK: may or may not unwrap "inherit" and "initial" depending on implementation
        // REMARK: giving "matchedRules" as a parameter allow you to mutualize the "findAllMatching" rules calls
        // 
        getSpecifiedStyle: function getSpecifiedStyle(element, cssPropertyName, matchedRules) {
            
            // hook for css regions
            var fragmentSource;
            if(fragmentSource=element.getAttribute('data-css-regions-fragment-of')) {
                fragmentSource = document.querySelector('[data-css-regions-fragment-source="'+fragmentSource+'"]');
                if(fragmentSource) return cssCascade.getSpecifiedStyle(fragmentSource, cssPropertyName);
            }
            
            // give IE a thumbs up for this!
            if(element.currentStyle && !window.opera) {
                
                // ask IE to manage the style himself...
                var bestValue = element.myStyle[cssPropertyName] || element.currentStyle[cssPropertyName] || '';
                
                // return a parsed representation of the value
                return cssSyntax.parseAListOfComponentValues(bestValue);
                
            } else {
                
                // TODO: support the "initial" and "inherit" things?
                
                // first, let's try inline style as it's fast and generally accurate
                // TODO: what if important rules override that?
                try {
                    if(bestValue = element.style.getPropertyValue(cssPropertyName) || element.myStyle[cssPropertyName]) {
                        return cssSyntax.parseAListOfComponentValues(bestValue);
                    }
                } catch(ex) {}
                
                // find all relevant style rules
                var isBestImportant=false; var bestPriority = 0; var bestValue = new cssSyntax.TokenList();
                var rules = matchedRules || (
                    cssPropertyName in cssCascade.monitoredProperties
                    ? element.myMatchedRules || []
                    : cssCascade.findAllMatchingRules(element)
                );
                
                var visit = function(rules) {
                    
                    for(var i=rules.length; i--; ) {
                        
                        // media queries hook
                        if(rules[i].disabled) continue;
                        
                        // find a relevant declaration
                        if(rules[i] instanceof cssSyntax.StyleRule) {
                            var decls = rules[i].getDeclarations();
                            for(var j=decls.length-1; j>=0; j--) {
                                if(decls[j].type=="DECLARATION") {
                                    if(decls[j].name==cssPropertyName) {
                                        // only works if selectors containing a "," are deduplicated
                                        var currentPriority = cssCascade.computeSelectorPriorityOf(rules[i].selector);
                                        
                                        if(isBestImportant) {
                                            // only an important declaration can beat another important declaration
                                            if(decls[j].important) {
                                                if(currentPriority >= bestPriority) {
                                                    bestPriority = currentPriority;
                                                    bestValue = decls[j].value;
                                                }
                                            }
                                        } else {
                                            // an important declaration beats any non-important declaration
                                            if(decls[j].important) {
                                                isBestImportant = true;
                                                bestPriority = currentPriority;
                                                bestValue = decls[j].value;
                                            } else {
                                                // the selector priority has to be higher otherwise
                                                if(currentPriority >= bestPriority) {
                                                    bestPriority = currentPriority;
                                                    bestValue = decls[j].value;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        } else if((rules[i] instanceof cssSyntax.AtRule) && (rules[i].name=="media")) {
                            
                            // visit them
                            visit(rules[i].toStylesheet())
                            
                        }
                        
                    }
                    
                }
                visit(rules);
                
                // return our best guess...
                return bestValue||null;
                
            }
            
        },
        
        
        //
        // start monitoring a new stylesheet
        // (should usually not be used because stylesheets load automatically)
        //
        stylesheets: [],
        loadStyleSheet: function loadStyleSheet(cssText,i) {
            
            // load in order
            
            // parse the stylesheet content
            var rules = cssSyntax.parse(cssText).value;
            
            // add the stylesheet into the object model
            if(typeof(i)!=="undefined") { cssCascade.stylesheets[i]=rules; } 
            else { i=cssCascade.stylesheets.push(rules);}
            
            // make sure to monitor the required rules
            cssCascade.startMonitoringStylesheet(rules)
            
        },
        
        //
        // start monitoring a new stylesheet
        // (should usually not be used because stylesheets load automatically)
        //
        loadStyleSheetTag: function loadStyleSheetTag(stylesheet,i) {
            
            if(stylesheet.hasAttribute('data-css-polyfilled')) {
                return;
            }
            
            if(stylesheet.tagName=='LINK') {
                
                // oh, no, we have to download it...
                try {
                    
                    // dummy value in-between
                    cssCascade.stylesheets[i] = new cssSyntax.TokenList();
                    
                    //
                    var xhr = new XMLHttpRequest(); xhr.href = stylesheet.href;
                    xhr.open('GET',stylesheet.href,true); xhr.ruleIndex = i; 
                    xhr.onreadystatechange = function() {
                        if(this.readyState==4) { 
                            
                            // status 0 is a webkit bug for local files
                            if(this.status==200||this.status==0) {
                                cssCascade.loadStyleSheet(this.responseText,this.ruleIndex)
                            } else {
                                cssConsole.log("css-cascade polyfill failled to load: " + this.href);
                            }
                        }
                    };
                    xhr.send();
                    
                } catch(ex) {
                    cssConsole.log("css-cascade polyfill failled to load: " + stylesheet.href);
                }
                
            } else {
                
                // oh, cool, we just have to parse the content!
                cssCascade.loadStyleSheet(stylesheet.textContent,i);
                
            }
            
            // mark the stylesheet as ok
            stylesheet.setAttribute('data-css-polyfilled',true);
            
        },
        
        //
        // calling this function will load all currently existing stylesheets in the document
        // (should usually not be used because stylesheets load automatically)
        //
        selectorForStylesheets: "style:not([data-no-css-polyfill]):not([data-css-polyfilled]), link[rel=stylesheet]:not([data-no-css-polyfill]):not([data-css-polyfilled])",
        loadAllStyleSheets: function loadAllStyleSheets() {
            
            // for all stylesheets in the <head> tag...
            var head = document.head || document.documentElement;
            var stylesheets = head.querySelectorAll(cssCascade.selectorForStylesheets);
            
            var intialLength = this.stylesheets.length;
            this.stylesheets.length += stylesheets.length
            
            // for all of them...
            for(var i = stylesheets.length; i--;) {
                
                // 
                // load the stylesheet
                // 
                var stylesheet = stylesheets[i]; 
                cssCascade.loadStyleSheetTag(stylesheet,intialLength+i)
                
            }
        },
        
        //
        // this is where we store event handlers for monitored properties
        //
        monitoredProperties: Object.create ? Object.create(null) : {},
        monitoredPropertiesHandler: {
            onupdate: function(element, rule) {
                
                // we need to find all regexps that matches
                var mps = cssCascade.monitoredProperties;
                var decls = rule.getDeclarations();
                for(var j=decls.length-1; j>=0; j--) {
                    if(decls[j].type=="DECLARATION") {
                        if(decls[j].name in mps) {
                            
                            // call all handlers waiting for this
                            var hs = mps[decls[j].name];
                            for(var hi=hs.length; hi--;) {
                                hs[hi].onupdate(element,rule);
                            };
                            
                            // don't call twice
                            break;
                            
                        }
                    }
                }
                
            }
        },
        
        //
        // add an handler to some properties (aka fire when their value *MAY* be affected)
        // REMARK: because this event does not promise the value changed, you may want to figure it out before relayouting
        //
        startMonitoringProperties: function startMonitoringProperties(properties, handler) {
            
            for(var i=properties.length; i--; ) {
                var property = properties[i];
                var handlers = (
                    cssCascade.monitoredProperties[property]
                    || (cssCascade.monitoredProperties[property] = [])
                );
                handlers.push(handler)
            }
            
            for(var s=0; s<cssCascade.stylesheets.length; s++) {
                var currentStylesheet = cssCascade.stylesheets[s];
                cssCascade.startMonitoringStylesheet(currentStylesheet);
            }
            
        },
        
        //
        // calling this function will detect monitored rules in the stylesheet
        // (should usually not be used because stylesheets load automatically)
        //
        startMonitoringStylesheet: function startMonitoringStylesheet(rules) {
            for(var i=0; i<rules.length; i++) {
                
                // only consider style rules
                if(rules[i] instanceof cssSyntax.StyleRule) {
                    
                    // try to see if the current rule is worth monitoring
                    if(rules[i].isMonitored) continue;
                    
                    // for that, let's see if we can find a declaration we should watch
                    var decls = rules[i].getDeclarations();
                    for(var j=decls.length-1; j>=0; j--) {
                        if(decls[j].type=="DECLARATION") {
                            if(decls[j].name in cssCascade.monitoredProperties) {
                                
                                // if we found some, start monitoring
                                cssCascade.startMonitoringRule(rules[i]);
                                break;
                                
                            }
                        }
                    }
                    
                } else if(rules[i] instanceof cssSyntax.AtRule) {
                    
                    // handle @media
                    if(rules[i].name == "media" && window.matchMedia) {
                        
                        cssCascade.startMonitoringMedia(rules[i]);
                        
                    }
                    
                }
                
            }
        },
        
        //
        // calling this function will detect media query updates and fire events accordingly
        // (should usually not be used because stylesheets load automatically)
        //
        startMonitoringMedia: function startMonitoringMedia(atrule) {
            try {
                
                var media = window.matchMedia(atrule.prelude.toCSSString());
                
                // update all the rules when needed
                var rules = atrule.toStylesheet().value;
                cssCascade.updateMedia(rules, !media.matches, false);
                media.addListener(
                    function(newMedia) { cssCascade.updateMedia(rules, !newMedia.matches, true); }
                );
                
                // it seems I like taking risks...
                cssCascade.startMonitoringStylesheet(rules);
                
            } catch(ex) {
                setImmediate(function() { throw ex; })
            }
        },
        
        //
        // define what happens when a media query status changes
        //
        updateMedia: function(rules,disabled,update) {
            for(var i=rules.length; i--; ) {
                rules[i].disabled = disabled;
                // TODO: should probably get handled by a setter on the rule...
                var sr = rules[i].subRules;
                if(sr) {
                    for(var j=sr.length; j--; ) {
                        sr[j].disabled = disabled;
                    }
                }
            }
            
            // in case of update, all elements matching the selector went potentially updated...
            if(update) {
                for(var i=rules.length; i--; ) {
                    var els = document.querySelectorAll(rules[i].selector.toCSSString());
                    for(var j=els.length; j--; ) {
                        cssCascade.monitoredPropertiesHandler.onupdate(els[j],rules[i]);
                    }
                }
            }
        },
        
        // 
        // splits a rule if it has multiple selectors
        // 
        splitRule: function splitRule(rule) {
            
            // create an array for all the subrules
            var rules = [];
            
            // fill the array
            var currentRule = new cssSyntax.StyleRule(); currentRule.disabled=rule.disabled;
            for(var i=0; i<rule.selector.length; i++) {
                if(rule.selector[i] instanceof cssSyntax.DelimToken && rule.selector[i].value==",") {
                    currentRule.value = rule.value; rules.push(currentRule);
                    currentRule = new cssSyntax.StyleRule(); currentRule.disabled=rule.disabled;
                } else {
                    currentRule.selector.push(rule.selector[i])
                }
            }
            currentRule.value = rule.value; rules.push(currentRule);
            
            // save the result of the split as subrules
            return rule.subRules = rules;
            
        },
        
        // 
        // ask the css-selector implementation to notify changes for the rules
        // 
        startMonitoringRule: function startMonitoringRule(rule) {
            
            // avoid monitoring rules twice
            if(!rule.isMonitored) { rule.isMonitored=true } else { return; }
            
            // split the rule if it has multiple selectors
            var rules = rule.subRules || cssCascade.splitRule(rule);
            
            // monitor the rules
            for(var i=0; i<rules.length; i++) {
                rule = rules[i];
                querySelectorLive(rule.selector.toCSSString(), {
                    onadded: function(e) {
                        
                        // add the rule to the matching list of this element
                        (e.myMatchedRules = e.myMatchedRules || []).unshift(rule); // TODO: does not respect priority order
                        
                        // generate an update event
                        cssCascade.monitoredPropertiesHandler.onupdate(e, rule);
                        
                    },
                    onremoved: function(e) {
                        
                        // remove the rule from the matching list of this element
                        if(e.myMatchedRules) e.myMatchedRules.splice(e.myMatchedRules.indexOf(rule), 1);
                        
                        // generate an update event
                        cssCascade.monitoredPropertiesHandler.onupdate(e, rule);
                        
                    }
                });
            }
            
        },
        
        //
        // converts a css property name to a javascript name
        //
        toCamelCase: function toCamelCase(variable) { 
            return variable.replace(
                /-([a-z])/g, 
                function(str,letter) { 
                    return letter.toUpperCase();
                }
            );
        },
        
        //
        // add some magic code to support properties on the style interface
        //
        polyfillStyleInterface: function(cssPropertyName) {
            
            var prop = {
                
                get: function() {
                    
                    // check we know which element we work on
                    try { if(!this.parentElement) throw new Error("Please use the anHTMLElement.myStyle property to get polyfilled properties") }
                    catch(ex) { setImmediate(function() { throw ex; }); return ''; }
                    
                    try { 
                        // non-computed style: return the local style of the element
                        this.clip = (this.clip===undefined?'':this.clip);
                        return this.parentElement.getAttribute('data-style-'+cssPropertyName);
                    } catch (ex) {
                        // computed style: return the specified style of the element
                        var value = cssCascade.getSpecifiedStyle(this.parentElement, cssPropertyName, undefined, true);
                        return value && value.length>0 ? value.toCSSString() : '';
                    }
                    
                },
                
                set: function(v) {
                    
                    // check that the style is writable
                    this.clip = (this.clip===undefined?'':this.clip);

                    // check we know which element we work on
                    try { if(!this.parentElement) throw new Error("Please use the anHTMLElement.myStyle property to set polyfilled properties") }
                    catch(ex) { setImmediate(function() { throw ex; }); return; }
                    
                    // modify the local style of the element
                    if(this.parentElement.getAttribute('data-style-'+cssPropertyName) != v) {
                        this.parentElement.setAttribute('data-style-'+cssPropertyName,v);
                    }
                    
                }
                
            };
            
            var styleProtos = [];
            try { styleProtos.push(Object.getPrototypeOf(document.documentElement.style) || CSSStyleDeclaration); } catch (ex) {}
            //try { styleProtos.push(Object.getPrototypeOf(getComputedStyle(document.documentElement))); } catch (ex) {}
            //try { styleProtos.push(Object.getPrototypeOf(document.documentElement.currentStyle)); } catch (ex) {}
            //try { styleProtos.push(Object.getPrototypeOf(document.documentElement.runtimeStyle)); } catch (ex) {}
            //try { styleProtos.push(Object.getPrototypeOf(document.documentElement.specifiedStyle)); } catch (ex) {}
            //try { styleProtos.push(Object.getPrototypeOf(document.documentElement.cascadedStyle)); } catch (ex) {}
            //try { styleProtos.push(Object.getPrototypeOf(document.documentElement.usedStyle)); } catch (ex) {}
            
            for(var i = styleProtos.length; i--;) {
                var styleProto = styleProtos[i];
                Object.defineProperty(styleProto,cssPropertyName,prop);
                Object.defineProperty(styleProto,cssCascade.toCamelCase(cssPropertyName),prop);
            }
            cssCascade.startMonitoringRule(cssSyntax.parse('[style*="'+cssPropertyName+'"]{'+cssPropertyName+':attr(style)}').value[0]);
            cssCascade.startMonitoringRule(cssSyntax.parse('[data-style-'+cssPropertyName+']{'+cssPropertyName+':attr(style)}').value[0]);
            
            // add to the list of polyfilled properties...
            cssCascade.getAllCSSProperties().push(cssPropertyName);
            cssCascade.computationUnsafeProperties[cssPropertyName] = true;
            
        }
        
    };

    //
    // polyfill for browsers not support CSSStyleDeclaration.parentElement (all of them right now)
    //
    domEvents.EventTarget.implementsIn(cssCascade);
    Object.defineProperty(Element.prototype,'myStyle',{
        get: function() {
            var style = this.style; 
            if(!style.parentElement) style.parentElement = this;
            return style;
        }
    });

    //
    // load all stylesheets at the time the script is loaded
    // then do it again when all stylesheets are downloaded
    // and again if some style tag is added to the DOM
    //
    if(!("no_auto_stylesheet_detection" in window)) {
        
        cssCascade.loadAllStyleSheets();
        document.addEventListener("DOMContentLoaded", function() {
            cssCascade.loadAllStyleSheets();
            querySelectorLive(
                cssCascade.selectorForStylesheets,
                {
                    onadded: function(e) {
                        // TODO: respect DOM order?
                        cssCascade.loadStyleSheetTag(e);
                        cssCascade.dispatchEvent('stylesheetadded');
                    }
                }
            )
        })
    }
    
    return cssCascade;

})(window, document);
require.define('src/core/css-cascade.js');

////////////////////////////////////////

module.exports = (function(window, document) { "use strict"; 

    var cssSyntax = require('src/core/css-syntax.js');
    var cssCascade = require('src/core/css-cascade.js');
    
    var cssBreak = {

        //
        // returns true if an element is replaced 
        // (can't be broken because considered as an image in css layout)
        // 
        isReplacedElement: function isReplacedElement(element) {
            if(!(element instanceof Element)) return false;
            var replacedElementTags = /^(SVG|MATH|IMG|VIDEO|PICTURE|OBJECT|EMBED|IFRAME|TEXTAREA|BUTTON|INPUT)$/; // TODO: more
            return replacedElementTags.test(element.tagName);
        },
        
        // 
        // returns true if an element has a scrollbar or act on overflowing content
        // 
        isScrollable: function isScrollable(element, elementOverflow) {
            if(!(element instanceof Element)) return false;
            if(typeof(elementOverflow)=="undefined") elementOverflow = getComputedStyle(element).overflow;
            
            return (
                elementOverflow !== "visible"
                && elementOverflow !== "hidden"
            );
            
        },
        
        // 
        // returns true if the element is part of an inline flow
        // TextNodes definitely qualify, but also inline-block elements
        // 
        isSingleLineOfTextComponent: function(element, elementStyle, elementDisplay, elementPosition, isReplaced) {
            if(!(element instanceof Element)) return true;
            if(typeof(elementStyle)=="undefined") elementStyle = getComputedStyle(element);
            if(typeof(elementDisplay)=="undefined") elementDisplay = elementStyle.display;
            if(typeof(elementPosition)=="undefined") elementPosition = elementStyle.position;
            if(typeof(isReplaced)=="undefined") isReplaced = this.isReplacedElement(element);
            
            return (
                elementDisplay === "inline-block"
                || elementDisplay === "inline-table"
                || elementDisplay === "inline-flex"
                || elementDisplay === "inline-grid"
                // TODO: more
            ) && (
                elementPosition === "static"
                || elementPosition === "relative"
            );
            
        },
        
        // 
        // returns true if the element is part of an inline flow
        // TextNodes definitely qualify, but also inline-block elements
        // 
        hasAnyInlineFlow: function(element) {
            
            function countAsInline(element) {
                if(!(element instanceof Element)) return !(/^\s*$/.test(element.nodeValue));
                return !cssBreak.isOutOfFlowElement(element) && cssBreak.isSingleLineOfTextComponent(element);
            }
            
            // try to find any inline element
            var current = element.firstChild;
            while(current) {
                if(countAsInline(current)) return true;
                current = current.nextSibling;
            }
            
            // no inline element
            return false;
            
        },
        
        // 
        // returns true if the element breaks the inline flow
        // (the case of block elements, mostly)
        // 
        isLineBreakingElement: function(element, elementStyle, elementDisplay, elementPosition) {
            
            if(!(element instanceof Element)) return false;
            if(typeof(elementStyle)=="undefined") elementStyle = getComputedStyle(element);
            if(typeof(elementDisplay)=="undefined") elementDisplay = elementStyle.display;
            if(typeof(elementPosition)=="undefined") elementPosition = elementStyle.position;
            
            return (
                (
                    // in-flow bock elements
                    (elementDisplay === "block")
                    && !this.isOutOfFlowElement(element, elementStyle, elementDisplay, elementPosition)
                    
                ) || (
                    
                    // displayed <br> elements
                    element.tagName==="BR" && elementDisplay!=="none"
                    
                )
            );
        },
        
        // 
        // returns true if the element breaks the inline flow before him
        // (the case of block elements, mostly)
        // 
        isLinePreBreakingElement: function(element, elementStyle, elementDisplay, elementPosition) {
            if(!(element instanceof Element)) return false;

            var breakBefore = cssCascade.getSpecifiedStyle(element,'break-before').toCSSString();
            return (
                (breakBefore=="region"||breakBefore=="all") 
                || cssBreak.isLineBreakingElement(element, elementStyle, elementDisplay, elementPosition)
            );
            
        },
        
        // 
        // returns true if the element breaks the inline flow after him
        // (the case of block elements, mostly)
        // 
        isLinePostBreakingElement: function(element, elementStyle, elementDisplay, elementPosition) {
            if(!(element instanceof Element)) return false;
            
            var breakAfter = cssCascade.getSpecifiedStyle(element,'break-after').toCSSString();
            return (
                (breakAfter=="region"||breakAfter=="all") 
                || cssBreak.isLineBreakingElement(element, elementStyle, elementDisplay, elementPosition)
            );
            
        },
        
        // 
        // returns true if the element is outside any block/inline flow
        // (this the case of absolutely positioned elements, and floats)
        // 
        isOutOfFlowElement: function(element, elementStyle, elementDisplay, elementPosition, elementFloat) {
            if(!(element instanceof Element)) return false;
            if(typeof(elementStyle)=="undefined") elementStyle = getComputedStyle(element);
            if(typeof(elementDisplay)=="undefined") elementDisplay = elementStyle.display;
            if(typeof(elementPosition)=="undefined") elementPosition = elementStyle.position; 
            if(typeof(elementFloat)=="undefined") elementFloat = elementStyle.float || elementStyle.styleFloat || elementStyle.cssFloat;
            
            return (
                
                // positioned elements are out of the flow
                (elementPosition==="absolute"||elementPosition==="fixed")
                
                // floated elements as well
                || (elementFloat!=="none") 
                
                // not sure but let's say hidden elements as well
                || (elementDisplay==="none")
                
            );
            
        },
        
        // 
        // returns true if two sibling elements are in the same text line
        // (this function is not perfect, work with it with care)
        // 
        areInSameSingleLine: function areInSameSingleLine(element1, element2) {
            
            //
            // look for obvious reasons why it wouldn't be the case
            //
            
            // if the element are not direct sibling, we must use their inner siblings as well
            if(element1.nextSibling != element2) { 
                if(element2.nextSibling != element1) throw "I gave up!"; 
                var t = element1; element1=element2; element2=t;
            }
             
            // a block element is never on the same line as another element
            if(this.isLinePostBreakingElement(element1)) return false;
            if(this.isLinePreBreakingElement(element2)) return false;
            
            // if the previous element is out of flow, we may consider it as being part of the current line
            if(this.isOutOfFlowElement(element1)) return true;
            
            // if the current object is not a single line component, return false
            if(!this.isSingleLineOfTextComponent(element1)) return false;
            
            // 
            // compute the in-flow bounding rect of the two elements
            // 
            var element1box = Node.getBoundingClientRect(element1);
            var element2box = Node.getBoundingClientRect(element2);
            function shift(box,shiftX,shiftY) {
                return {
                    top: box.top+shiftY,
                    bottom: box.bottom+shiftY,
                    left: box.left+shiftX,
                    right: box.right+shiftX
                }
            }
            
            // we only need to shift elements
            if(element1 instanceof Element) {
                var element1Style = getComputedStyle(element1);
                element1box = shift(element1box, parseFloat(element1Style.marginLeft), parseFloat(element1Style.marginTop))
                if(element1Style.position=="relative") {
                    element1box = shift(element1box, parseFloat(element1Style.left), parseFloat(element1Style.top))
                }
            }
            
            // we only need to shift elements
            if(element2 instanceof Element) {
                var element2Style = getComputedStyle(element2);
                element2box = shift(element2box, parseFloat(element2Style.marginLeft), parseFloat(element2Style.marginTop))
                if(element2Style.position=="relative") {
                    element2box = shift(element2box, parseFloat(element2Style.left), parseFloat(element2Style.top))
                }
            }
            
            // order the nodes so that they are in left-to-right order
            // (this means invert their order in the case of right-to-left flow)
            var firstElement = getComputedStyle(element1.parentNode).direction=="rtl" ? element2box : element1box;
            var secondElement = getComputedStyle(element1.parentNode).direction=="rtl" ? element1box : element2box;
            
            // return true if both elements are have non-overlapping
            // margin- and position-corrected in-flow bounding rect
            // and if their relative position is the one of the current
            // flow (either rtl or ltr)
            return firstElement.right <= secondElement.left;
            
            // TODO: what about left-to-right + right-aligned text?
            // I should probably takes care of vertical position in this case to solve ambiguities
            
        },
        
        //
        // returns true if the element has "overflow: hidden" set on it, and actually overflows
        //
        isHiddenOverflowing: function isHiddenOverflowing(element, elementOverflow) {
            if(!(element instanceof Element)) return false;
            if(typeof(elementOverflow)=="undefined") elementOverflow = getComputedStyle(element).display;
            
            return (
                elementOverflow == "hidden" 
                && element.offsetHeight != element.scrollHeight // trust me that works
            );
            
        },
        
        //
        // returns true if the element has a border-radius that impacts his layout
        //
        hasBigRadius: function(element, elementStyle) {
            if(!(element instanceof Element)) return false;
            if(typeof(elementStyle)=="undefined") elementStyle = getComputedStyle(element);

            // if the browser supports radiuses {f### prefixes}
            if("borderTopLeftRadius" in elementStyle) {
                
                var tlRadius = parseFloat(elementStyle.borderTopLeftRadius);
                var trRadius = parseFloat(elementStyle.borderTopRightRadius);
                var blRadius = parseFloat(elementStyle.borderBottomLeftRadius);
                var brRadius = parseFloat(elementStyle.borderBottomRightRadius);
                
                // tiny radiuses (<15px) are tolerated anyway
                if(tlRadius < 15 && trRadius < 15 && blRadius < 15 && brRadius < 15) {
                    return false;
                }
                
                var tWidth = parseFloat(elementStyle.borderTopWidth);
                var bWidth = parseFloat(elementStyle.borderBottomWidth);
                var lWidth = parseFloat(elementStyle.borderLeftWidth);
                var rWidth = parseFloat(elementStyle.borderRightWidth);
                
                // make sure the radius itself is contained into the border
                
                if(tlRadius > tWidth) return true;
                if(tlRadius > lWidth) return true;
                
                if(trRadius > tWidth) return true;
                if(trRadius > rWidth) return true;
                
                if(blRadius > bWidth) return true;
                if(blRadius > lWidth) return true;
                
                if(brRadius > bWidth) return true;
                if(brRadius > rWidth) return true;
                
            }
            
            // all conditions were met
            return false;
        },
        
        //
        // return trus if the break-inside property is 'avoid' or 'avoid-region'
        //
        isBreakInsideAvoid: function isBreakInsideAvoid(element, elementStyle) {
            var breakInside = cssCascade.getSpecifiedStyle(element, 'break-inside', undefined, true).toCSSString().trim().toLowerCase(); 
            return (breakInside == "avoid" || breakInside == "avoid-region");
        },
        
        //
        // returns true if the element is unbreakable according to the spec
        // (and some of the expected limitations of HTML/CSS)
        //
        isMonolithic: function isMonolithic(element) {
            if(!(element instanceof Element)) return false;
            
            var elementStyle = getComputedStyle(element);
            var elementOverflow = elementStyle.overflow;
            var elementDisplay = elementStyle.display;
            
            // Some content is not fragmentable, for example:
            // - many types of replaced elements (such as images or video)
            
            var isReplaced = this.isReplacedElement(element);
            
            // - scrollable elements
            
            var isScrollable = this.isScrollable(element, elementOverflow);
            
            // - a single line of text content. 
            
            var isSingleLineOfText = this.isSingleLineOfTextComponent(element, elementStyle, elementDisplay, undefined, isReplaced);
            
            // Such content is considered monolithic: it contains no
            // possible break points. 
            
            // In addition to any content which is not fragmentable, 
            // UAs may consider as monolithic:
            // - any elements with ‘overflow’ set to ‘auto’ or ‘scroll’ 
            // - any elements with ‘overflow: hidden’ and a non-‘auto’ logical height (and no specified maximum logical height).
            
            var isHiddenOverflowing = this.isHiddenOverflowing(element, elementOverflow);
            
            // ADDITION TO THE SPEC:
            // I don't want to handle the case where 
            // an element has a border-radius that is bigger
            // than the border-width to which it belongs
            var hasBigRadius = this.hasBigRadius(element, elementStyle);
            
            // ADDITION TO THE SPEC:
            // Someone proposed to support "break-inside: avoid" here
            var isBreakInsideAvoid = this.isBreakInsideAvoid(element, elementStyle);
            
            // all of them are monolithic
            return isReplaced || isScrollable || isSingleLineOfText || isHiddenOverflowing || hasBigRadius || isBreakInsideAvoid;
            
        },
        
        // 
        // returns true if "r" is a collapsed range located at a possible break point for "region"
        // (this function does all the magic for you, but you may want to avoid using it too much)
        // 
        isPossibleBreakPoint: function isPossibleBreakPoint(r, region) {
            
            // r has to be a range, and be collapsed
            if(!(r instanceof Range)) return false;
            if(!(r.collapsed)) return false;
            
            // no ancestor up to the region has to be monolithic
            var ancestor = r.startContainer;
            while(ancestor && ancestor !== region) {
                if(cssBreak.isMonolithic(ancestor)) {
                    return false;
                }
                ancestor = ancestor.parentNode;
            }
            
            // we also have to check that we're not between two single-line-of-text elements
            // that are actually on the same line (in which case you can't break)
            var ancestor = r.startContainer; 
            var lastAncestor = r.startContainer.childNodes[r.startOffset];
            while(ancestor && lastAncestor !== region) {
                if(lastAncestor && lastAncestor.previousSibling) {
                    
                    if(this.areInSameSingleLine(lastAncestor, lastAncestor.previousSibling)) {
                        return false;
                    }
                    
                }
                
                lastAncestor = ancestor;
                ancestor = ancestor.parentNode;
            }
            
            // there are some very specific conditions for breaking
            // at the edge of an element:
            
            if(r.startOffset==0) {
                
                // Class 3 breaking point:
                // ========================
                // Between the content edge of a block container box 
                // and the outer edges of its child content (margin 
                // edges of block-level children or line box edges 
                // for inline-level children) if there is a (non-zero)
                // gap between them.
                
                var firstChild = r.startContainer.childNodes[0];
                if(firstChild) {
                    
                    var firstChildBox = (
                        Node.getBoundingClientRect(firstChild)
                    );
                    
                    var parentBox = (
                        r.startContainer.getBoundingClientRect()
                    );
                    
                    if(firstChildBox.top == parentBox.top) {
                        return false;
                    }
                    
                } else {
                    return false;
                }
                
            }
            
            // all conditions are met!
            return true;
            
        }
        
    };
    
    return cssBreak;
    
})(window, document);
require.define('src/core/css-break.js');

////////////////////////////////////////

"use strict";

//
// start by polyfilling caretRangeFromPoint
//

if(!document.caretRangeFromPoint) {
    if (document.caretPositionFromPoint) {
        document.caretRangeFromPoint = function caretRangeFromPoint(x,y) {
            var r = document.createRange();
            var p = document.caretPositionFromPoint(x,y); 
            if(p.offsetNode) {
                r.setStart(p.offsetNode, p.offset);
                r.setEnd(p.offsetNode, p.offset);
            }
            return r;
        }
    } else if((document.body||document.createElement('body')).createTextRange) {
        
        //
        // we may want to convert TextRange to Range
        //
        
        var TextRangeUtils = {
            convertToDOMRange: function (textRange, document) {
                var adoptBoundary = function(domRange, textRangeInner, bStart) {
                    // iterate backwards through parent element to find anchor location
                    var cursorNode = document.createElement('a'), cursor = textRangeInner.duplicate();
                    cursor.collapse(bStart);
                    var parent = cursor.parentElement();
                    do {
                            parent.insertBefore(cursorNode, cursorNode.previousSibling);
                            cursor.moveToElementText(cursorNode);
                    } while (cursor.compareEndPoints(bStart ? 'StartToStart' : 'StartToEnd', textRangeInner) > 0 && cursorNode.previousSibling);
                    
                    // when we exceed or meet the cursor, we've found the node
                    if (cursor.compareEndPoints(bStart ? 'StartToStart' : 'StartToEnd', textRangeInner) == -1 && cursorNode.nextSibling) {
                            // data node
                            cursor.setEndPoint(bStart ? 'EndToStart' : 'EndToEnd', textRangeInner);
                            domRange[bStart ? 'setStart' : 'setEnd'](cursorNode.nextSibling, cursor.text.length);
                    } else {
                            // element
                            domRange[bStart ? 'setStartBefore' : 'setEndBefore'](cursorNode);
                    }
                    cursorNode.parentNode.removeChild(cursorNode);
                }
                
                // validate arguments
                if(!document) { document=window.document; }
                
                // return a DOM range
                var domRange = document.createRange();
                adoptBoundary(domRange, textRange, true);
                adoptBoundary(domRange, textRange, false);
                return domRange;
            },

            convertFromDOMRange: function (domRange) {
                var adoptEndPoint = function(textRange, domRangeInner, bStart) {
                    // find anchor node and offset
                    var container = domRangeInner[bStart ? 'startContainer' : 'endContainer'];
                    var offset = domRangeInner[bStart ? 'startOffset' : 'endOffset'], textOffset = 0;
                    var anchorNode = DOMUtils.isDataNode(container) ? container : container.childNodes[offset];
                    var anchorParent = DOMUtils.isDataNode(container) ? container.parentNode : container;
                    // visible data nodes need a text offset
                    if (container.nodeType == 3 || container.nodeType == 4)
                        textOffset = offset;
                    
                    // create a cursor element node to position range (since we can't select text nodes)
                    var cursorNode = domRangeInner._document.createElement('a');
                    anchorParent.insertBefore(cursorNode, anchorNode);
                    var cursor = domRangeInner._document.body.createTextRange();
                    cursor.moveToElementText(cursorNode);
                    cursorNode.parentNode.removeChild(cursorNode);
                    // move range
                    textRange.setEndPoint(bStart ? 'StartToStart' : 'EndToStart', cursor);
                    textRange[bStart ? 'moveStart' : 'moveEnd']('character', textOffset);
                }
               
                // return an IE text range
                var textRange = domRange._document.body.createTextRange();
                adoptEndPoint(textRange, domRange, true);
                adoptEndPoint(textRange, domRange, false);
                return textRange;
            }
        };

        
        document.caretRangeFromPoint = function caretRangeFromPoint(x,y) {
            
            // the accepted number of vertical backtracking, in CSS pixels
            var IYDepth = 40;
            
            // try to create a text range at the specified location
            var r = document.body.createTextRange();
            for(var iy=IYDepth; iy; iy=iy-4) {
                var ix = x; if(true) {
                    try {
                        r.moveToPoint(ix,iy+y-IYDepth); 
                        return TextRangeUtils.convertToDOMRange(r);
                    } catch(ex) {}
                }
            }
            
            // if that fails, return the location just after the element located there
            try {
                
                var elem = document.elementFromPoint(x-1,y-1);
                var r = document.createRange();
                r.setStartAfter(elem);
                return r;
                
            } catch(ex) {
                
                return null;
                
            }
        }
    }
}


///
/// helper function for moving ranges char by char
///

Range.prototype.myMoveOneCharLeft = function() {
    var r = this;
    
    // move to the previous cursor location
    if(r.endOffset > 0) {
        
        // if we can enter into the previous sibling
        var previousSibling = r.endContainer.childNodes[r.endOffset-1];
        if(previousSibling && previousSibling.lastChild) {
            
            // enter the previous sibling from its end
            r.setEndAfter(previousSibling.lastChild);
            
        } else if(previousSibling && previousSibling.nodeType==previousSibling.TEXT_NODE) { // todo: lookup value
            
            // enter the previous text node from its end
            r.setEnd(previousSibling, previousSibling.nodeValue.length);
            
        } else {
            
            // else move before that element
            r.setEnd(r.endContainer, r.endOffset-1);
            
        }
        
    } else {
        r.setEndBefore(r.endContainer);
    }
    
}

Range.prototype.myMoveOneCharRight = function() {
    var r = this;
    
    // move to the previous cursor location
    var max = (r.startContainer.nodeType==r.startContainer.TEXT_NODE ? r.startContainer.nodeValue.length : r.startContainer.childNodes.length)
    if(r.startOffset < max) {
        
        // if we can enter into the next sibling
        var nextSibling = r.endContainer.childNodes[r.endOffset];
        if(nextSibling && nextSibling.firstChild) {
            
            // enter the next sibling from its start
            r.setStartBefore(nextSibling.firstChild);
            
        } else if(nextSibling && nextSibling.nodeType==nextSibling.TEXT_NODE && nextSibling.nodeValue!='') { // todo: lookup value
            
            // enter the next text node from its start
            r.setStart(nextSibling, 0);
            
        } else {
            
            // else move before that element
            r.setStart(r.startContainer, r.startOffset+1);
            
        }
        
    } else {
        r.setStartAfter(r.endContainer);
    }
    
    // shouldn't be needed but who knows...
    r.setEnd(r.startContainer, r.startOffset);
    
}


///
/// This functions is optimized to not yield inside a word in a text node
///
Range.prototype.myMoveTowardRight = function() {
    var r = this;
    
    // move to the previous cursor location
    var isTextNode = r.startContainer.nodeType==r.startContainer.TEXT_NODE;
    var max = (isTextNode ? r.startContainer.nodeValue.length : r.startContainer.childNodes.length)
    if(r.startOffset < max) {
        
        // if we can enter into the next sibling
        var nextSibling = r.endContainer.childNodes[r.endOffset];
        if(nextSibling && nextSibling.firstChild) {
            
            // enter the next sibling from its start
            r.setStartBefore(nextSibling.firstChild);
            
        } else if(nextSibling && nextSibling.nodeType==nextSibling.TEXT_NODE && nextSibling.nodeValue!='') { // todo: lookup value
            
            // enter the next text node from its start
            r.setStart(nextSibling, 0);
            
        } else if(isTextNode) {
            
            // move to the next non a-zA-Z symbol
            var currentText = r.startContainer.nodeValue;
            var currentOffset = r.startOffset;
            var currentLetter = currentText[currentOffset++];
            while(currentOffset < max && /^\w$/.test(currentLetter)) {
                currentLetter = currentText[currentOffset++];
            }
            r.setStart(r.startContainer, currentOffset);
            
        } else {
            
            // else move after that element
            r.setStart(r.startContainer, r.startOffset+1);
            
        }
        
    } else {
        r.setStartAfter(r.endContainer);
    }
    
    // shouldn't be needed but who knows...
    r.setEnd(r.startContainer, r.startOffset);
    
}


Range.prototype.myMoveEndOneCharLeft = function() {
    var r = this;
    
    // move to the previous cursor location
    if(r.endOffset > 0) {
        
        // if we can enter into the previous sibling
        var previousSibling = r.endContainer.childNodes[r.endOffset-1];
        if(previousSibling && previousSibling.lastChild) {
            
            // enter the previous sibling from its end
            r.setEndAfter(previousSibling.lastChild);
            
        } else if(previousSibling && previousSibling.nodeType==previousSibling.TEXT_NODE) { // todo: lookup value
            
            // enter the previous text node from its end
            r.setEnd(previousSibling, previousSibling.nodeValue.length);
            
        } else {
            
            // else move before that element
            r.setEnd(r.endContainer, r.endOffset-1);
            
        }
        
    } else {
        r.setEndBefore(r.endContainer);
    }
    
}

Range.prototype.myMoveEndOneCharRight = function() {
    var r = this;
    
    // move to the previous cursor location
    var max = (r.endContainer.nodeType==r.endContainer.TEXT_NODE ? r.endContainer.nodeValue.length : r.endContainer.childNodes.length)
    if(r.endOffset < max) {
        
        // if we can enter into the next sibling
        var nextSibling = r.endContainer.childNodes[r.endOffset];
        if(nextSibling && nextSibling.firstChild) {
            
            // enter the next sibling from its start
            r.setEndBefore(nextSibling.firstChild);
            
        } else if(nextSibling && nextSibling.nodeType==nextSibling.TEXT_NODE) { // todo: lookup value
            
            // enter the next text node from its start
            r.setEnd(nextSibling, 0);
            
        } else {
            
            // else move before that element
            r.setEnd(r.endContainer, r.endOffset+1);
            
        }
        
    } else {
        r.setEndAfter(r.endContainer);
    }
    
}

//
// Get the *real* bounding client rect of the range
// { therefore we need to fix some browser bugs... }
//
Range.prototype.myGetSelectionRect = function() {
    
    // get the browser's claimed rect
    var rect = this.getBoundingClientRect();
    
    // HACK FOR ANDROID BROWSER AND OLD WEBKIT
    if(!rect) { 
        rect={top:0,right:0,bottom:0,left:0,width:0,height:0}; 
    }
    
    // if the value seems wrong... (some browsers don't like collapsed selections)
    if(this.collapsed && rect.top===0 && rect.bottom===0) {
        
        // select one char and infer location
        var clone = this.cloneRange(); var collapseToLeft=false; clone.collapse(false); 
        
        // the case where no char before is tricky...
        if(clone.startOffset==0) {
            
            // let's move on char to the right
            clone.myMoveTowardRight();
            collapseToLeft=true;

            // note: some browsers don't like selections
            // that spans multiple containers, so we will
            // iterate this process until we have one true
            // char selected
            clone.setStart(clone.endContainer, 0); 
            
        } else {
            
            // else, just select the char before
            clone.setStart(this.startContainer, this.startOffset-1);
            collapseToLeft=false;
            
        }
        
        // get some real rect
        var rect = clone.myGetSelectionRect();
        
        // compute final value
        if(collapseToLeft) {
            return {
                
                left: rect.left,
                right: rect.left,
                width: 0,
                
                top: rect.top,
                bottom: rect.bottom,
                height: rect.height
                
            }
        } else {
            return {
                
                left: rect.right,
                right: rect.right,
                width: 0,
                
                top: rect.top,
                bottom: rect.bottom,
                height: rect.height
                
            }
        }
        
    } else {
        return rect;
    }
    
}

// not sure it's needed but still
if(!window.Element) window.Element=window.HTMLElement;
if(!window.Node) window.Node = {};

// make getBCR working on text nodes & stuff
Node.getBoundingClientRect = function getBoundingClientRect(element) {
    if (element.getBoundingClientRect) {
        
        var rect = element.getBoundingClientRect();
        
    } else {
        
        var range = document.createRange();
        range.selectNode(element);
        
        var rect = range.getBoundingClientRect();
        
    }
    
    // HACK FOR ANDROID BROWSER AND OLD WEBKIT
    if(!rect) { 
        rect={top:0,right:0,bottom:0,left:0,width:0,height:0}; 
    }
    
    return rect;
};


// FIXME: Bug with chromium recent version when we reload the page with scroll
// make getCR working on text nodes & stuff
Node.getClientRects = function getClientRects(firstChild) {
    if (firstChild.getBoundingClientRect) {
        
        return firstChild.getClientRects();
        
    } else {
        
        var range = document.createRange();
        range.selectNode(firstChild);
        
        return range.getClientRects();
        
    }
};

// fix for IE (contains fails for text nodes...)
Node.contains = function contains(parentNode,node) {
    if(node.nodeType != 1) {
        if(!node.parentNode) return false;
        return node.parentNode==parentNode || parentNode.contains(node.parentNode);
    } else {
        return parentNode.contains(node);
    }
}

//
// get the bounding rect of the selection, including the bottom padding/marging of the previous element if required
// { this is a special version for breaking algorithms that do not want to miss the previous element real size }
//
Range.prototype.myGetExtensionRect = function() {
    
    // this function returns the selection rect
    // but does take care of taking in account 
    // the bottom-{padding/border} of the previous
    // sibling element, to detect overflow points
    // more accurately
    
    var rect = this.myGetSelectionRect();
    var previousSibling = this.endContainer.childNodes[this.endOffset-1];
    if(previousSibling) {
        
        // correct with the new take
        var prevSibRect = Node.getBoundingClientRect(previousSibling);
        var adjustedBottom = Math.max(rect.bottom,prevSibRect.bottom);
        if(adjustedBottom == rect.bottom) return rect;
        return {
            
            left: rect.left,
            right: rect.right,
            width: rect.width,
            
            top: rect.top,
            bottom: adjustedBottom,
            height: adjustedBottom - rect.top
            
        };
        
    } else if(rect.bottom==0 && this.endContainer.nodeType === 3) {
        
        // note that if we are in a text node, 
        // we may want to cover all the previous
        // text in the node to avoid whitespace
        // related bugs
        
        var onlyWhiteSpaceBefore = /^(\s|\n)*$/.test(this.endContainer.nodeValue.substr(0,this.endOffset));
        if(onlyWhiteSpaceBefore) {
            
            // if we are in the fucking whitespace land, return first line
            var prevSibRect = Node.getClientRects(this.endContainer)[0];

            // FIXME: temporary fix the chromium bug
            //return prevSibRect;
            return rect;
            
        } else {
            
            // otherwhise, let's rely on previous chars
            var auxiliaryRange = this.cloneRange();
            auxiliaryRange.setStart(this.endContainer,0);
            
            // correct with the new take
            var prevSibRect = auxiliaryRange.getBoundingClientRect();
            var adjustedBottom = Math.max(rect.bottom,prevSibRect.bottom);
            return {
                
                left: rect.left,
                right: rect.right,
                width: rect.width,
                
                top: rect.top,
                bottom: adjustedBottom,
                height: adjustedBottom - rect.top
                
            };
            
        }
        
    } else {
        
        return rect;
        
    }
}
require.define('src/css-regions/lib/range-extensions.js');

////////////////////////////////////////

//
// this module holds the big-picture actions of the polyfill
//
module.exports = (function(window, document) { "use strict";
    
    var domEvents = require('src/core/dom-events.js');
    var cssSyntax = require('src/core/css-syntax.js');
    var cssCascade = require('src/core/css-cascade.js');
    var cssBreak = require('src/core/css-break.js');

    var cssRegionsHelpers = window.cssRegionsHelpers = {
        
        //
        // returns the previous sibling of the element
        // or the previous sibling of its nearest ancestor that has one
        //
        getAllLevelPreviousSibling: function(e, region) {
            if(!e || e==region) return null;
            
            // find the nearest ancestor that has a previous sibling
            while(!e.previousSibling) {
                
                // but bubble to the next avail ancestor
                e = e.parentNode;
                
                // dont get over the bar
                if(!e || e==region) return null;
                
            }
            
            // return that sibling
            return e.previousSibling;
        },
        
        //
        // prepares the element to become a css region
        //
        markNodesAsRegion: function(nodes,fast) {
            nodes.forEach(function(node) {
                node.regionOverset = 'empty';
                node.setAttribute('data-css-region',node.cssRegionsLastFlowFromName);
                cssRegionsHelpers.hideTextNodesFromFragmentSource([node]);
                node.cssRegionsWrapper = node.cssRegionsWrapper || node.appendChild(document.createElement("cssregion"));
            });
        },
        
        //
        // prepares the element to return to its normal css life
        //
        unmarkNodesAsRegion: function(nodes,fast) {
            nodes.forEach(function(node) {
                
                // restore regionOverset to its natural value
                node.regionOverset = 'fit';
                
                // remove the current <cssregion> tag
                try { node.cssRegionsWrapper && node.removeChild(node.cssRegionsWrapper); } 
                catch(ex) { setImmediate(function() { throw ex })}; 
                node.cssRegionsWrapper = undefined;
                delete node.cssRegionsWrapper;
                
                // restore top-level texts that may have been hidden
                cssRegionsHelpers.unhideTextNodesFromFragmentSource([node]);
                
                // unmark as a region
                node.removeAttribute('data-css-region');
            });
        },
        
        //
        // prepares the element for cloning (mainly give them an ID)
        //
        fragmentSourceIndex: 0,
        markNodesAsFragmentSource: function(nodes,ignoreRoot) {
            
            function visit(node,k) {
                var child, next;
                switch (node.nodeType) {
                    case 1: // Element node
                        
                        if(typeof(k)=="undefined" || !ignoreRoot) {
                            
                            // mark as fragment source
                            var id = node.getAttributeNode('data-css-regions-fragment-source');
                            if(!id) { node.setAttribute('data-css-regions-fragment-source', cssRegionsHelpers.fragmentSourceIndex++); }
                            
                        }
                        
                        node.setAttribute('data-css-regions-cloning', true);
                        
                        // expand list values
                        if(node.tagName=='OL') cssRegionsHelpers.expandListValues(node);
                        if(typeof(k)!="undefined" && node.tagName=="LI") cssRegionsHelpers.expandListValues(node.parentNode);
                        
                    case 9: // Document node
                    case 11: // Document fragment node
                        child = node.firstChild;
                        while (child) {
                            next = child.nextSibling;
                            visit(child);
                            child = next;
                        }
                        break;
                }
            }
            
            nodes.forEach(visit);
            
        },
        
        //
        // computes the "value" attribute of every LI element out there
        //
        expandListValues: function(OL) {
            if(OL.getAttribute("data-css-li-value-expanded")) return;
            OL.setAttribute('data-css-li-value-expanded', true);
            
            if(OL.hasAttribute("reversed")) {
                
                var currentValue = OL.getAttribute("start") ? parseInt(OL.getAttribute("start")) : OL.childElementCount;
                var increment = -1;
                
            } else {
                
                var currentValue = OL.getAttribute("start") ? parseInt(OL.getAttribute("start")) : 1;
                var increment = +1;
                
            }
            
            var LI = OL.firstElementChild; var LIV = null;
            while(LI) {
                if(LI.tagName==="LI") {
                    if(LIV=LI.getAttributeNode("value")) {
                        currentValue = parseInt(LIV.nodeValue);
                        LI.setAttribute('data-css-old-value', currentValue)
                    } else {
                        LI.setAttribute("value", currentValue);
                    }
                    currentValue = currentValue + increment;
                }
                LI = LI.nextElementSibling;
            }
            
            
        },
        
        //
        // reverts to automatic computation of the value of LI elements
        //
        unexpandListValues: function(OL) {
            if(!OL.hasAttribute('data-css-li-value-expanded')) return;
            OL.removeAttribute('data-css-li-value-expanded')
            var LI = OL.firstElementChild; var LIV = null;
            while(LI) {
                if(LI.tagName==="LI") {
                    if(LIV=LI.getAttributeNode("data-css-old-value")) {
                        LI.removeAttributeNode(LIV);
                    } else {
                        LI.removeAttribute('value');
                    }
                }
                LI = LI.nextElementSibling;
            }
        },
        
        //
        // makes empty text nodes which cannot get "display: none" applied to them
        //
        listOfTextNodesForIE: [],
        hideTextNodesFromFragmentSource: function(nodes) {
            
            function visit(node,k) {
                var child, next;
                switch (node.nodeType) {
                    case 3: // Text node
                        
                        if(!node.parentNode.getAttribute('data-css-regions-fragment-source')) {
                            // we have to remove their content the hard way...
                            node.cssRegionsSavedNodeValue = node.nodeValue;
                            node.nodeValue = "";
                            
                            // HACK: OTHERWISE IE WILL GC THE TEXTNODE AND RETURNS YOU
                            // A FRESH TEXTNODE THE NEXT TIME WHERE YOUR EXPANDO
                            // IS NOWHERE TO BE SEEN!
                            if(navigator.userAgent.indexOf('MSIE')>0 || navigator.userAgent.indexOf("Trident")>0) {
                                if(cssRegionsHelpers.listOfTextNodesForIE.indexOf(node)==-1) {
                                    cssRegionsHelpers.listOfTextNodesForIE.push(node);
                                }
                            }
                        }
                        
                        break;
                        
                    case 1: // Element node
                        if(node.hasAttribute('data-css-regions-cloning')) {
                            node.removeAttribute('data-css-regions-cloning');
                            node.setAttribute('data-css-regions-cloned', true);
                            if(node.currentStyle) node.currentStyle.display.toString(); // IEFIX FOR BAD STYLE RECALC
                        }
                        if(typeof(k)=="undefined") return;
                        
                    case 9: // Document node
                    case 11: // Document fragment node                    
                        child = node.firstChild;
                        while (child) {
                            next = child.nextSibling;
                            visit(child);
                            child = next;
                        }
                        break;
                }
            }
            
            nodes.forEach(visit);
            
        },
        
        //
        // makes emptied text nodes visible again
        //
        unhideTextNodesFromFragmentSource: function(nodes) {
            
            function visit(node) {
                var child, next;
                switch (node.nodeType) {
                    case 3: // Text node
                        
                        // we have to remove their content the hard way...
                        if("cssRegionsSavedNodeValue" in node) {
                            node.nodeValue = node.cssRegionsSavedNodeValue;
                            delete node.cssRegionsSavedNodeValue;
                        }
                        
                        break;
                        
                    case 1: // Element node
                        if(typeof(k)=="undefined") return;
                        
                    case 9: // Document node
                    case 11: // Document fragment node                    
                        child = node.firstChild;
                        while (child) {
                            next = child.nextSibling;
                            visit(child);
                            child = next;
                        }
                        break;
                }
            }
            
            nodes.forEach(visit);
            
        },
        
        //
        // prepares the content elements to return to ther normal css life
        //
        unmarkNodesAsFragmentSource: function(nodes) {
            
            function visit(node,k) {
                var child, next;
                switch (node.nodeType) {
                    case 3: // Text node
                        
                        // we have to reinstall their content the hard way...
                        if("cssRegionsSavedNodeValue" in node) {
                            node.nodeValue = node.cssRegionsSavedNodeValue;
                            delete node.cssRegionsSavedNodeValue;
                        }
                        
                        break;
                    case 1: // Element node
                        node.removeAttribute('data-css-regions-cloned');
                        node.removeAttribute('data-css-regions-fragment-source');
                        if(node.currentStyle) node.currentStyle.display.toString(); // IEFIX FOR BAD STYLE RECALC
                        if(node.tagName=="OL") cssRegionsHelpers.unexpandListValues(node);
                        if(typeof(k)!="undefined" && node.tagName=="LI") cssRegionsHelpers.unexpandListValues(node.parentNode);
                        
                    case 9: // Document node
                    case 11: // Document fragment node
                        child = node.firstChild;
                        while (child) {
                            next = child.nextSibling;
                            visit(child);
                            child = next;
                        }
                        break;
                }
            }
            
            nodes.forEach(visit);
            
        },
        
        //
        // marks cloned content as fragment instead of as fragment source (basically)
        //
        transformFragmentSourceToFragments: function(nodes) {
            
            function visit(node) {
                var child, next;
                switch (node.nodeType) {
                    case 1: // Element node
                        var id = node.getAttribute('data-css-regions-fragment-source');
                        node.removeAttribute('data-css-regions-fragment-source');
                        node.removeAttribute('data-css-regions-cloning');
                        node.removeAttribute('data-css-regions-cloned');
                        node.setAttribute('data-css-regions-fragment-of', id);
                        if(node.id) node.id += "--fragment";
                        
                    case 9: // Document node
                    case 11: // Document fragment node
                        child = node.firstChild;
                        while (child) {
                            next = child.nextSibling;
                            visit(child);
                            child = next;
                        }
                        break;
                }
            }
            
            nodes.forEach(visit);
            
        },
        
        //
        // removes some invisible text nodes from the tree
        // (useful if you don't want to face browser bugs when dealing with them)
        //
        embedTrailingWhiteSpaceNodes: function(fragment) {
            
            var onlyWhiteSpace = /^\s*$/;
            function visit(node) {
                var child, next;
                switch (node.nodeType) {
                    case 3: // Text node
                        
                        // we only remove nodes at the edges
                        if (!node.previousSibling) {
                            
                            // we only remove nodes if their parent doesn't preserve whitespace
                            if (getComputedStyle(node.parentNode).whiteSpace.substring(0,3)!=="pre") {
                                
                                // only remove pure whitespace nodes
                                if (onlyWhiteSpace.test(node.nodeValue)) {
                                    node.parentNode.setAttribute('data-whitespace-before',node.nodeValue);
                                    node.parentNode.removeChild(node);
                                }
                                
                            }
                            
                            break;
                        }
                        
                        // we only remove nodes at the edges
                        if (!node.nextSibling) {
                            
                            // we only remove nodes if their parent doesn't preserve whitespace
                            if (getComputedStyle(node.parentNode).whiteSpace.substring(0,3)!=="pre") {
                                
                                // only remove pure whitespace nodes
                                if (onlyWhiteSpace.test(node.nodeValue)) {
                                    node.parentNode.setAttribute('data-whitespace-after',node.nodeValue);
                                    node.parentNode.removeChild(node);
                                }
                                
                            }
                            
                            break;
                        }
                        
                        break;
                    case 1: // Element node
                    case 9: // Document node
                    case 11: // Document fragment node
                        child = node.firstChild;
                        while (child) {
                            next = child.nextSibling;
                            visit(child);
                            child = next;
                        }
                        break;
                }
            }
            
            visit(fragment);
            
        },
        
        //
        // recover the previously removed invisible text nodes
        //
        unembedTrailingWhiteSpaceNodes: function(fragment) {
            
            var onlyWhiteSpace = /^\s*$/;
            function visit(node) {
                var child, next;
                switch (node.nodeType) {
                    case 1: // Element node
                        var txt = "";
                        if(txt = node.getAttribute('data-whitespace-before')) {
                            if(node.getAttribute('data-starting-fragment')=='' && node.getAttribute('data-special-starting-fragment','')) {
                                node.insertBefore(document.createTextNode(txt),node.firstChild);
                            }
                        }
                        node.removeAttribute('data-whitespace-before')
                        if(txt = node.getAttribute('data-whitespace-after')) {
                            if(node.getAttribute('data-continued-fragment')=='' && node.getAttribute('data-special-continued-fragment','')) {
                                node.insertAfter(document.createTextNode(txt),node.lastChild);
                            }
                        }
                        node.removeAttribute('data-whitespace-after')
                        
                    case 9: // Document node
                    case 11: // Document fragment node
                        child = node.firstChild;
                        while (child) {
                            next = child.nextSibling;
                            visit(child);
                            child = next;
                        }
                        break;
                }
            }
            
            visit(fragment);
            
        },
        
        ///
        /// walk the two trees the same way, and copy all the styles
        /// BEWARE: if the DOMs are different, funny things will happen
        /// NOTE: this function will also remove elements put in another flow
        ///
        copyStyle: function(root1, root2) {
            
            function visit(node1, node2, isRoot) {
                var child1, next1, child2, next2;
                switch (node1.nodeType) {
                    case 1: // Element node
                        
                        // firstly, setup a cache of all css properties on the element
                        var matchedRules = (node1.currentStyle && !window.opera) ? undefined : cssCascade.findAllMatchingRules(node1)
                        
                        // and compute the value of all css properties
                        var properties = cssCascade.allCSSProperties || cssCascade.getAllCSSProperties();
                        for(var p=properties.length; p--; ) {
                            
                            // if the property is computation-safe, use the computed value
                            if(!(properties[p] in cssCascade.computationUnsafeProperties) && properties[p][0]!='-') {
                                var style = getComputedStyle(node1).getPropertyValue(properties[p]);
                                var defaultStyle = cssCascade.getDefaultStyleForTag(node1.tagName).getPropertyValue(properties[p]);
                                if(style != defaultStyle) node2.style.setProperty(properties[p], style)
                                continue;
                            }
                            
                            // otherwise, get the element's specified value
                            var cssValue = cssCascade.getSpecifiedStyle(node1, properties[p], matchedRules);
                            if(cssValue && cssValue.length) {
                                
                                // if we have a specified value, let's use it
                                node2.style.setProperty(properties[p], cssValue.toCSSString());
                                
                            } else if(isRoot && node1.parentNode && properties[p][0] != '-') {
                                
                                // NOTE: the root will be detached from its parent
                                // Therefore, we have to inherit styles from it (oh no!)
                                
                                // TODO: create a list of inherited properties
                                if(!(properties[p] in cssCascade.inheritingProperties)) continue;
                                
                                // if the property is computation-safe, use the computed value
                                if((properties[p]=="font-size") || (!(properties[p] in cssCascade.computationUnsafeProperties) && properties[p][0]!='-')) {
                                    var style = getComputedStyle(node1).getPropertyValue(properties[p]);
                                    node2.style.setProperty(properties[p], style);
                                    //var parentStyle = style; try { parentStyle = getComputedStyle(node1.parentNode).getPropertyValue(properties[p]) } catch(ex){}
                                    //var defaultStyle = cssCascade.getDefaultStyleForTag(node1.tagName).getPropertyValue(properties[p]);
                                    
                                    //if(style === parentStyle) {
                                    //  node2.style.setProperty(properties[p], style)
                                    //}
                                    continue;
                                }
                                
                                // otherwise, get the parent's specified value
                                var cssValue = cssCascade.getSpecifiedStyle(node1, properties[p], matchedRules);
                                if(cssValue && cssValue.length) {
                                    
                                    // if we have a specified value, let's use it
                                    node2.style.setProperty(properties[p], cssValue.toCSSString());
                                    
                                }
                                
                            }
                            
                        }
                        
                        // now, let's work on ::after and ::before
                        var importPseudo = function(node1,node2,pseudo) {
                            
                            //
                            // we'll need to use getSpecifiedStyle here as the pseudo thing is slow
                            //
                            var mayExist = !!cssCascade.findAllMatchingRulesWithPseudo(node1,pseudo.substr(1)).length;
                            if(!mayExist) return;
                            
                            var pseudoStyle = getComputedStyle(node1,pseudo);
                            if(pseudoStyle.content!='none'){
                                
                                // let's create a stylesheet for the element
                                var stylesheet = document.createElement('style');
                                stylesheet.setAttribute('data-no-css-polyfill',true);
                                
                                // compute the value of all css properties
                                var node2style = "";
                                var properties = cssCascade.allCSSProperties || cssCascade.getAllCSSProperties();
                                for(var p=properties.length; p--; ) {
                                    
                                    // we always use the computed value, because we don't have better
                                    var style = pseudoStyle.getPropertyValue(properties[p]);
                                    node2style += properties[p]+":"+style+";";
                                    
                                }
                                
                                stylesheet.textContent = (
                                    '[data-css-regions-fragment-of="' + node1.getAttribute('data-css-regions-fragment-source') + '"]' 
                                    +':not([data-css-regions-starting-fragment]):not([data-css-regions-special-starting-fragment])'
                                    +':'+pseudo+'{'
                                    +node2style
                                    +"}"
                                );
                                
                                node2.parentNode.insertBefore(stylesheet, node2);
                                
                            }
                        }
                        importPseudo(node1,node2,":before");
                        importPseudo(node1,node2,":after");
                        
                        // retarget events
                        cssRegionsHelpers.retargetEvents(node1,node2);
                        
                        
                    case 9: // Document node
                    case 11: // Document fragment node
                        child1 = node1.firstChild;
                        child2 = node2.firstChild;
                        while (child1) {
                            next1 = child1.nextSibling;
                            next2 = child2.nextSibling;
                            
                            // decide between process style or hide
                            if(child1.cssRegionsLastFlowIntoName && child1.cssRegionsLastFlowIntoType==="element") {
                                node2.removeChild(child2);
                            } else {
                                visit(child1, child2);
                            }
                            
                            child1 = next1;
                            child2 = next2;
                        }
                        break;
                }
            }
            
            visit(root1, root2, true);
            
        },
        
        //
        // make sure the most critical events still fire in the fragment source
        // even if the browser initially fire them on the fragments
        //
        retargetEvents: function retargetEvents(node1,node2) {
            
            var retargetEvent = "cssRegionsHelpers.retargetEvent(this,event)";
            //node2.setAttribute("onclick", retargetEvent);
            //node2.setAttribute("ondblclick", retargetEvent);
            //node2.setAttribute("onmousedown", retargetEvent);
            //node2.setAttribute("onmouseup", retargetEvent);
            //node2.setAttribute("onmousein", retargetEvent);
            //node2.setAttribute("onmouseout", retargetEvent);
            //node2.setAttribute("onmouseenter", retargetEvent);
            //node2.setAttribute("onmouseleave", retargetEvent);
            
        },
        
        //
        // single hub for event retargeting operations.
        //
        retargetEvent: function retargeEvent(node2,e) {
            
            // get the node we should fire the event on
            var node1 = (
                (node2.cssRegionsFragmentSource) ||
                (node2.cssRegionsFragmentSource=document.querySelector('[data-css-regions-fragment-source="' + node2.getAttribute('data-css-regions-fragment-of') + '"]'))
            );
            
            if(node1) {
            
                // dispatch the event on the real node
                var ne = domEvents.cloneEvent(e);
                node1.dispatchEvent(ne);
                
                // prevent the event to fire on the region
                e.stopImmediatePropagation ? e.stopImmediatePropagation() : e.stopPropagation();
                
                // make sure to cancel the event if required
                if(ne.isDefaultPrevented || ne.defaultPrevented) { e.preventDefault(); return false; }
            
            }
            
        }
    };
    
    return cssRegionsHelpers;
    
})(window, document);
require.define('src/css-regions/lib/helpers.js');

////////////////////////////////////////

//
// this module holds the front-facing features of the polyfill
//
module.exports = (function(window, document, cssRegions) { "use strict";

    var domEvents = require('src/core/dom-events.js');
    var cssSyntax = require('src/core/css-syntax.js');
    var cssCascade = require('src/core/css-cascade.js');
    var cssBreak = require('src/core/css-break.js');
    var cssRegionsHelpers = require('src/css-regions/lib/helpers.js');
    var ES = require('src/core/dom-experimental-event-streams.js');

    // 
    // this class contains flow-relative data field
    // 
    cssRegions.Flow = function NamedFlow(name) {
        
        // TODO: force immediate relayout if someone ask the overset properties
        // and the layout has been deemed wrong (still isn't a proof of correctness but okay)
        
        // define the flow name
        this.name = name; Object.defineProperty(this, "name", {get: function() { return name; }});
        
        // define the overset status
        this.overset = false;
        
        // define the first empty region
        this.firstEmptyRegionIndex = -1;
        
        // elements poured into the flow
        this.content = []; this.lastContent = [];
        
        // elements that consume this flow
        this.regions = []; this.lastRegions = [];
        
        // event handlers
        this.eventListeners = {
            "regionfragmentchange": [],
            "regionoversetchange": [],
        };
        
        // this function is used to work with dom event streams
        var This=this; This.update = function(stream) {
            stream.schedule(This.update); This.relayout();
        };
        
        // register to style changes already
        This.lastStylesheetAdded = 0;
        cssCascade.addEventListener('stylesheetadded', function() {
            if(This.lastStylesheetAdded - Date() > 100) {
                This.lastStylesheetAdded = +Date();
                This.relayout();
            } else {
                cssConsole.warn("Please don't add stylesheets as a response to region events. Operation cancelled.")
            }
        });
        
        // a small counter to avoid enter retry loops
        This.failedLayoutCount = 0;
        
        // some other fields
        This.lastEventRAF = 0;
        This.restartLayout = false;
    }
        
    cssRegions.Flow.prototype.removeFromContent = function(element) {
        
        // clean up stuff
        this.removeEventListenersOf(element);
        
        // remove reference
        var index = this.content.indexOf(element);
        if(index>=0) { this.content.splice(index,1); }
        
    };

    cssRegions.Flow.prototype.removeFromRegions = function(element) {
        
        // clean up stuff
        this.removeEventListenersOf(element);
        
        // remove reference
        var index = this.regions.indexOf(element);
        if(index>=0) { this.regions.splice(index,1); }
        
    };

    cssRegions.Flow.prototype.addToContent = function(element) {
        
        // walk the tree to find an element inside the content chain
        var content = this.content;
        var treeWalker = document.createTreeWalker(
            document.documentElement,
            NodeFilter.SHOW_ELEMENT,
            function(node) { 
                return content.indexOf(node) >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 
            },
            false
        ); 
        
        // which by the way has to be after the considered element
        treeWalker.currentNode = element;
        
        // if we find such node
        if(treeWalker.nextNode()) {
            
            // insert the element at his current location
            content.splice(content.indexOf(treeWalker.currentNode),0,element);
            
        } else {
            
            // add the new element to the end of the array
            content.push(element);
            
        }

    };

    cssRegions.Flow.prototype.addToRegions = function(element) {
        
        // walk the tree to find an element inside the region chain
        var regions = this.regions;
        var treeWalker = document.createTreeWalker(
            document.documentElement,
            NodeFilter.SHOW_ELEMENT,
            function(node) { 
                return regions.indexOf(node) >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 
            },
            false
        );
        
        // which by the way has to be after the considered element
        treeWalker.currentNode = element;
        
        // if we find such node
        if(treeWalker.nextNode()) {
            
            // insert the element at his current location
            regions.splice(this.regions.indexOf(treeWalker.currentNode),0,element);
            
        } else {
            
            // add the new element to the end of the array
            regions.push(element);
        }
        
    };

    cssRegions.Flow.prototype.generateContentFragment = function() {
        var fragment = document.createDocumentFragment(); var This=this;

        // add copies of all due content
        for(var i=0; i<this.content.length; i++) {
            var element = this.content[i];
            
            // 
            // STEP 1: IDENTIFY FRAGMENT SOURCES AS SUCH
            //
            cssRegionsHelpers.markNodesAsFragmentSource([element], element.cssRegionsLastFlowIntoType=="content");
            
            
            //
            // STEP 2: CLONE THE FRAGMENT SOURCES
            // 
            
            // depending on the requested behavior
            if(element.cssRegionsLastFlowIntoType=="element") {
                
                    // add the element
                    var el = element;
                    var elClone = el.cloneNode(true);
                    var elToInsert = elClone; if(elToInsert.tagName=="LI") {
                        elToInsert = document.createElement(el.parentNode.tagName);
                        elToInsert.style.margin="0";
                        elToInsert.style.padding="0";
                        elToInsert.appendChild(elClone);
                    }
                    fragment.appendChild(elToInsert);
                    
                    // clone the style
                    cssRegionsHelpers.copyStyle(el, elClone);
                
            } else {
                
                // add current children
                var el = element.firstChild; while(el) {
                    
                    // add the element
                    var elClone = el.cloneNode(true);
                    var elToInsert = elClone; if(elToInsert.tagName=="LI") {
                        elToInsert = document.createElement(el.parentNode.tagName);
                        elToInsert.style.margin="0";
                        elToInsert.style.padding="0";
                        elToInsert.appendChild(elClone);
                    }
                    fragment.appendChild(elToInsert);
                    
                    // clone the style
                    cssRegionsHelpers.copyStyle(el, elClone);
                    
                    el = el.nextSibling;
                }
                
            }
            
        }
        
        //
        // STEP 3: HIDE TEXT NODES IN FRAGMENT SOURCES
        //
        cssRegionsHelpers.hideTextNodesFromFragmentSource(this.content);
        
        //
        // STEP 4: CONVERT CLONED FRAGMENT SOURCES INTO TRUE FRAGMENTS
        //
        cssRegionsHelpers.transformFragmentSourceToFragments(
            Array.prototype.slice.call(fragment.childNodes,0)
        )
        
        
        //
        // CLONED CONTENT IS READY!
        //
        return fragment;
    }

    cssRegions.Flow.prototype.relayout = function() {
        var This = this;
        
        // prevent previous relayouts from eventing
        cancelAnimationFrame(This.lastEventRAF);
        
        // batch relayout queries
        if(This.relayoutScheduled) { return; }
        if(This.relayoutInProgress) { This.restartLayout=true; return; }
        This.relayoutScheduled = true;
        requestAnimationFrame(function() { This._relayout() });
        
    }

    cssRegions.Flow.prototype._relayout = function(data){
        var This=this;
        
        try {
            
            //
            // note: it is recommended to look at the beautiful 
            // drawings I made before attempting to understand
            // this stuff. If you don't have them, ask me.
            //
            cssConsole.log("starting a new relayout for "+This.name);
            This.relayoutInProgress=true; This.relayoutScheduled=false;
            This.lastRelayout = +new Date();
            //debugger;
            
            // NOTE: we recover the scroll position in case the browser mess it up
            var docElmScrollTop = data && data.docElmScrollTop ? data.docElmScrollTop : document.documentElement.scrollTop;
            var docBdyScrollTop = data && data.docBdyScrollTop ? data.docBdyScrollTop : document.body.scrollTop;
            
            
            //
            // STEP 1: REMOVE PREVIOUS EVENT LISTENERS
            //
            
            // remove the listeners from everything
            This.removeEventListenersOf(This.lastRegions);
            This.removeEventListenersOf(This.lastContent);
            cancelAnimationFrame(This.lastEventRAF);
            
            
            //
            // STEP 2: RESTORE CONTENT/REGIONS TO A CLEAN STATE
            //
            
            // detect elements being removed of the document
            This.regions = This.regions.filter(function(e) { return document.documentElement.contains(e); })
            This.content = This.content.filter(function(e) { return document.documentElement.contains(e); })
            
            // cleanup previous layout
            cssRegionsHelpers.unmarkNodesAsRegion(This.lastRegions); This.lastRegions = This.regions.slice(0);
            cssRegionsHelpers.unmarkNodesAsFragmentSource(This.lastContent); This.lastContent = This.content.slice(0);
            
            
            
            //
            // STEP 3: EMPTY ALL REGIONS
            // ADD WRAPPER FOR FLOW CONTENT
            // PREPARE FOR CONTENT CLONING
            //
            
            // empty all the regions
            cssRegionsHelpers.markNodesAsRegion(This.regions);
            
            // create a fresh list of the regions
            var regionStack = This.regions.slice(0).reverse();
            
            
            
            //
            // STEP 4: CLONE THE CONTENT
            // ADD METADATA TO CLONED CONTENT
            // HIDE FLOW CONTENT AT INITIAL POSITION
            //
            
            // create a fresh list of the content
            // compute the style of all source elements
            // generate stylesheets for those rules
            var contentFragment = This.generateContentFragment();
            
            
            
            //
            // STEP 5: POUR CONTENT INTO THE REGIONS
            //
            
            // layout this stuff
            cssRegions.layoutContent(regionStack, contentFragment, {
                onprogress: function(continueLayout) {
                    
                    // NOTE: we recover the scroll position in case the browser mess it up
                    document.documentElement.scrollTop = docElmScrollTop;
                    document.body.scrollTop = docBdyScrollTop;
                    
                    // NOTE: if the current layout goes nowhere, start a new one already
                    if(This.restartLayout) {
                        
                        This.relayoutInProgress = false;
                        This.failedLayoutCount = 0;
                        This.restartLayout = false;
                        This._relayout({
                            docElmScrollTop: docElmScrollTop,
                            docBdyScrollTop: docBdyScrollTop
                        });
                        
                    } else {
                        
                        setImmediate(continueLayout);
                        
                    }
                    
                },
                ondone: function onLayoutDone(overset) {
                
                    This.overset = overset;
                    This.firstEmptyRegionIndex = This.regions.length-1; while(This.regions[This.firstEmptyRegionIndex]) {
                    
                        // tell whether the region is empty
                        var isEmpty = false;
                        isEmpty = isEmpty || !This.regions[This.firstEmptyRegionIndex].cssRegionsWrapper;
                        isEmpty = isEmpty || !This.regions[This.firstEmptyRegionIndex].cssRegionsWrapper.firstChild;
                        
                        // if the region is not empty
                        if(!isEmpty) {
                            
                            // the first empty region if the next one, if it exists
                            if((++This.firstEmptyRegionIndex)==This.regions.length) {
                                This.firstEmptyRegionIndex = -1;
                            }
                            break;
                            
                        } else {
                             
                            // else, let's try the previous region
                            This.firstEmptyRegionIndex--; 
                            
                        }
                    }
                    
                    
                    
                    //
                    // STEP 6: REGISTER TO UPDATE EVENTS
                    //
                    
                    // make sure regions update are taken in consideration
                    //if(window.MutationObserver) {
                        //This.addEventListenersTo(This.content);
                        //This.addEventListenersTo(This.regions);
                    //} else {
                        //// the other browsers don't get this as acurately
                        //// but that shouldn't be that of an issue for 99% of the cases
                        //setImmediate(function() {
                            //This.addEventListenersTo(This.content);
                        //});
                    //}
                    
                    
                    
                    //
                    // STEP 7: FIRE SOME EVENTS
                    //
                    if(This.regions.length > 0 && !This.restartLayout) {
                        
                        // before doing anything, let's check our stuff is consistent
                        var isBuggy = false;
                        isBuggy = isBuggy || This.regions.some(function(e) { return !document.documentElement.contains(e); })
                        isBuggy = isBuggy || This.content.some(function(e) { return !document.documentElement.contains(e); })
                        
                        if(isBuggy) {
                            
                            // if we found any bug, we will need to restart a layout
                            cssConsole.warn("Buggy css regions layout: the page changed; we need to restart.");
                            This.restartLayout = true; 
                            
                        } else {
                            
                            // if it was okay, let's fire some event
                            This.lastEventRAF = requestAnimationFrame(function() {
                            
                                // TODO: only fire when necessary but...
                                This.dispatchEvent('regionfragmentchange');
                                This.dispatchEvent('regionoversetchange');
                                
                            });
                            
                        }
                    }
                    
                    
                    // NOTE: we recover the scroll position in case the browser mess it up
                    document.documentElement.scrollTop = docElmScrollTop;
                    document.body.scrollTop = docBdyScrollTop;
                    
                    // mark layout has being done
                    This.relayoutInProgress = false;
                    This.failedLayoutCount = 0;
                    
                    // restart a layout if a request was queued during the current one
                    if(This.restartLayout) {
                        This.restartLayout = false;
                        This.relayout();
                    }
                    
                }
            });
            
        } catch(ex) {
            
            // sometimes IE fails for no valid reason 
            // (other than the page is still loading)
            setImmediate(function() { throw ex; });
            
            // but we cannot accept to fail, so we need to try again
            // until we finish a complete layout pass...
            This.failedLayoutCount++;
            if(This.failedLayoutCount<7) {requestAnimationFrame(function() { This._relayout() });}
            else {This.failedLayoutCount=0; This.relayoutScheduled=false; This.relayoutInProgress=false; This.restartLayout=false; }
            
        }
    }

    cssRegions.Flow.prototype.relayoutIfSizeChanged = function() {
        
        // go through all regions
        // and see if any did change of size
        var rs = this.regions;     
        for(var i=rs.length; i--; ) {
            if(
                rs[i].offsetHeight !== rs[i].cssRegionsLastOffsetHeight
                || rs[i].offsetWidth !== rs[i].cssRegionsLastOffsetWidth
            ) {
                this.relayout(); return;
            }
        }
        
    }

    cssRegions.Flow.prototype.addEventListenersTo = function(nodes) {
        var This=this; if(nodes instanceof Element) { nodes=[nodes] }
        
        nodes.forEach(function(element) {
            if(!element.cssRegionsEventStream) {
                element.cssRegionsEventStream = new ES.DOMUpdateEventStream({target: element});
                element.cssRegionsEventStream.schedule(This.update);
            }
        });
        
    }

    cssRegions.Flow.prototype.removeEventListenersOf = function(nodes) {
        var This=this; if(nodes instanceof Element) { nodes=[nodes] }
        
        nodes.forEach(function(element) {
            if(element.cssRegionsEventStream) {
                element.cssRegionsEventStream.dispose();
                delete element.cssRegionsEventStream;
            }
        });
        
    }

    // alias
    cssRegions.NamedFlow = cssRegions.Flow;

    // return a disconnected array of the content of a NamedFlow
    cssRegions.NamedFlow.prototype.getContent = function getContent() {
        return this.content.slice(0)
    }

    // return a disconnected array of the regions of a NamedFlow
    cssRegions.NamedFlow.prototype.getRegions = function getRegions() {
        return this.regions.slice(0)
    }

    cssRegions.NamedFlow.prototype.getRegionsByContent = function getRegionsByContent(node) {
        var regions = [];
        var fragments = document.querySelectorAll('[data-css-regions-fragment-of="'+node.getAttribute('data-css-regions-fragment-source')+'"]');
        for (var i=0; i<fragments.length; i++) {
            
            var current=fragments[i]; do {
                
                if(current.getAttribute('data-css-region')) {
                    regions.push(current); break;
                }
                
            } while(current=current.parentNode);
            
        }
        
        return regions;
    }

    domEvents.EventTarget.implementsIn(cssRegions.Flow);

    //
    // this class is a collection of named flows (not an array, sadly)
    //
    cssRegions.NamedFlowCollection = function NamedFlowCollection() {
        
        this.length = 0;
        
    }

    cssRegions.NamedFlowCollection.prototype.namedItem = function(k) {
        return cssRegions.flows[k] || (cssRegions.flows[k]=new cssRegions.Flow(k));
    }


    //
    // this helper creates the required methods on top of the DOM {ie: public exports}
    //
    cssRegions.enablePolyfillObjectModel = function() {
        
        //
        // DOCUMENT INTERFACE
        //
        
        //
        // returns a static list of active named flows
        //
        document.getNamedFlows = function() {
                
            var c = new cssRegions.NamedFlowCollection(); var flows = cssRegions.flows;
            for(var flowName in cssRegions.flows) {
                
                if(Object.prototype.hasOwnProperty.call(flows, flowName)) {
                    
                    // only active flows can be included
                    if(flows[flowName].content.length!=0 || flows[flowName].regions.length!=0) {
                        c[c.length++] = c[flowName] = flows[flowName];
                    }
                    
                }
                
            }
            return c;
            
        }
        
        //
        // returns a live object for any named flow
        //
        document.getNamedFlow = function(flowName) {
                
            var flows = cssRegions.flows;
            return (flows[flowName] || (flows[flowName]=new cssRegions.NamedFlow(flowName)));
            
        }
        
        //
        // ELEMENT INTERFACE
        //    
        Object.defineProperties(
            Element.prototype,
            {
                "regionOverset": {
                    get: function() {
                        return this._regionOverset || 'fit';
                    },
                    set: function(value) {
                        this._regionOverset = value;
                    }
                },
                "getRegionFlowRanges": {
                    value: function getRegionFlowRanges() {
                        return null; // TODO: can we implement that? I think we can't (properly).
                    }
                },
                "getComputedRegionStyle": {
                    value: function getComputedRegionStyle(element,pseudo) {
                        // TODO: only works while we don't relayout
                        // TODO: only works properly for elements actually in the region
                        var fragment = document.querySelector('[data-css-regions-fragment-of="'+element.getAttribute('data-css-regions-fragment-source')+'"]');
                        if(pseudo) {
                            return getComputedStyle(fragment||element, pseudo);
                        } else {
                            return getComputedStyle(fragment||element);
                        }
                    }
                }
            }
        )
        
        
        //
        // CSSStyleDeclaration interface
        //
        cssCascade.polyfillStyleInterface('flow-into');
        cssCascade.polyfillStyleInterface('flow-from');
        cssCascade.polyfillStyleInterface('region-fragment');
        cssCascade.polyfillStyleInterface('break-before');
        cssCascade.polyfillStyleInterface('break-after');

    }

    // load the polyfill immediately if not especially told otherwise
    if(!("cssRegionsManualTrigger" in window)) { cssRegions.enablePolyfill(); }
    
});
require.define('src/css-regions/lib/objectmodel.js');

////////////////////////////////////////

//
// this module holds the big-picture actions of the polyfill
//
module.exports = (function(window, document) { "use strict";

    var domEvents = require('src/core/dom-events.js');
    var cssSyntax = require('src/core/css-syntax.js');
    var cssCascade = require('src/core/css-cascade.js');
    var cssBreak = require('src/core/css-break.js');
    
    require('src/css-regions/lib/range-extensions.js');
    var cssRegionsHelpers = require('src/css-regions/lib/helpers.js');
    var enableObjectModel = require('src/css-regions/lib/objectmodel.js');
    
    var CSS_STYLE = "cssregion,[data-css-region]>*,[data-css-regions-fragment-source]:not([data-css-regions-cloning]),[data-css-regions-fragment-source][data-css-regions-cloned]{display:none!important}[data-css-region]>cssregion:last-of-type{display:inline!important}[data-css-region]{content:normal!important}[data-css-special-continued-fragment]{counter-reset:none!important;counter-increment:none!important;margin-bottom:0!important;border-bottom-left-radius:0!important;border-bottom-right-radius:0!important}[data-css-continued-fragment]{counter-reset:none!important;counter-increment:none!important;margin-bottom:0!important;padding-bottom:0!important;border-bottom:none!important;border-bottom-left-radius:0!important;border-bottom-right-radius:0!important}[data-css-continued-fragment]::after{content:none!important;display:none!important}[data-css-special-starting-fragment]{text-indent:0!important;margin-top:0!important}[data-css-starting-fragment]{text-indent:0!important;margin-top:0!important;padding-top:0!important;border-top:none!important;border-top-left-radius:0!important;border-top-right-radius:0!important}[data-css-starting-fragment]::before{content:none!important;display:none!important}[data-css-continued-block-fragment][data-css-continued-fragment]:not(:empty)::after{content:''!important;display:inline-block!important;width:100%!important;height:0!important;font-size:0!important;line-height:0!important;margin:0!important;padding:0!important;border:0!important}";

    var cssRegions = {
        
        //
        // this function is at the heart of the region polyfill
        // it will iteratively fill a list of regions until no
        // content or no region is left
        //
        // the before-overflow size of a region is determined by
        // adding all content to it and comparing his offsetHeight
        // and his scrollHeight
        //
        // when this is done, we use dom ranges to detect the point
        // where the content exceed this box and we split the fragment
        // at that point.
        //
        // when splitting inside an element, the borders, paddings and
        // generated content must be tied to the right fragments which
        // require some code
        //
        // this functions returns whether some content was still remaining
        // when the flow when the last region was filled. please not this
        // can only happen if this last region has "region-fragment" set
        // to break, otherwhise all the content will automatically overflow
        // this last region.
        //
        layoutContent: function(regions, remainingContent, callback, startTime) {
            
            //
            // this function will iteratively fill all the regions
            // when we reach the last region, we return the overset status
            //
            
            // validate args
            if(!regions) return callback.ondone(!!remainingContent.hasChildNodes());
            if(!regions.length) return callback.ondone(!!remainingContent.hasChildNodes());
            if(!startTime) startTime = Date.now();
            
            // get the next region
            var region = regions.pop();
              
            // NOTE: while we don't monitor that, and it can therefore become inaccurate
            // I'm going to follow the spec and refuse to mark as region inline/none elements]
            while(true) {
                var regionDisplay = getComputedStyle(region).display;
                if(regionDisplay == "none" || regionDisplay.indexOf("inline") !== -1) {
                    if(region = regions.pop()) { continue } else { return callback.ondone(!!remainingContent.hasChildNodes()) };
                } else {
                    break;
                }
            }
            
            // the polyfill actually use a <cssregion> wrapper
            // we need to link this wrapper and the actual region
            if(region.cssRegionsWrapper) {
                region.cssRegionsWrapper.cssRegionHost = region;
                region = region.cssRegionsWrapper;
            } else {
                region.cssRegionHost = region;
            }
            
            // empty the region
            region.innerHTML = '';
            
            // avoid doing the layout of empty regions
            if(!remainingContent.hasChildNodes()) {
                
                region.cssRegionHost.cssRegionsLastOffsetHeight = region.cssRegionHost.offsetHeight;
                region.cssRegionHost.cssRegionsLastOffsetWidth = region.cssRegionHost.offsetWidth;
                
                region.cssRegionHost.regionOverset = 'empty';
                
                var dummyCallback = { ondone:function(){}, onprogress:function(f){f()} };
                cssRegions.layoutContent(regions, remainingContent, dummyCallback, startTime);
                
                return callback.ondone(false);
                
            }
            
            // append the remaining content to the region
            region.appendChild(remainingContent);
            
            // check if we have more regions to process
            if(regions.length !== 0) {
                
                return this.layoutContentInNextRegionsWhenReady(region, regions, remainingContent, callback, startTime);
                
            } else {
                
                return this.layoutContentInLastRegionWhenReady(region, regions, remainingContent, callback, startTime);
                
            }
            
        },
        
        layoutContentInNextRegionsWhenReady: function(region, regions, remainingContent, callback, startTime) {
                    
            // delays until all images are loaded
            var imgs = region.getElementsByTagName('img');
            for(var imgs_index=imgs.length; imgs_index--; ) {
                if(!imgs[imgs_index].complete && !imgs[imgs_index].hasAttribute('height')) {
                    return setTimeout(
                        function() {
                            this.layoutContentInNextRegionsWhenReady(region, regions, remainingContent, callback, startTime+32);
                        }.bind(this), 
                        16
                    );
                }
            }
            
            // check if there was an overflow or some break-before/after instruction
            var regionDidOverflow = region.cssRegionHost.scrollHeight != region.cssRegionHost.offsetHeight;
            var shouldSegmentContent = regionDidOverflow;
            if(!shouldSegmentContent) {
                var first = region.firstElementChild;
                var last = region.lastElementChild;
                var current = first;
                while(current) {
                    
                    if(current != first) {
                        if(/(region|all|always)/i.test(cssCascade.getSpecifiedStyle(current,'break-before',undefined,true).toCSSString())) {
                            shouldSegmentContent = true; break;
                        }
                    }
                    
                    if(current != last) {
                        if(/(region|all|always)/i.test(cssCascade.getSpecifiedStyle(current,'break-after',undefined,true).toCSSString())) {
                            current = current.nextElementSibling;
                            shouldSegmentContent = true; break;
                        }
                    }

                    current = current.nextElementSibling;
                }
            }
            
            
            if(shouldSegmentContent) {
                
                // the remaining content is what was overflowing
                remainingContent = this.extractOverflowingContent(region);
                
            } else {
                
                // there's nothing more to insert
                remainingContent = document.createDocumentFragment();
                
            }
            
            // if any content didn't fit
            if(remainingContent.hasChildNodes()) {
                region.cssRegionHost.regionOverset = 'overset';
            } else {
                region.cssRegionHost.regionOverset = 'fit';
            }
            
            // update flags
            region.cssRegionHost.cssRegionsLastOffsetHeight = region.cssRegionHost.offsetHeight;
            region.cssRegionHost.cssRegionsLastOffsetWidth = region.cssRegionHost.offsetWidth;
            
            // layout the next regions
            // WE LET THE NEXT REGION DECIDE WHAT TO RETURN
            if(startTime+200 > Date.now()) {
                
                return cssRegions.layoutContent(regions, remainingContent, callback, startTime);
                
            } else {
                
                return callback.onprogress(function() {
                    cssRegions.layoutContent(regions, remainingContent, callback);
                });
                
            }
            
        },
        
        layoutContentInLastRegionWhenReady: function(region, regions, remainingContent, callback, startTime) {
            
            // delays until all images are loaded
            var imgs = region.getElementsByTagName('img');
            for(var imgs_index=imgs.length; imgs_index--; ) {
                if(!imgs[imgs_index].complete && !imgs[imgs_index].hasAttribute('height')) {
                    return setTimeout(
                        function() {
                            this.layoutContentInLastRegionWhenReady(region, regions, remainingContent, callback, startTime+32);
                        }.bind(this), 
                        32
                    );
                }
            }
            
            // support region-fragment: break
            if(cssCascade.getSpecifiedStyle(region.cssRegionHost,"region-fragment",undefined,true).toCSSString().trim().toLowerCase()=="break") {
                
                // WE RETURN TRUE IF WE DID OVERFLOW
                var didOverflow = (this.extractOverflowingContent(region).hasChildNodes());
                
                // update flags
                region.cssRegionHost.cssRegionsLastOffsetHeight = region.cssRegionHost.offsetHeight;
                region.cssRegionHost.cssRegionsLastOffsetWidth = region.cssRegionHost.offsetWidth;
                
                return callback.ondone(didOverflow);
                
            } else {
                
                // update flags
                region.cssRegionHost.cssRegionsLastOffsetHeight = region.cssRegionHost.offsetHeight;
                region.cssRegionHost.cssRegionsLastOffsetWidth = region.cssRegionHost.offsetWidth;
                
                // WE RETURN FALSE IF WE DIDN'T OVERFLOW
                return callback.ondone(region.cssRegionHost.offsetHeight != region.cssRegionHost.scrollHeight);
                
            }
        },

        
        //
        // this function returns a document fragment containing the content
        // that didn't fit in a particular <cssregion> element.
        //
        // in the simplest cases, we can just use hit-targeting to get very
        // close the the natural breaking point. for mostly textual flows,
        // this works perfectly, for the others, we may need some tweaks.
        //
        // there's a code detecting whether this hit-target optimization
        // did possibly fail, in which case we return to a setup where we
        // start from scratch.
        //
        extractOverflowingContent: function(region, dontOptimize) {
            
            // make sure empty nodes don't make our life more difficult
            cssRegionsHelpers.embedTrailingWhiteSpaceNodes(region);
            
            // get the region layout
            //var sizingH = region.cssRegionHost.offsetHeight; // avail size (max-height)
            //
            // FIXME: By Alex. added 1pt because it sometimes skips the last line
            var sizingH = region.cssRegionHost.offsetHeight + 1; // avail size (max-height)
            var sizingW = region.cssRegionHost.offsetWidth; // avail size (max-width)
            var pos = region.cssRegionHost.getBoundingClientRect(); // avail size?
            pos = {top: pos.top, bottom: pos.bottom, left: pos.left, right: pos.right};
            
            // substract from the bottom any border/padding of the region
            var lostHeight = parseInt(getComputedStyle(region.cssRegionHost).paddingBottom);
            lostHeight += parseInt(getComputedStyle(region.cssRegionHost).borderBottomWidth);
            pos.bottom -= lostHeight; sizingH -= lostHeight;
            
            //
            // note: let's use hit targeting to find a dom range
            // which is close to the location where we will need to
            // break the content into fragments
            // 
            
            // get the caret range for the bottom-right of that location
            try {
                var r = dontOptimize ? document.createRange() : document.caretRangeFromPoint(
                    pos.left + sizingW - 1,
                    pos.top + sizingH - 1
                );
            } catch (ex) {
                try {
                    cssConsole.error(ex.message);
                    cssConsole.dir(ex);
                } catch (ex) {}
            }
            
            // helper for logging info
            /*cssConsole.log("extracting overflow")
            cssConsole.log(pos.bottom)*/
            var debug = function() {
                /*cssConsole.dir({
                    startContainer: r.startContainer,
                    startOffset: r.startOffset,
                    browserBCR: r.getBoundingClientRect(),
                    computedBCR: rect
                });*/
            }
            
            var fixNullRect = function() {
                if(rect.bottom==0 && rect.top==0 && rect.left==0 && rect.right==0) {
                    
                    var scrollTop = -(document.documentElement.scrollTop || document.body.scrollTop);
                    var scrollLeft = -(document.documentElement.scrollLeft || document.body.scrollLeft);
                    
                    rect = {
                        width: 0,
                        heigth: 0,
                        top: scrollTop,
                        bottom: scrollTop,
                        left: scrollLeft,
                        right: scrollLeft
                    }
                }
            }
            
            // if the caret is outside the region
            if(!r || (region !== r.endContainer && !Node.contains(region,r.endContainer))) {
                
                // if the caret is after the region wrapper but inside the host...
                if(r && r.endContainer === region.cssRegionHost && r.endOffset==r.endContainer.childNodes.length) {
                    
                    // move back at the end of the region, actually
                    r.setStart(region, region.childNodes.length);
                    r.setEnd(region, region.childNodes.length);
                    
                } else {
                    
                    // move back into the region
                    r = r || document.createRange();
                    r.setStart(region, 0);
                    r.setEnd(region, 0);
                    dontOptimize=true;
                    
                }
            }
            
            // start finding the natural breaking point
            do {
                
                // store the current selection rect for fast access
                var rect = r.myGetExtensionRect(); fixNullRect();
                debug();
                
                //
                // note: maybe the text is right-to-left
                // in this case, we can go further than the caret
                //
                
                // move the end point char by char until it's completely in the region
                while(!(r.endContainer==region && r.endOffset==r.endContainer.childNodes.length) && rect.bottom<=pos.top+sizingH) {
                    
                    debug();
                    
                    // look if we can optimize by moving fast forward
                    var nextSibling = r.endContainer.childNodes[r.endOffset];
                    var nextSiblingRect = !nextSibling || Node.getBoundingClientRect(nextSibling);
                    if(nextSibling && nextSiblingRect.bottom<=pos.top+sizingH) {
                        
                        // if yes, move element by element
                        r.setStartAfter(nextSibling)
                        r.setEndAfter(nextSibling)
                        rect = nextSiblingRect
                        fixNullRect()
                        
                    } else {
                        
                        // otherwise, go char-by-char
                        r.myMoveTowardRight(); rect = r.myGetExtensionRect(); fixNullRect();
                        
                    }
                }
                
                //
                // note: maybe the text is one line too big
                // in this case, we have to backtrack a little
                //
                
                // move the end point char by char until it's completely in the region
                while(!(r.endContainer==region && r.endOffset==0) && rect.bottom>pos.top+sizingH) {
                    debug(); r.myMoveOneCharLeft(); rect = r.myGetExtensionRect(); fixNullRect();
                }
                
                debug()
                
                //
                // note: if we optimized via hit-testing, this may be wrong
                // if next condition does not hold, we're fine. 
                // otherwhise we must restart without optimization...
                //
                
                // if the selected content is possibly off-target
                var optimizationFailled = false; if(!dontOptimize) {
                    
                    var current = r.endContainer;
                    while(current = cssRegionsHelpers.getAllLevelPreviousSibling(current, region)) {
                        if(Node.getBoundingClientRect(current).bottom > pos.top + sizingH) {
                            r.setStart(region,0);
                            r.setEnd(region,0);
                            optimizationFailled=true;
                            dontOptimize=true;
                            break;
                        }
                    }
                    
                }
                
            } while(optimizationFailled) 
            
            // 
            // note: we should not break the content inside monolithic content
            // if we do, we need to change the selection to avoid that
            // 
            
            // move the selection before the monolithic ancestors
            var current = r.endContainer;
            while(current !== region) {
                if(cssBreak.isMonolithic(current)) {
                    r.setEndBefore(current);
                }
                current = current.parentNode;
            }
            
            // if the selection is not in the region anymore, add the whole region
            if(!r || (region !== r.endContainer && !Node.contains(region,r.endContainer))) {
                cssConsole.dir(r.cloneRange()); debugger;
                r.setStart(region,region.childNodes.length);
                r.setEnd(region,region.childNodes.length);
            }
            
            // 
            // note: we don't want to break inside a line.
            // backtrack to end of previous line...
            // 
            var first = r.startContainer.childNodes[r.startOffset], current = first; 
            if(cssBreak.hasAnyInlineFlow(r.startContainer)) {
                while((current) && (current = current.previousSibling)) {
                    
                    if(cssBreak.areInSameSingleLine(current,first)) {
                        
                        // optimization: first and current are on the same line
                        // so if next and current are not the same line, it will still be
                        // the same line the "first" element is in
                        first = current;
                        
                        if(current instanceof Element) {
                            
                            // we don't want to break inside text lines
                            r.setEndBefore(current);
                            
                        } else {
                            
                            // get last line via client rects
                            var lines = Node.getClientRects(current);
                            
                            // if the text node did wrap into multiple lines
                            if(lines.length>1) {
                                
                                // move back from the end until we get into previous line
                                var previousLineBottom = lines[lines.length-2].bottom;
                                r.setEnd(current, current.nodeValue.length);
                                while(rect.bottom>previousLineBottom) {
                                    r.myMoveOneCharLeft(); rect = r.myGetExtensionRect(); fixNullRect();
                                }
                                
                                // make sure we didn't exit the text node by mistake
                                if(r.endContainer!==current) {
                                    // if we did, there's something wrong about the text node
                                    // but we can consider the text node as an element instead
                                    r.setEndBefore(current); // debugger; 
                                }
                                
                            } else {
                                
                                // we can consider the text node as an element
                                r.setEndBefore(current);
                                
                            }
                            
                        }
                    } else {
                        
                        // if the two elements are not on the same line, 
                        // then we just found a line break!
                        break;
                        
                    }
                    
                }
            }
            
            // if the selection is not in the region anymore, add the whole region
            if(!r || (region !== r.endContainer && !Node.contains(region,r.endContainer))) {
                cssConsole.dir(r.cloneRange()); debugger;
                r.setStart(region,region.childNodes.length);
                r.setEnd(region,region.childNodes.length);
            }
            
            
            // 
            // note: the css-break spec says that a region should not be emtpy
            // 
            
            // if we end up with nothing being selected, add the first block anyway
            if(r.endContainer===region && r.endOffset===0 && r.endOffset!==region.childNodes.length) {
                
                // find the first allowed break point
                do {
                    
                    //cssConsole.dir(r.cloneRange()); 
                    
                    // move the position char-by-char
                    r.myMoveTowardRight(); 
                    
                    // but skip long islands of monolithic elements
                    // since we know we cannot break inside them anyway
                    var current = r.endContainer;
                    while(current && current !== region) {
                        if(cssBreak.isMonolithic(current)) {
                            r.setStartAfter(current);
                            r.setEndAfter(current);
                        }
                        current = current.parentNode;
                    }
                    
                }
                // do that until we reach a possible break point, or the end of the element
                while(!cssBreak.isPossibleBreakPoint(r,region) && !(r.endContainer===region && r.endOffset===region.childNodes.length))
                
            }
            
            // if the selection is not in the region anymore, add the whole region
            if(!r || region !== r.endContainer && !Node.contains(region,r.endContainer)) {
                cssConsole.dir(r.cloneRange()); debugger;
                r.setStart(region,region.childNodes.length);
                r.setEnd(region,region.childNodes.length);
            }
                
            // now, let's try to find a break-before/break-after element before the splitting point
            var current = r.endContainer; if(current.hasChildNodes()) { if(r.endOffset>0) { current=current.childNodes[r.endOffset-1] } };
            var first = r.endContainer.firstChild;
            do {
                if(current.style) {
                    
                    if(current != first) {
                        if(/(region|all|always)/i.test(cssCascade.getSpecifiedStyle(current,'break-before',undefined,true).toCSSString())) {
                            r.setStartBefore(current);
                            r.setEndBefore(current);
                            dontOptimize=true; // no algo involved in breaking, after all
                        }
                    }
                    
                    if(current !== region) {
                        if(/(region|all|always)/i.test(cssCascade.getSpecifiedStyle(current,'break-after',undefined,true).toCSSString())) {
                            r.setStartAfter(current);
                            r.setEndAfter(current);
                            dontOptimize=true; // no algo involved in breaking, after all
                        }
                    }
                    
                }
            } while(current = cssRegionsHelpers.getAllLevelPreviousSibling(current, region));
            
            // we're almost done! now, let's collect the ancestors to make some splitting postprocessing
            var current = r.endContainer; var allAncestors=[];
            if(current.nodeType !== current.ELEMENT_NODE) current=current.parentNode;
            while(current !== region) {
                allAncestors.push(current);
                current = current.parentNode;
            }
            
            //
            // note: if we're about to split after the last child of
            // an element which has bottom-{padding/border/margin}, 
            // we need to figure how how much of that p/b/m we can
            // actually keep in the first fragment
            // 
            // TODO: avoid top & bottom p/b/m cuttings to use the 
            // same variables names, it's ugly
            //
            
            // split bottom-{margin/border/padding} correctly
            if(r.endOffset == r.endContainer.childNodes.length && r.endContainer !== region) {
                
                // compute how much of the bottom border can actually fit
                var box = r.endContainer.getBoundingClientRect();
                var excessHeight = box.bottom - (pos.top + sizingH);
                var endContainerStyle = getComputedStyle(r.endContainer);
                var availBorderHeight = parseFloat(endContainerStyle.borderBottomWidth);
                var availPaddingHeight = parseFloat(endContainerStyle.paddingBottom);
                
                // start by cutting into the border
                var borderCut = excessHeight;
                if(excessHeight > availBorderHeight) {
                    borderCut = availBorderHeight;
                    excessHeight -= borderCut;
                    
                    // continue by cutting into the padding
                    var paddingCut = excessHeight;
                    if(paddingCut > availPaddingHeight) {
                        paddingCut = availPaddingHeight;
                        excessHeight -= paddingCut;
                    } else {
                        excessHeight = 0;
                    }
                } else {
                    excessHeight = 0;
                }
                
                
                // we don't cut borders with radiuses
                // TODO: accept to cut the content not affected by the radius
                if(typeof(borderCut)==="number" && borderCut!==0) {
                    
                    // check the presence of a radius:
                    var hasBottomRadius = (
                        parseInt(endContainerStyle.borderBottomLeftRadius)>0
                        || parseInt(endContainerStyle.borderBottomRightRadius)>0
                    );
                    
                    if(hasBottomRadius) {
                        // break before the whole border:
                        borderCut = availBorderHeight;
                    }
                    
                }
                
            }
            
            
            // split top-{margin/border/padding} correctly
            if(r.endOffset == 0 && r.endContainer !== region) {
                
                // note: the only possibility here is that we 
                // did split after a padding or a border.
                // 
                // it can only happen if the border/padding is 
                // too big to fit the region but is actually 
                // the first break we could find!
                
                // compute how much of the top border can actually fit
                var box = r.endContainer.getBoundingClientRect();
                var availHeight = (pos.top + sizingH) - pos.top;
                var endContainerStyle = getComputedStyle(r.endContainer);
                var availBorderHeight = parseFloat(endContainerStyle.borderTopWidth);
                var availPaddingHeight = parseFloat(endContainerStyle.paddingTop);
                var excessHeight = availBorderHeight + availPaddingHeight - availHeight;
                
                if(excessHeight > 0) {
                
                    // start by cutting into the padding
                    var topPaddingCut = excessHeight;
                    if(excessHeight > availPaddingHeight) {
                        topPaddingCut = availPaddingHeight;
                        excessHeight -= topPaddingCut;
                        
                        // continue by cutting into the border
                        var topBorderCut = excessHeight;
                        if(topBorderCut > availBorderHeight) {
                            topBorderCut = availBorderHeight;
                            excessHeight -= topBorderCut;
                        } else {
                            excessHeight = 0;
                        }
                    } else {
                        excessHeight = 0;
                    }
                    
                }
                
            }
            
            // remove bottom-{pbm} from all ancestors involved in the cut
            for(var i=allAncestors.length-1; i>=0; i--) {
                allAncestors[i].setAttribute('data-css-continued-fragment',true);
                if(getComputedStyle(allAncestors[i]).display.indexOf('block')>=0) {
                    allAncestors[i].setAttribute('data-css-continued-block-fragment',true);
                }
            }
            if(typeof(borderCut)==="number") {
                allAncestors[0].removeAttribute('data-css-continued-fragment');
                allAncestors[0].setAttribute('data-css-special-continued-fragment',true);
                allAncestors[0].style.borderBottomWidth = (availBorderHeight-borderCut)+'px';
            }
            if(typeof(paddingCut)==="number") {
                allAncestors[0].removeAttribute('data-css-continued-fragment');
                allAncestors[0].setAttribute('data-css-special-continued-fragment',true);
                allAncestors[0].style.paddingBottom = (availPaddingHeight-paddingCut)+'px';
            }
            if(typeof(topBorderCut)==="number") {
                allAncestors[0].removeAttribute('data-css-continued-fragment');
                allAncestors[0].setAttribute('data-css-continued-fragment',true);
                allAncestors[0].style.borderTopWidth = (availBorderHeight-topBorderCut)+'px';
            }
            if(typeof(topPaddingCut)==="number") {
                allAncestors[0].removeAttribute('data-css-continued-fragment');
                allAncestors[0].setAttribute('data-css-special-continued-fragment',true);
                allAncestors[0].style.paddingTop = (availPaddingHeight-topPaddingCut)+'px';
            }
            
            
            //
            // note: at this point we have a collapsed range 
            // located at the split point
            //
            
            // select the overflowing content
            r.setEnd(region, region.childNodes.length);
            
            // extract it from the current region
            var overflowingContent = r.extractContents();
            
            // remove trailing whitespace from the cut element
            var tmp = allAncestors[0];
            if(tmp && (tmp=tmp.lastChild) && !tmp.tagName && tmp.nodeValue) {
                var nodeValue = tmp.nodeValue.replace(/(\s|\r|\n)*$/,'');
                if(nodeValue) {
                    // if the last cut was just after a &shy; (soft hyphen), we need to append a dash
                    if(/\u00AD$/.test(nodeValue)) {
                        nodeValue = nodeValue.replace(/\u00AD$/, '-');
                    }
                    tmp.nodeValue = nodeValue;
                } else {
                    tmp.parentNode.removeChild(tmp);
                }
            }
            
            // 
            // note: now we have to cancel out the artifacts of
            // the fragments cloning algorithm...
            //
            
            // do not forget to remove any top p/b/m on cut elements
            var newFragments = overflowingContent.querySelectorAll("[data-css-continued-fragment]");
            for(var i=newFragments.length; i--;) { // TODO: optimize by using while loop and a simple matchesSelector.
                newFragments[i].removeAttribute('data-css-continued-fragment')
                newFragments[i].setAttribute('data-css-starting-fragment',true);
            }
            
            // deduct any already-used bottom p/b/m
            var specialNewFragment = overflowingContent.querySelector('[data-css-special-continued-fragment]');
            if(specialNewFragment) {
                specialNewFragment.removeAttribute('data-css-special-continued-fragment')
                specialNewFragment.setAttribute('data-css-starting-fragment',true);
                
                if(typeof(borderCut)==="number") {
                    specialNewFragment.style.borderBottomWidth = (borderCut)+'px';
                }
                if(typeof(paddingCut)==="number") {
                    specialNewFragment.style.paddingBottom = (paddingCut);
                } else {
                    specialNewFragment.style.paddingBottom = '0px';
                }
                
                if(typeof(topBorderCut)==="number") {
                    specialNewFragment.removeAttribute('data-css-starting-fragment')
                    specialNewFragment.setAttribute('data-css-special-starting-fragment',true);
                    specialNewFragment.style.borderTopWidth = (topBorderCut)+'px';
                }
                if(typeof(topPaddingCut)==="number") {
                    specialNewFragment.removeAttribute('data-css-starting-fragment')
                    specialNewFragment.setAttribute('data-css-special-starting-fragment',true);
                    specialNewFragment.style.paddingTop = (topPaddingCut)+'px';
                    specialNewFragment.style.paddingBottom = '0px';
                    specialNewFragment.style.borderBottomWidth = '0px';
                }
                
            } else if(typeof(borderCut)==="number") {
                
                // hum... there's an element missing here... {never happens anymore}
                try { throw new Error() }
                catch(ex) { setImmediate(function() { throw ex; }) }
                
            } else if(typeof(topPaddingCut)==="number") {
                
                // hum... there's an element missing here... {never happens anymore}
                try { throw new Error() }
                catch(ex) { setImmediate(function() { throw ex; }) }
                
            }
            
            
            // make sure empty nodes are reintroduced
            cssRegionsHelpers.unembedTrailingWhiteSpaceNodes(region);
            cssRegionsHelpers.unembedTrailingWhiteSpaceNodes(overflowingContent);
            
            // we're ready to return our result!
            return overflowingContent;
            
        },
            
        enablePolyfill: function enablePolyfill() {
            
            //
            // [0] insert necessary css
            //
            var s = document.createElement('style');
            s.setAttribute("data-css-no-polyfill", true);
            s.textContent = CSS_STYLE;
            var head = document.head || document.getElementsByTagName('head')[0];
            head.appendChild(s);
            
            // 
            // [1] when any update happens:
            // construct new content and region flow pairs
            // restart the region layout algorithm for the modified pairs
            // 
            cssCascade.startMonitoringProperties(
                ["flow-into","flow-from","region-fragment"], 
                {
                    onupdate: function onupdate(element, rule) {
                        
                        // let's just ignore fragments
                        if(element.getAttributeNode('data-css-regions-fragment-of')) return;
                        
                        // log some message in the console for debug
                        cssConsole.dir({message:"onupdate",element:element,selector:rule.selector.toCSSString(),rule:rule});
                        var temp = null;
                        
                        //
                        // compute the value of region properties
                        //
                        var flowInto = (
                            cssCascade.getSpecifiedStyle(element, "flow-into")
                            .filter(function(t) { return t instanceof cssSyntax.IdentifierToken })
                        );
                        
                        var flowIntoName = flowInto[0] ? flowInto[0].toCSSString().toLowerCase() : "";
                        if(flowIntoName=="none"||flowIntoName=="initial"||flowIntoName=="inherit"||flowIntoName=="default") {flowIntoName=""}
                        var flowIntoType = flowInto[1] ? flowInto[1].toCSSString().toLowerCase() : ""; 
                        if(flowIntoType!="content") {flowIntoType="element"}
                        var flowInto = flowIntoName ? flowIntoName + " " + flowIntoType : "";
                        
                        var flowFrom = (
                            cssCascade.getSpecifiedStyle(element, "flow-from")
                            .filter(function(t) { return t instanceof cssSyntax.IdentifierToken })
                        );
                        
                        var flowFromName = flowFrom[0] ? flowFrom[0].toCSSString().toLowerCase() : ""; 
                        if(flowFromName=="none"||flowFromName=="initial"||flowFromName=="inherit"||flowFromName=="default") {flowFromName=""}
                        var flowFrom = flowFromName;
                        
                        //
                        // if the value of any property did change...
                        //
                        if(element.cssRegionsLastFlowInto != flowInto || element.cssRegionsLastFlowFrom != flowFrom) {
                            
                            // remove the element from previous regions
                            var regionOverset = element.regionOverset;
                            var lastFlowFrom = (cssRegions.flows[element.cssRegionsLastFlowFromName]);
                            var lastFlowInto = (cssRegions.flows[element.cssRegionsLastFlowIntoName]);
                            lastFlowFrom && lastFlowFrom.removeFromRegions(element);
                            lastFlowInto && lastFlowInto.removeFromContent(element);
                            
                            // relayout those regions 
                            // (it's async so it will wait for us
                            // to add the element back if needed)
                            lastFlowFrom && regionOverset!='empty' && lastFlowFrom.relayout();
                            lastFlowInto && lastFlowInto.relayout();
                            
                            // save some property values for later
                            element.cssRegionsLastFlowInto = flowInto;
                            element.cssRegionsLastFlowFrom = flowFrom;
                            element.cssRegionsLastFlowIntoName = flowIntoName;
                            element.cssRegionsLastFlowFromName = flowFromName;
                            element.cssRegionsLastFlowIntoType = flowIntoType;
                            
                            // add the element to new regions
                            // and relayout those regions, if deemed necessary
                            if(flowFromName) {
                                var lastFlowFrom = (cssRegions.flows[flowFromName] = cssRegions.flows[flowFromName] || new cssRegions.Flow(flowFromName));
                                lastFlowFrom && lastFlowFrom.addToRegions(element);
                                lastFlowFrom && lastFlowFrom.relayout();
                            }
                            if(flowIntoName) {
                                var lastFlowInto = (cssRegions.flows[flowIntoName] = cssRegions.flows[flowIntoName] || new cssRegions.Flow(flowIntoName));
                                lastFlowInto && lastFlowInto.addToContent(element);
                                lastFlowInto && lastFlowInto.relayout();
                            }
                            
                        }
                        
                    }
                }
            );
            cssCascade.startMonitoringProperties(
                ["break-before","break-after"], 
                {onupdate:function(element){
                    
                    // avoid fragments triggering update loops
                    if(element.getAttribute('data-css-regions-fragment-of')){return;}
                    
                    // update parent regions
                    while(element) {
                        if(element.cssRegionsLastFlowIntoName) {
                            cssRegions.flows[element.cssRegionsLastFlowIntoName].relayout();
                            return;
                        }
                        element=element.parentNode;
                    }
                    
                }}
            );
            
            
            //
            // [2] perform the OM exports
            //
            cssRegions.enablePolyfillObjectModel();
            
            //
            // [3] make sure to update the region layout when all images loaded
            //
            window.addEventListener("load", 
                function() { 
                    var flows = document.getNamedFlows();
                    for(var i=0; i<flows.length; i++) {
                        flows[i].relayout();
                    }
                }
            );
            
            // 
            // [4] make sure we react to window resizes
            //
            //
            var lastWindowResize = 0;
            var relayoutModifiedFlows = function() {
                
                // specify the function did run
                relayoutModifiedFlows.timeout = 0;
                
                // rerun the layout
                var flows = document.getNamedFlows();
                for(var i=0; i<flows.length; i++) {
                    if(flows[i].lastRelayout > lastWindowResize) continue;
                    if(flows[i].relayoutInProgress) {
                        flows[i].relayout();
                    } else {
                        flows[i].relayoutIfSizeChanged();
                    }
                }
                
            }
            var hasOngoingLayouts = function() {
                
                var flows = document.getNamedFlows();
                for(var i=0; i<flows.length; i++) {
                    if(flows[i].lastRelayout > lastWindowResize) continue;
                    if(flows[i].relayoutInProgress) {
                        return true;
                    }
                }
                
                return false;
                
            }
            var restartOngoingLayouts = function() {
                
                var flows = document.getNamedFlows();
                for(var i=0; i<flows.length; i++) {
                    if(flows[i].lastRelayout > lastWindowResize) continue;
                    if(flows[i].relayoutInProgress) {
                        flows[i].relayout();
                    }
                }
                
            }
            //window.addEventListener("resize",
                //function() {
                    
                    //// update the last layout flag
                    //lastWindowResize = +new Date();
                    
                    //// if we aren't planning a resfresh already
                    //if(!relayoutModifiedFlows.timeout) { 
                        
                        //// if we are already busy
                        //if(hasOngoingLayouts()) {
                            
                            //// restart all layouts now
                            //setTimeout(restartOngoingLayouts, 16);
                            
                            //// wait half a second before restarting them from now
                            //relayoutModifiedFlows.timeout = setTimeout(relayoutModifiedFlows, 500);
                            
                        //} else {
                            
                            //// debounce by running the resize code every 200ms
                            //relayoutModifiedFlows.timeout = setTimeout(relayoutModifiedFlows, 200);
                            
                        //}
                        
                    //}
                    
                //}
            //);
            
        },
        
        // this dictionary is supposed to contains all the currently existing flows
        flows: Object.create ? Object.create(null) : {}
        
    };
    
    enableObjectModel(window, document, cssRegions);
    
    return cssRegions;
})(window, document);
require.define('src/css-regions/polyfill.js');

////////////////////////////////////////

//require('core:polyfill-dom-matchMedia');
//require('core:polyfill-dom-classList');
//require('css-grid:polyfill');
require('src/css-regions/polyfill.js');
require.define('src/requirements.js');

window.cssPolyfills = { require: require };

})();
//# sourceMappingURL=css-regions-polyfill.js.map