;(function() {

    var StdLib = {};

    window.StdLib = StdLib;

    var KEYBOARD = {
        ESC: 27
    };

    (function() {
        var local = {};

        function cache(name, key, value) {
            if (value === undefined) {
                var c = local[name];

                if (c === undefined) {
                    return null;
                }

                value = c[key];

                return value === undefined ? null : value;
            }

            if (local[name] === undefined) {
                local[name] = {};
            }

            local[name][key] = value;

            return value;
        }

        StdLib.cache = cache;
    })();

    var object = {
        merge: function(target, input) {
            var output = {};
            var keys = Object.keys(target);
            for (var i = 0; i < keys.length; i++) {
                var key = keys[i];
                if (input.hasOwnProperty(key)) {
                    output[key] = input[key];
                } else {
                    output[key] = target[key];
                }
            }
            return output;
        },
        copy: function(obj) {
            if(typeof(obj) != "object") {
                return obj;
            }

            if(!obj) {
                return obj;
            }

            var res = (obj instanceof Array) ? [] : {};

            for (var i in obj) {
                if(obj.hasOwnProperty(i)) {
                    res[i] = object.copy(obj[i]);
                }
            }

            return res;
        }
    };

    var store = {
        create: function(state) {
            state = state || {};
            var subscribers = [];

            return {
                subscribe: function(subscriber) {
                    subscribers.push(subscriber);
                },
                setState: function(parts) {
                    state = StdLib.object.copy(state);
                    state = StdLib.object.merge(state, parts);
                    for (var i = 0; i < subscribers.length; i++) {
                        subscribers[i](state);
                    }
                },
                getState: function() {
                    return state;
                },
                do: function() {
                    for (var i = 0; i < subscribers.length; i++) {
                        subscribers[i](state);
                    }
                }
            };
        }
    };

    function сookie(name) {
        var value = null;
        if (document.cookie && document.cookie !== '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = cookies[i].trim();
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    value = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return value;
    }

    function Http() {}

    Http.prototype.post = function(config) {
        return this.request({
            method: 'POST',
            body: config.body,
            url: config.url,
            headers: config.headers,
            success: config.success,
            error: config.error,
            withCredentials: config.withCredentials
        });
    };

    Http.prototype.request = function(config) {
        var xhr = new XMLHttpRequest();
        var method = config.method;
        var url = config.url;
        var body = config.body;
        var headers = config.headers;
        var success = config.success;
        var error = config.error;
        var withCredentials = config.withCredentials;

        if (withCredentials) {
            xhr.withCredentials = true;
        }

        xhr.open(method, url);

        if (headers) {
            Object.keys(headers).forEach(function(key) {
                xhr.setRequestHeader(key, headers[key]);
            });
        }

        if (body) {
            xhr.send(body);
        } else {
            xhr.send();
        }

        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                success && success(xhr.responseText);
            } else {
                error && error(xhr);
            }
        };
    };

    function ModalFacade(container, settings) {
        settings = settings || {};

        this.id = ModalFacade.id();

        this.autoControl = settings.autoControl === undefined ? true : settings.autoControl;
        this.onClose = settings.onClose;

        this.container = container;
        this.cross = container.querySelector('.js-modal-close');
        this.modal = container.querySelector('.js-modal-popup');
        this.overlay = container.querySelector('.js-modal-overlay');

        this.keyupHandler = this.keyupHandler.bind(this);
        this.closeClickHandler = this.closeClickHandler.bind(this);
        this.overlayClickHandler = this.overlayClickHandler.bind(this);
    }

    ModalFacade.id = function() {
        if (!ModalFacade.counter) {
            ModalFacade.counter = 0;
        }
        ModalFacade.counter++;
        return ModalFacade.counter;
    };

    ModalFacade.lock = null;

    ModalFacade.prototype.keyupHandler = function(e) {
        if (e.keyCode === StdLib.KEYBOARD.ESC) {
            this.autoControl ? this.close() : this.onClose && this.onClose();
        }
    };

    ModalFacade.prototype.closeClickHandler = function(e) {
        this.autoControl ? this.close() : this.onClose && this.onClose();
    };

    ModalFacade.prototype.overlayClickHandler = function(e) {
        if (!this.modal.contains(e.target)) {
            this.autoControl ? this.close() : this.onClose && this.onClose();
        }
    };

    ModalFacade.prototype.darken = function() {
        this.modal.classList.add('modal-popup--darken');
    };

    ModalFacade.prototype.light = function() {
        this.modal.classList.remove('modal-popup--darken');
    };

    ModalFacade.prototype.open = function() {
        if (ModalFacade.lock) {
            return false;
        }

        ModalFacade.lock = this.id;

        var self = this;

        this.overlay.classList.add('show');

        StdLib.element.animate({
            duration: 300,
            timing: function(fraction) {
                return Math.pow(fraction, 2);
            },
            draw: function(value) {
                self.overlay.style.opacity = value;
            },
            done: function() {
                self.modal.classList.add('show');
                StdLib.element.animate({
                    duration: 300,
                    timing: function(fraction) {
                        return Math.pow(fraction, 2);
                    },
                    draw: function(value) {
                        self.modal.style.opacity = value;
                        self.modal.style.transform = 'translateY(' + -(100 * (1 - value)) + '%)';
                    },
                    done: function() {
                        document.addEventListener('keyup', self.keyupHandler);
                        self.cross.addEventListener('click', self.closeClickHandler);
                        self.overlay.addEventListener('click', self.overlayClickHandler);
                    }
                });
            }
        });

        return true;
    };

    ModalFacade.prototype.close = function() {
        if (ModalFacade.lock !== this.id) {
            return false;
        }

        var self = this;

        StdLib.element.animate({
            duration: 300,
            timing: function(fraction) {
                return Math.pow(fraction, 2);
            },
            draw: function(value) {
                self.modal.style.transform = 'translateY(-' + Math.round(100 * value) + '%)';
                self.modal.style.opacity = 1 - value;
            },
            done: function() {
                self.modal.classList.remove('show');
                StdLib.element.animate({
                    duration: 300,
                    timing: function(fraction) {
                        return Math.pow(fraction, 2);
                    },
                    draw: function(value) {
                        self.overlay.style.opacity = 1 - value;
                    },
                    done: function() {
                        self.overlay.classList.remove('show');

                        ModalFacade.lock = null;
                    }
                });
            }
        });

        document.removeEventListener('keyup', this.keyupHandler);
        this.cross.removeEventListener('click', this.closeClickHandler);
        this.overlay.removeEventListener('click', this.overlayClickHandler);

        return true;
    };

    function demensions(element) {
        var prev = {};
        var props = Object.keys(demensions.styles);

        for (var i = 0; i < props.length; i++) {
            var prop = props[i];
            var value = demensions.styles[prop];
            prev[prop] = element.style[prop];
            element.style[prop] = value;
        }

        var result = {
            width: element.offsetWidth,
            height: element.offsetHeight
        };

        for (var i = 0; i < props.length; i++) {
            var prop =  props[i];
            element.style[prop] = prev[prop];
        }

        return result;
    }

    demensions.styles = {
        display: 'block',
        position: 'absolute',
        visibility: 'hidden',
        height: 'auto',
        width: '100%'
    };

    var element = {
        demensions: demensions,
        animate: function(options) {
            var requestAnimationFrame = (
                window.requestAnimationFrame ||
                window.msRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.webkitRequestAnimationFrame
            );

            var start = performance.now();
            requestAnimationFrame(function animate(time) {
                var timeFraction = (time - start) / options.duration;

                if (timeFraction > 1) {
                    timeFraction = 1;
                }

                var progress = options.timing(timeFraction);

                options.draw(progress);

                if (timeFraction < 1) {
                    requestAnimationFrame(animate);
                } else {
                    requestAnimationFrame(function() {
                        options.done && options.done();
                    });
                }
            });
        },
        delayer: {
            delay: function(element, value) {
                element.style.transitionDelay = value + 'ms';
            },
            free: function(element) {
                setTimeout(function() {
                    element.style.transitionDelay = '0ms';
                }, 300);
            }
        }
    };

    function isTouchDevice() {
        return 'ontouchstart' in document.documentElement;
    };

    StdLib.isTouchDevice = isTouchDevice;
    StdLib.element = element;
    StdLib.store = store;
    StdLib.object = object;
    StdLib.сookie = сookie;
    StdLib.Http = Http;
    StdLib.ModalFacade = ModalFacade;
    StdLib.KEYBOARD = KEYBOARD;

})();