/**
 * Created by Amirka on 08.08.2017.
 */

//The style minimalist notation (minimalist)
_.dependency('minimalistProvider', [ 'middleware', 'getByType', 'css', 'map',
'extend', 'sortBy', 'isObject', 'isString', 'last', 'values', 'isArrayLikeObject', 'intval', 'fill', 'routeParser', 'capitalize', 'lowerCase',
'execute', 'indexOf', 'trim', 'create', 'color', 'split', 'minimalistSelectorsProvider', 'keys', 
'mergeEnd', 'set', 'get', 'unset', 'toPath', 'pullAll', 'cloneDepth', 'extendEnd', 'isNumeric',
'round', 'immediate', 'isEmpty', 'joinArrays', 'noop', 'flags.simple'],
(name, middleware, getByType, __css, __map, extend, sortBy, isObject, isString, __last, __values, isArrayLikeObject, intval, fill, routeParser, capitalize, lowerCase,
execute, indexOf, trim, __create, __color, __split, minimalistSelectorsProvider, __keys,
mergeEnd, __set, __get, __unset, __toPath, __pullAll, cloneDepth, extendEnd, isNumeric, __round, immediate,
 isEmpty, joinArrays, __noop, __flags) => {

    var __push = Array.prototype.push;
    return function($$storage){
        var $$queries = {};
        var $$synonyms = {};
        var $$states = {
            /*
            h: [ ':hover' ],
            hover: [ ':hover' ],

            a: [ ':active' ],
            active: [ ':active' ],

            f: [ ':focus' ],
            focus: [ ':focus' ],

            checked: [ ':checked' ],
            disabled: [ ':disabled' ],

            first: [ ':first-child' ],
            last: [ ':last-child' ],
            link: [ ':link' ]
            */
        };
        var $$priorities = {};
        var $$options = {
            contextMode: 'media-queries', // 'media-queries-essences', //
            enabled: true,
            queries: $$queries,
            states: $$states,
            synonyms: $$synonyms,
            synonymsEnabled: true,
            priorities: $$priorities
        };
        
        var $$essences = {};
        var $$assignors = {};
        var $$contexts = {};
        var $$statics = {};
        var $$styles = {};
        
        
        var $$mode;
        
        var $$deferred = {};
        var $$mergeDepth = 50;


        var $$sidesMap = {
            t: {top: true},
            b: {bottom: true},
            l: {left: true},
            r: {right: true},
            v: {top: true, bottom: true},
            h: {left: true, right: true},
            tl: {top: true, left: true},
            tr: {top: true, right: true},
            bl: {bottom: true, left: true},
            br: {bottom: true, right: true}
        };
        var $$classNames = {};
        var $$newClassNames = [];

        var keyframesContextName = 'keyframes';
        var $$keyframes = {};
        var $$keyframesChange = false;

        var mainContextName = 'main';
        var $$selectors = {};
        var $$selectorsChange = false;
        var defaultMinimalistContextName = '.all';
        var __regexpDelimiter = /\s+/i;
        var __matchName = routeParser('^([a-z]+):name(.+?):suffix?$');
        var __matchValue = routeParser('^(([A-Z][a-z]+):camel|((\\-):negative?[0-9]+):num):value(.+)?$');

        var cross = ((prefixes) => {
            var l = prefixes.length;
            return (src, dst = {}) => {
                for(var k in src){
                    for(var i = l; i--;)dst[prefixes[i] + k] = src[k];
                    dst[k] = src[k];
                }
                return dst;
            };
        })(['-webkit-', '-moz-', '-o-',  '-ms-', '-khtml-' ]);
        
        var $$keyframesPrefixes = (() => {
            var k, dst = {}, src = cross({'keyframes ': true});
            for(k in src)dst['@' + k] = true;
            return dst;
        })();

        

        //$$states, $$queries, $$synonyms, $$options.separators
        var minimalistSelectors = minimalistSelectorsProvider($$options);


        function setOptions(options){
            mergeEnd([ options ], $$options, $$mergeDepth);
            $$states = $$options.states;
            $$synonyms = $$options.synonyms;
            $$priorities = $$options.priorities;
            $$queries = $$options.queries;
            var modeName = $$options.contextMode;

            var mode = modeName && $$contextMods[modeName];
            if(!mode){
                console.error('Context mode "', modeName, '" is not available!');
            }else{
                $$mode = mode;
            }
            //minimalistSelectors = minimalistSelectorsProvider($$options);
            __apply(true);
            return minimalist;
        }
        function setStorage(storage){
            $$storage = storage;
            return minimalist;
        }
        var setPriority = (contextName, priority) => {
            if($$priorities[contextName] == priority)return minimalist;
            if(priority){
                $$priorities[contextName] = priority;
            }else{
                delete $$priorities[contextName];
            }
            __apply(true);
            return minimalist;
        };
        var setPriorities = (priorities) => {
            var priority;
            for(var contextName in priorities){
                if(priority = priorities[contextName]){
                    $$priorities[contextName] = priority;
                }else{
                    delete $$priorities[contextName];
                }
            }
            __apply(true);
            return minimalist;
        };
        var removePriority = (contextName) => {
            delete $$priorities[contextName];
            __apply(true);
            return minimalist;
        };
        var removePriorities = (contexts) => {
            for(var l = contexts.length; l--;)delete $$priorities[contexts[l]];
            __apply(true);
            return minimalist;
        };
        var getPriority = (contextName) => $$priorities[contextName] || 0;
        var getPriorities = (input) => {
            var map = {};
            if(input){
                for(var contextName, l = input.length; l--;){
                    contextName = input[l];
                    map[contextName] = $$priorities[contextName] || 0;
                }
                return map;
            }
            for(var contextName in $$priorities)map[contextName] = $$priorities[contextName] || 0;
            return map;
        };
        function setMediaQuery(contextName, mediaQuery, priority){
            if(arguments.length > 2)setPriority(contextName, priority);
            if($$queries[contextName] == mediaQuery)return minimalist;
            if(mediaQuery){
                $$queries[contextName] = mediaQuery;
            }else{
                delete $$queries[contextName];
            }
            __apply(true);
            return minimalist;
        };
        var getMediaQueries = (input) => {
            var map = {};
            if(input){
                for(var contextName, l = input.length; l--;){
                    contextName = input[l];
                    map[contextName] = $$queries[contextName] || ''
                }
                return map;
            }
            for(var contextName in $$queries)map[contextName] = $$queries[contextName] || ''
            return map;
        };
        var setMediaQueries = (mediaQueries) => {
            var mediaQuery;
            for(var contextName in mediaQueries){
                if(mediaQuery = mediaQueries[contextName]){
                    if(isArrayLikeObject(mediaQuery)){
                        setMediaQuery.apply(null, [ contextName ].concat(mediaQuery));
                    }else{
                        setMediaQuery(contextName, mediaQueries[contextName]);
                    }
                }else{
                    delete $$queries[contextName];
                }
            }
            __apply(true);
            return minimalist;
        };
        var removeMediaQuery = (contextName) => {
            delete $$queries[contextName];
            __apply(true);
            return minimalist;
        };
        var removeMediaQueries = (contexts) => {
            for(var l = contexts.length; l--;)delete $$queries[contexts[l]];
            __apply(true);
            return minimalist;
        };
        var getMediaQuery = (contextName) => $$queries[contextName] || '';
        var getMediaQueries = (input) => {
            var map = {};
            if(input){
                for(var contextName, l = input.length; l--;){
                    contextName = input[l];
                    map[contextName] = $$queries[contextName] || ''
                }
                return map;
            }
            for(var contextName in $$queries)map[contextName] = $$queries[contextName] || ''
            return map;
        };
        var setSynonyms = (targetSelector, selectors) => {
            var l = selectors.length;
            if(!l)return minimalist;
            var synonyms = $$synonyms[targetSelector] || ($$synonyms[targetSelector] = {});
            for(;l--;)synonyms[selectors[l]] = true;
            return minimalist;
        };
        var getSynonyms = (targetSelector) => __keys($$synonyms[targetSelector]);
        var removeSynonyms = (targetSelector, selectors) => {
            var l = selectors.length;
            if(!l)return minimalist;
            var synonyms = $$synonyms[targetSelector];
            if(!synonyms)return minimalist;
            for(;l--;)delete synonyms[selectors[l]];
            return minimalist;
        };
        function setStyle(contextName, content, priority){
            $$styles[contextName] = true;
            $$storage.set(contextName, content, priority);
            return minimalist;
        }
        function removeStyle(contextName){
            $$storage.remove(contextName);
            return minimalist;
        }


        var __assign = (essenceName, selectors, contextName) => {
            contextName || (contextName = defaultMinimalistContextName);
            var context = $$contexts[contextName] || ($$contexts[contextName] = {});
            var contextEssence = context[essenceName] || (context[essenceName] = { map: {} });
            var map = contextEssence.map;
            extend(map, selectors);
            extend(map, $$assignors[essenceName]);
            contextEssence.updated = true;         
        };
        var assignCore = function(essenceName, selectors){
            __assign(essenceName, selectors);
            __updateEssence(essenceName, selectors, {}, {});
        };
        var assign = function(essencesNames, selectors){
            var ei = essencesNames.length;
            var essenceName, assignors, si, sl = selectors.length;
            for(;ei--;){
                essenceName = essencesNames[ei];
                assignors = $$assignors[essenceName] || ($$assignors[essenceName] = {});
                for(si = sl; si--;)assignors[selectors[si]] = true;
                assignCore(essenceName, assignors);
                /*
                __assign(essenceName, assignors);
                __updateEssence(essenceName, assignors, {}, {});
                */
            }
            __apply();
            return minimalist;
        };

        var __norm = (essence) => {
            var style = essence.style;
            if(!isObject(style))essence.style = style ? __css.parse(style) : {};
            var selectors = essence.selectors;
            if(isString(selectors)){
                essence.selectors = [ selectors ];
            }else{
                if(!(selectors && selectors.length))essence.selectors = [ '' ];
            }
            var childs = essence.childs, child;
            for(var k in childs){
                if(child = childs[k])__norm(child);
            }
            return essence;
        };
        var regexpExts = /[\s,]+/;
        var __propNormalize = (obj, key) => {
            var src = obj[key];
            var dst = obj[key] = {};
            if(isString(src))src = src.split(regexpExts);
            if(isObject(src)){
                if(isArrayLikeObject(src)){
                    __flags(src, dst);
                }else{
                    extend(dst, src);
                }
            }
            return obj;
        };
        var __normalize = (essence) => {
            __propNormalize(essence, 'exts');
            var include = essence.include;
            if(isString(include) && (include = include.split(regexpExts)).length){
                essence.include = include;
            }
            return __norm(essence);
        };
        var __getRules = (rules, selectors, style, priority, childs) => {
            var css = __css(style);
            if(css)rules.push({ selectors, css: '{' + css + '}', priority });
            var child;
            for(var k in childs){
                if(child = childs[k])__getRules(
                    rules, joinArrays([], selectors, child.selectors), 
                    child.style, child.priority || 0, child.childs
                );
            }
            return rules;
        };
        var getRules = (e) => {

            /* include impl start */
            var include = e.include;
            var length = include && include.length || 0;
            if(length){
                var mixinEssenseName, mergingMixins = new Array(length + 1);
                mergingMixins[length] = e;
                for(i = length; i--;){
                    mergingMixins[i] = updateEssence(mixinEssenseName = include[i], {}, {}, {});    
                }
                e = mergeEnd(mergingMixins, {}, $$mergeDepth);
            }
            /* include impl end */

            return sortBy(__getRules([], e.selectors || [], e.style, 0, e.childs), item => item.priority);
        };
        var minimalistSet = (essencePath, extendedEssence) => {
            if(!isObject(extendedEssence)){
                console.warn('minimalist.set: value must be an object', extendedEssence);
                return minimalist;
            }
            essencePath = __toPath(essencePath);
            var essenceName = essencePath[0];
            var path = [ essenceName ];
            var prevEssence = $$statics[essenceName];
            var exts = prevEssence ? extend({}, prevEssence.exts) : {};
            for(var i = 1, l = essencePath.length; i < l; i++)path.push('childs', path[i]);
            __set($$statics, path, mergeEnd([ 
                __get($$statics, path), 
                __normalize(extendedEssence)
            ], {}, $$mergeDepth));
            var staticEssence = $$statics[essenceName];
            if(!staticEssence.aliases)staticEssence.aliases = {};

            if(!('inited' in staticEssence))staticEssence.inited = true;

            var delegates = staticEssence.exts;
            staticEssence.exts = exts;
            if(delegates){
                __delegators(essenceName, exts, __pullAll(__keys(delegates), __keys(exts)));
            }else{
                __removeDelegators(essenceName, exts, __keys(exts));
            }
            __apply(true);

            return minimalist;
        };
        var minimalistRemove = (essencePath) => {
            essencePath = __toPath(essencePath);
            var essenceName = essencePath[0];
            var path = [ essenceName ];
            var prevEssence = $$statics[essenceName];
            var exts = prevEssence ? prevEssence.exts : {};
            for(var i = 1, l = essencePath.length; i < l; i++)path.push('childs', path[i]);
            __unset($$statics, path);
            if(!$$statics[essenceName])__removeDelegators(essenceName, exts, __keys(exts));
            __apply();
            return minimalist;
        };
        var minimalistGet = (essencePath) => {
            essencePath = __toPath(essencePath);
            var path = [ essencePath[0] ];
            for(var i = 1, l = essencePath.length; i < l; i++)path.push('childs', path[i]);
            return __get($$statics, path);
        };
        var minimalistSetAliases = (essenceName, aliases) => {
            var staticEssence = $$statics[essenceName] || ($$statics[essenceName] = __newEssence());
            var updated;
            var _aliases = staticEssence.aliases;
            for(var k, i = aliases.length; i--;){
                if(_aliases[k = aliases[i]])continue;
                staticEssence = $$statics[k] || ($$statics[k] = __newEssence());     
                updated = _aliases[k] = staticEssence.exts[essenceName] = true;
            }
            if(updated)__apply(true);
            return minimalist;
        };
        var minimalistRemoveAliases = (essenceName, aliases) => {
            var staticEssence = $$statics[essenceName];
            if(!staticEssence)return minimalist;
            var _aliases = staticEssence.aliases;
            var updated;
            for(var k, i = aliases.length; i--;){
                if(!(_aliases[k = aliases[i]]))continue;
                if((staticEssence = $$statics[k]))continue;
                updated = true;
                delete staticEssence.exts[essenceName];
            }
            if(updated)__apply(true);
            return minimalist;
        };
        var minimalistGetAliases = (essenceName) => {
            var staticEssence = $$statics[essenceName];
            return staticEssence ? __keys(staticEssence.aliases) : [];
        };

        var __delegators = (essenceName, exts, delegators) => {
            var count = 0;
            for(var k, staticEssence, i = delegators.length; i--;){
                if(exts[k = delegators[i]])continue;
                count++;
                staticEssence = $$statics[k] || ($$statics[k] = __newEssence());
                exts[k] = staticEssence.aliases[essenceName] = true;
            }
            return count;
        };
        var minimalistSetDelegators = (essenceName, delegators) => {
            var staticEssence = $$statics[essenceName] || ($$statics[essenceName] = __newEssence());
            if(__delegators(essenceName, staticEssence.exts, delegators))__apply(true);
            return minimalist;
        };
        var __removeDelegators = (essenceName, exts, delegators) => {
            var _staticEssence, count = 0;
            for(var k, i = delegators.length; i--;){
                if(!(exts[k = delegators[i]]))continue;
                count++;
                delete exts[k];
                if(_staticEssence = $$statics[k])delete _staticEssence.aliases[essenceName];
            }
            return count;
        };
        var minimalistRemoveDelegators = (essenceName, delegators) => {
            var staticEssence = $$statics[essenceName];
            if(!staticEssence)return minimalist;
            if(__removeDelegators(essenceName, staticEssence.exts, delegators))__apply();
            return minimalist;
        };
        var minimalistGetDelegators = (essenceName) => {
            var staticEssence = $$statics[essenceName];
            return staticEssence ? __keys(staticEssence.exts) : [];
        };

        var __getEessenceRules = (output, essenceRules, selectors) => {
            var l = essenceRules.length, rule;
            for(;l--;){
                rule = essenceRules[l];
                output.push(joinArrays([], selectors, rule.selectors).join(',') + rule.css);
            }
        };
        var regexpBrowserPrefix = /(\:\:\-|\:\:|\:\-)(([a-z]+)\-)?/;
        var __getEessenceContent = (essenceRules, essenceMap) => {
            var l = essenceRules && essenceRules.length || 0;
            if(l < 1)return '';
            var specifics = {};
            var matchs, prefix, other = [], k;
            for(k in essenceMap){
                matchs = k.match(regexpBrowserPrefix);
                if(!matchs){
                    other.push(k);
                    continue;
                }
                prefix = matchs[3] || 'specific';
                (specifics[prefix] || (specifics[prefix] = [])).push(k);
            }
            var output = [];
            for(k in specifics)__getEessenceRules(output, essenceRules, specifics[k]);
            if(other.length)__getEessenceRules(output, essenceRules, other);
            return output.join('');
        };

        var __argsTypesSetter = {
            object: [ 'style', 'childs' ],
            number: [ 'priority' ]
        };
        var __newEssence = (extended) => {
            var dst = { aliases: {}, exts: {} };
            return extended ? extend(dst, extended) : dst;
        };
        var __extends = (exts, selectors, contexts, excludes) => {
            for(var k in exts)updateEssence(k, extend({}, selectors), contexts, excludes);
        };
        var __essenceSet = (essence, extended) => {
            __normalize(extended);
            extendEnd(essence, extended, $$mergeDepth);
            essence.inited = essence.updated = true;
            return essence;
        };
        var $$waiters = {};
        var __updateEssence = (essenceName, selectors, contexts, excludes) => {
            var essence = $$essences[essenceName];

            var isAsync = true;
            function rend(){
                isAsync = false;
                __extends(essence.exts, selectors, contexts, excludes);
                var include = essence.include;
                if(include && include.length)__extends(__flags(include, {}), {}, {}, excludes);
                essence.updated = true;
                return essence;
            }
            if(essence){
                if(essence.inited)return rend();
            }else{
                var staticEssence = $$statics[essenceName];
                essence = $$essences[essenceName] = staticEssence ? cloneDepth(staticEssence, $$mergeDepth) : {};
                if(essence.inited)return rend();
            }

            function set(){
                __essenceSet(essence, getByType(arguments, __argsTypesSetter));
                isAsync = false;
                delete $$waiters[essenceName];
                return set;
            }
            set.essence = (data) => {
                __essenceSet(essence, data);
                isAsync = false;
                delete $$waiters[essenceName];
                rend();
                return set;
            };
            var args = { params: {},  path: essenceName, suffix: '' };
            __matchName(essenceName, args);
            __matchValue(args.suffix, args);
            $$middleware(args, set);
            if(isAsync){
                $$waiters[essenceName] = true;
                return rend();
            }
        }
        var updateEssence = (essenceName, selectors, contexts, excludes) => {
            if(excludes[essenceName])return;
            if(isEmpty(contexts)){
                __assign(essenceName, selectors);
            }else{
                for(var contextName in contexts)__assign(essenceName, selectors, contextName);
            }
            return __updateEssence(essenceName, selectors, contexts, excludes);
        };
        var updateSelector = (className) => {
            var map = minimalistSelectors(className), suffix, essenceName, rule, essencesNames, selectors, contexts;
            //console.log('map', map);
            for(suffix in map){
                rule = map[suffix];
                essencesNames = rule.essences;
                selectors = rule.selectors;
                contexts = rule.contexts;
                for(essenceName in essencesNames)updateEssence(essenceName, selectors, contexts, {});
            }
        };

        var __getContextContent = (contextName) => {
            var mediaQuery = $$queries[contextName] || '';
            mediaQuery == 'all' && (mediaQuery = '');
            var output = mediaQuery ? ['@media ' , mediaQuery , '{' ] : [];
            var context = $$contexts[contextName]; 
            if(context){
                var essences = sortBy(__values(context), [ 'priority' ]);
                for(var i = 0, l = essences.length; i < l; i++){
                    output.push(essences[i].content);
                }
            };
            mediaQuery && output.push('}');
            return output.join('');            
        };
        var __getContextEssenceContent = (contextName, content) => {
            var mediaQuery = $$queries[contextName] || '';
            mediaQuery == 'all' && (mediaQuery = '');
            return mediaQuery ? ['@media ' , mediaQuery , '{', content, '}' ].join('') : content;        
        };
        var contextPrefix = 'context-';
        var $$contextMods = {
            'media-queries': (contextName) => {
                var force = false;
                var context = $$contexts[contextName] || ($$contexts[contextName] = {});
                var essence, essenceName, contextEssence, staticEssence;
                for(essenceName in context){
                    contextEssence = context[essenceName];
                    if(!(essence = $$essences[essenceName]))continue;
                    if(essence.updated || contextEssence.updated){
                        force = true;
                        if(essence.updated)essence.rules = getRules(essence);
                        contextEssence.priority = essence.priority || 0;
                        contextEssence.updated = essence.updated = false;
                        contextEssence.content = __getEessenceContent(essence.rules, contextEssence.map);
                    }
                }
                if(force)setStyle(contextPrefix + contextName, __getContextContent(contextName), $$priorities[contextName]);
            },
            'media-queries-essences': (contextName) => {
                var context = $$contexts[contextName] || ($$contexts[contextName] = {});
                var essence, essenceName, contextEssence, content;
                for(essenceName in context){
                    contextEssence = context[essenceName];
                    if(!(essence = $$essences[essenceName]))continue;
                    if(essence.updated || contextEssence.updated){
                        if(essence.updated)essence.rules = getRules(essence);
                        contextEssence.priority = essence.priority || 0;
                        contextEssence.updated = essence.updated = false;
                        content = contextEssence.content =  __getEessenceContent(essence.rules, contextEssence.map);
                        setStyle(contextPrefix + contextName + '.' + essenceName, __getContextEssenceContent(contextName, content), $$priorities[contextName]);
                    }
                }
            }
        };
        $$mode = $$contextMods['media-queries'];
        var reset = () => {
            $$essences = {};
            $$contexts = {};
            var contextName;
            for(contextName in $$styles)removeStyle(contextName);
            $$styles = {};
            minimalistSelectors = minimalistSelectorsProvider($$options);
            var k, map = extend({}, $$assignors);
            for(k in $$waiters)map[k] || (map[k] = {});
            for(k in map)assignCore(k, map[k]);
            for(k in $$classNames)updateSelector(k);
            for(contextName in $$queries)$$mode(contextName);
        };
        var clear = () => {
            $$classNames = {};
            $$newClassNames = [];
            reset();
        };

        var onRender = (force) => {
            var k, v, prefix, output;
            if($$keyframesChange){
                $$keyframesChange = false;
                output = [];
                for(k in $$keyframes){
                    v = $$keyframes[k];
                    for(prefix in $$keyframesPrefixes)output.push(prefix, v);
                }
                setStyle(keyframesContextName, output.join(''));

            }

            if($$selectorsChange){
                $$selectorsChange = false;
                output = [];
                for(k in $$selectors)output.push($$selectors[k].content);
                $$storage.set(mainContextName, output.join(''));
            }
            if(minimalist.disabled){
                $$storage.render();
                return;
            }
            
            if(force){
                reset();
            }else{
                //for(k in $$waiters)assignCore(k, {});
                var _newClassNames = $$newClassNames;
                $$newClassNames = [];
                for(var l = _newClassNames.length; l--;)updateSelector(_newClassNames[l]);
                for(var contextName in $$queries)$$mode(contextName);
            }
            $$storage.render();
        };

        var setSides = (sidesMap) => {
            extend($$sidesMap, sidesMap);
            __apply(true);
            return minimalist;
        };
        var setSide = (sideName, sidesMap) => {
            var sideKey, side = $$sidesMap[sideName];
            if(!sidesMap){
                if(side){
                    delete $$sidesMap[sideName];
                    __apply(true);
                }
                return minimalist;
            }
            if(!side)side = {};
            var count = 0, sideValue;
            for(sideKey in sidesMap){
                if(side[sideKey] == (sideValue = sidesMap[sideKey]))continue;
                count++;
                side[sideKey] = sideValue;
            }
            if(!count)return minimalist;
            $$sidesMap[sideName] = side;
            __apply(true);
            return minimalist;
        };
        var getSide = (sideName) => sideName && $$sidesMap[sideName] || null;

        var setStates = (statesMap) => {
            extend($$states, statesMap);
            __apply(true);
            return minimalist;
        };


        var __argsTypesUse = {
            'function': [ 'handler' ],
            string: [ 'path' ],
            object: [ 'data', 'match' ]
        };
        var use = function(){
            var options = getByType(arguments, __argsTypesUse);
            var handler = options.handler;
            if(!handler)return minimalist;
            var data = options.data;
            var match = options.match ? __map(options.match, routeParser) : null;
            var matchHanlder = match ? (req) => execute(match, [ req.suffix, req.params ]) : __noop;

            var useHandler = data ? (isArrayLikeObject(data) ? (req, res, next) => {
                if(indexOf(data, req.name) < 0)return next();
                matchHanlder(req);
                req.options = req.name;
                handler(req, res, next);
            } : (req, res, next) => {
                var options = data[req.name];
                if(!options)return next();
                matchHanlder(req)
                req.options = options;
                handler(req, res, next);
            }) : (req, res, next) => {
                matchHanlder(req);
                handler(req, res, next);
            }
            $$middleware.use(options.path, useHandler);
            __apply(true);
            return minimalist;
        };
        
   
        function __render(force){
            onRender(force || $$deferred.force);
            $$deferred.apply = $$deferred.force = false;
            return minimalist;
        }
        function __deferApply(){
            if($$deferred.apply)__render($$deferred.force);
        }
        function __apply(force){
            $$deferred.force = force || $$deferred.force;
            if($$deferred.apply)return minimalist;
            $$deferred.apply = true;
            immediate(__deferApply);
            return minimalist;
        }
        function render(){
            if($$deferred.apply)return minimalist;
            $$newClassNames.length && __render();
            return minimalist;
        }

        function checkOne(className){
            if(!className || $$classNames[className])return minimalist;
            $$classNames[className] = true;
            $$newClassNames.push(className);
            return minimalist;
        }
        function coreCheck(classNamesStr){
            var classNames = __split(classNamesStr, __regexpDelimiter);
            for(var l = classNames.length; l--;){
                if(!(className = classNames[l]) || $$classNames[className])continue;
                $$classNames[className] = true;
                $$newClassNames.push(className);
            }
        };
        function check(classNamesStr){
            coreCheck(classNamesStr);
            $$newClassNames.length && __apply();
        };
        function ngCheck(ngClass){
            ngClass.replace(reNgClass, __ngCheck);
            $$newClassNames.length && __apply();
            return minimalist;
        }
        function checkNode(node){
            if(minimalist.disabled)return;
            var className = node.className;
            className && check(className);
            var ngClass = node.getAttribute && node.getAttribute('ng-class');
            ngClass && ngClass.replace(reNgClass, __ngCheck);
        };
        var reNgClass = /"([^"]+)"|'([^']+)'|([A-Za-z0-9_]+)\s*\:/g;
        var __ngCheck = (find, a, b, c) => coreCheck(a || b || c);

        var childrenCheck = (nodes) => {
            for(var node, l = nodes.length || 0; l--;){
                checkNode(node = nodes[l]);
                recursiveCheck(node.children);
            }
        };
        var recursiveCheck = (nodes) => {
            for(var node, l = nodes.length, children; l--;){
                checkNode(node = nodes[l]);
                if(children = node.children){
                    recursiveCheck = childrenCheck;
                    recursiveCheck(children);
                    continue;
                }
                if(children = node.childNodes)recursiveCheck(children);
            }
        };


        function minimalist(node){
            if(!$$options.enabled)return;
            if(!node)return minimalist;
            recursiveCheck(isArrayLikeObject(node) ? node : [ node ]);
            render();
            return minimalist;
        };
       
        var wrapSides = (propName, sideName, value, suffix) => {
            suffix || (suffix = '');
            var side, style = {};
            if(sideName){
                if(side = $$sidesMap[sideName]){
                    var priority = 4;
                    propName && (propName += '-');
                    for(var k in side){
                        priority--;
                        style[propName + k + suffix] = value;
                    }
                    return {style, priority};
                }
            }
            style[propName + suffix] = value;
            return {style, priority: 0};
        };

        var setKeyframes = (name, body) => {
            if(!body){
                delete $$keyframes[name];
            }else{
                var css, output = [ name, '{' ];
                if(isObject(body)){
                    for(var k in body){
                        output.push(k, '{', isObject(css = body[k]) ? __css(css) : css, '}');
                    }
                }else{
                    output.push(body);
                }
                output.push('}');
                $$keyframes[name] = output.join('');
            }
            $$keyframesChange = true;
            __apply();
            return minimalist;
        };
        function setCSS(selector, css){
            var instance = $$selectors[selector] || ($$selectors[selector] = { css: {} });
            css = isObject(css) ? extend(instance.css, css) : __css.parse(css, instance.css)
            instance.content = [ selector , '{', __css(css),  '}' ].join('');
            $$selectorsChange = true;
            __apply();
            return minimalist;
        }
        function getCSS(selector, dst){
            var instance = $$selectors[selector];
            return instance ? extend(dst || {}, instance.css) : (dst || {});
        }
        function removeCSS(selector){
            if(!$$selectors[selector])return minimalist;
            delete $$selectors[selector];
            $$selectorsChange = true;
            __apply();
            return minimalist;
        }
        function minimalistCSS(name, value){
            if(arguments.length < 2)return getCSS(name);
            return value ? setCSS(name, value) : removeCSS(name);
        }
        
        //var __defaultEndHandler = (req, res) => req.path && console.warn('No handler for', req.className);
        var __defaultEndHandler = __noop;

        var $$middleware = middleware(__defaultEndHandler);
        

        function Context(name){
            if(!(this instanceof Context))return new Context(name);
            this.$$name = name;
        }
        Context.prototype = {
            mediaQuery(value){
                if(arguments.length < 1)return getMediaQuery(this.$$name);
                value ? setMediaQuery(this.$$name, value) : removeMediaQuery(this.$$name);
                return this;   
            },
            priority(value){
                if(arguments.length < 1)return getPriority(this.$$name);
                value ? setPriority(this.$$name, value) : removePriority(this.$$name);
                return this; 
            },
            set(mediaQuery, priority){
                setMediaQuery(this.$$name, mediaQuery, priority)
            },
            remove(value){
                removeMediaQuery(this.$$name);
                return this; 
            }
        };


        function Essence(name, essence){
            if(!(this instanceof Essence))return new Essence(name, essence);
            this.$$name = name;
            essence && minimalistSet(name, essence);
        }
        Essence.prototype = {
            aliases(value){
                if(arguments.length < 1)return minimalistGetAliases(this.$$name);
                value ? minimalistSetAliases(this.$$name, value) : minimalistRemoveAliases(this.$$name);
                return this; 
            },
            delegators(value){
                if(arguments.length < 1)return minimalistGetDelegators(this.$$name);
                value ? minimalistSetDelegators(this.$$name, value) : minimalistRemoveDelegators(this.$$name);
                return this; 
            },
            set(value){
                minimalistSet(this.$$name, value);
                return this;
            },
            remove(){
                minimalistRemove(this.$$name);
                return this;
            },
            get(value){
                return minimalistGet(this.$$name);
            },
            assign(value){
                assign([ this.$$name ], value);
                return this
            }
        };
        function Selector(name, css){
            if(!(this instanceof Selector))return new Selector(name, css);
            setCSS(this.$$name = name, css);
        }
        Selector.prototype = {
            css(value){
                if(arguments.length < 1)return getCSS(this.$$name);
                value ? setCSS(this.$$name, value) : removeCSS(this.$$name);
                return this; 
            },
            remove(){
                return remove(this.$$name);
            },
            content(){
                var v = this.$$name && $$selectors[this.$$name];
                return v && v.content || '';
            }
        };
        


        


        extend(minimalist, {
            check,
            ngCheck,
            checkOne,
            checkNode,
            setSides,
            setStates,
            getSide,
            setSide,
            setOptions, 
            setStorage,
            setMediaQuery, 
            setMediaQueries,
            getMediaQuery, 
            getMediaQueries,
            removeMediaQuery, 
            removeMediaQueries,
            setPriority, 
            setPriorities,
            getPriority, 
            getPriorities,
            removePriority, 
            removePriorities,
            setSynonyms, 
            getSynonyms, 
            removeSynonyms, 
            setCSS, 
            getCSS, 
            removeCSS,
            css: minimalistCSS,
            assign,

            apply: __apply, 
            render,

            wrapSides,
            cross,
            setKeyframes,

            color: __color, 
            getBackground: __color.getBackground,

            selector: Selector, 
            context: Context, 
            essence: Essence,
            set: minimalistSet, 
            get: minimalistGet, 
            remove: minimalistRemove, 
            setAliases: minimalistSetAliases, 
            getAliases: minimalistGetAliases, 
            removeAliases: minimalistRemoveAliases, 
            use,
            reset
        });


        return minimalist;

    }
});