(function(w){var window = w;

window._N2 = window._N2 || {
    _r: [],
    _d: [],
    r: function () {
        this._r.push(arguments);
    },
    d: function () {
        this._d.push(arguments);
    }
};

var document = w.document,
    html = document.documentElement,
    body, // Body is not available at this point
    undefined,
    setTimeout = w.setTimeout,
    clearTimeout = w.clearTimeout,
    _N2 = window._N2,
    _requestAnimationFrame = w.requestAnimationFrame,
    /**
     *
     * @param {string} tagName
     * @param {string|[]} [classes]
     * @param {{}} [attributes]
     * @param {{}} [data]
     * @return {HTMLElement}
     */
    _CreateElement = function (tagName, classes = null, attributes = null, data = null) {

        const element = document.createElement(tagName);

        if (classes) {
            if (typeof classes === 'string') {
                _NodeAddClass(element, classes);
            } else {
                _NodeAddClasses(element, classes);
            }
        }

        if (attributes) {
            _NodeSetAttributes(element, attributes);
        }

        if (data) {
            _NodeSetDatas(element, data);
        }

        return element;
    },
    /**
     *
     * @param {HTMLElement} appendTo
     * @param {string} tagName
     * @param {string|[]} [classes]
     * @param {{}} [attributes]
     * @param {{}} [data]
     * @return {HTMLElement}
     */
    _CreateElementIn = function (appendTo, tagName, classes, attributes, data) {
        const element = _CreateElement(tagName, classes, attributes, data);
        if (appendTo) {
            appendTo.appendChild(element);
        }
        return element;
    },

    /**
     *
     * @param {string} tagName
     * @param {string} text
     * @param {string|[]} [classes]
     * @param {{}} [attributes]
     * @param {{}} [data]
     * @return {HTMLDivElement}
     */
    _CreateElementText = function (tagName, text, classes, attributes, data) {
        const element = _CreateElement(tagName, classes, attributes, data);
        element.innerHTML = text;
        return element;
    },

    /**
     *
     * @param {HTMLElement} appendTo
     * @param {string} tagName
     * @param {string} text
     * @param {string|[]} [classes]
     * @param {{}} [attributes]
     * @param {{}} [data]
     * @return {HTMLDivElement}
     */
    _CreateElementTextIn = function (appendTo, tagName, text, classes, attributes, data) {
        const element = _CreateElementIn(appendTo, tagName, classes, attributes, data);
        element.innerHTML = text;
        return element;
    },

    /**
     *
     * @param {string|[]} [classes]
     * @param {{}} [attributes]
     * @param {{}} [data]
     * @return {HTMLDivElement}
     */
    _CreateElementDiv = function (classes, attributes, data) {
        return _CreateElement('div', classes, attributes, data);
    },

    /**
     *
     * @param {HTMLElement} appendTo
     * @param {string|[]} [classes]
     * @param {{}} [attributes]
     * @param {{}} [data]
     * @return {HTMLDivElement}
     */
    _CreateElementDivIn = function (appendTo, classes, attributes, data) {
        return _CreateElementIn(appendTo, 'div', classes, attributes, data);
    },
    /**
     *
     * @param {string} text
     * @param {string|[]} [classes]
     * @param {{}} [attributes]
     * @param {{}} [data]
     * @return {HTMLDivElement}
     */
    _CreateElementDivText = function (text, classes, attributes, data) {
        return _CreateElementText('div', text, classes, attributes, data);
    },

    /**
     *
     * @param {HTMLElement} appendTo
     * @param {string} text
     * @param {string|[]} [classes]
     * @param {{}} [attributes]
     * @param {{}} [data]
     * @return {HTMLDivElement}
     */
    _CreateElementDivTextIn = function (appendTo, text, classes, attributes, data) {
        return _CreateElementTextIn(appendTo, 'div', text, classes, attributes, data);
    },
    /**
     *
     * @param {string} label
     * @param {string} href
     * @param {string|[]} [classes]
     * @param {{}} [attributes]
     * @param {{}} [data]
     * @return {HTMLAnchorElement}
     */
    _CreateElementLink = function (label, href, classes, attributes, data) {
        const element = _CreateElement('a', classes, attributes, data);
        _NodeSetAttribute(element, 'href', href);
        element.innerHTML = label;
        return element;
    },

    /**
     *
     * @param {HTMLElement} appendTo
     * @param {string} label
     * @param {string} href
     * @param {string|[]} [classes]
     * @param {{}} [attributes]
     * @param {{}} [data]
     * @return {HTMLAnchorElement}
     */
    _CreateElementLinkIn = function (appendTo, label, href, classes, attributes, data) {
        const element = _CreateElementIn(appendTo, 'a', classes, attributes, data);
        _NodeSetAttribute(element, 'href', href);
        element.innerHTML = label;
        return element;
    },

    _Assign = Object.assign,
    _AssignRecursive = function (target, varArgs) {
        var to = Object(target);

        for (var index = 1; index < arguments.length; index++) {
            var nextSource = arguments[index];

            if (nextSource !== null && nextSource !== undefined) {
                for (var nextKey in nextSource) {
                    if (nextSource[nextKey] !== null && Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                        if (typeof nextSource[nextKey] === 'object') {

                            if (nextSource[nextKey].constructor === Object || Array.isArray(nextSource[nextKey])) {
                                // Merge only plain objects or arrays
                                if (Array.isArray(nextSource[nextKey])) {
                                    to[nextKey] = []; // array value always overrides previous value
                                } else if (typeof to[nextKey] !== 'object' || Array.isArray(to[nextKey])) {
                                    to[nextKey] = {}; // always override if target is not object or target is an array.
                                }

                                to[nextKey] = _AssignRecursive(to[nextKey], nextSource[nextKey]);
                            } else {

                                to[nextKey] = nextSource[nextKey];
                            }
                        } else {
                            to[nextKey] = nextSource[nextKey];
                        }
                    }
                }
            }
        }
        return to;
    },
    _NodeGetAttribute = function (node, attribute) {
        return node.getAttribute(attribute);
    },
    _NodeSetAttribute = function (node, attribute, value) {
        node.setAttribute(attribute, value);
    },
    _NodeSetAttributes = function (node, attributes) {
        for (var k in attributes) {
            _NodeSetAttribute(node, k, attributes[k]);
        }
    },
    _NodeRemoveAttribute = function (node, name) {
        node.removeAttribute(name);
    },
    _NodeListSetAttribute = function (nodeList, attribute, value) {
        nodeList.forEach(function (node) {
            _NodeSetAttribute(node, attribute, value);
        });
    },
    _NodeListSetAttributes = function (nodeList, attribute) {
        nodeList.forEach(function (node) {
            _NodeSetAttributes(node, attribute);
        });
    },
    _NodeListAddClass = function (nodeList, className) {
        nodeList.forEach(function (node) {
            _NodeAddClass(node, className);
        });
    },
    _NodeListRemoveClass = function (nodeList, className) {
        nodeList.forEach(function (node) {
            _NodeRemoveClass(node, className);
        });
    },
    _NodeGetData = function (node, property) {
        return node.dataset[property];
    },
    _NodeSetData = function (node, property, value) {
        node.dataset[property] = value;
    },
    _NodeSetDatas = function (node, datas) {
        for (let k in datas) {
            _NodeSetData(node, k, datas[k]);
        }
    },
    _NodeRemoveData = function (node, property) {
        delete node.dataset[property];
    },
    _NodeGetStyle = function (node, property) {
        return window.getComputedStyle(node).getPropertyValue(property);
    },
    _NodeSetStyle = function (node, property, value) {
        node.style.setProperty(property, value);
    },
    _NodeSetStyles = function (node, styles) {
        for (var k in styles) {
            _NodeSetStyle(node, k, styles[k]);
        }
    },
    _NodeRemoveStyle = function (node, property) {
        node.style.removeProperty(property);
    },
    _NodeRemoveStyles = function (node, styles) {
        styles.forEach(function (style) {
            _NodeRemoveStyle(node, style);
        });
    },
    _NodeListSetStyle = function (nodeList, property, value) {
        nodeList.forEach(function (node) {
            _NodeSetStyle(node, property, value);
        });
    },
    _NodeListSetStyles = function (nodeList, styles) {
        nodeList.forEach(function (node) {
            _NodeSetStyles(node, styles);
        });
    },
    _NodeListRemoveStyle = function (nodeList, property) {
        nodeList.forEach(function (node) {
            _NodeRemoveStyle(node, property);
        });
    },
    _NodeRemove = function (node) {
        if (node && node.parentNode) {
            node.parentNode.removeChild(node);
        }
    },
    _NodeListRemove = function (nodeList) {
        nodeList.forEach(function (node) {
            _NodeRemove(node);
        });
    },
    _NodeAddClass = function (node, className) {
        node.classList.add(className);
    },
    _NodeAddClasses = function (node, classNames) {
        classNames.forEach(function (className) {
            node.classList.add(className);
        });
    },
    _NodeRemoveClass = function (node, className) {
        node.classList.remove(className);
    },
    _NodeRemoveClasses = function (node, classNames) {

        classNames.forEach(function (className) {
            node.classList.remove(className);
        });
    },
    _NodeToggleClass = function (node, className, state) {
        if (state) {
            _NodeAddClass(node, className);
        } else {
            _NodeRemoveClass(node, className);
        }
    },
    _NodeAppendTo = function (node, target) {
        target.appendChild(node);
    },
    _NodePrependTo = function (node, target) {

        if (target.childNodes.length) {
            _NodeInsertBefore(node, target.childNodes[0]);
        } else {
            target.appendChild(node);
        }
    },
    _NodeInsertAfter = function (node, target) {

        if (target.nextSibling) {
            _NodeInsertBefore(node, /** @var {Element} */ target.nextSibling);
        } else {
            target.parentNode.appendChild(node);
        }
    },
    _NodeInsertBefore = function (node, target) {

        target.parentNode.insertBefore(node, target);
    },
    _dispatchEvent = function (target, event) {
        return target.dispatchEvent(event);
    },
    _dispatchEventSimple = function (target, eventName, options) {
        options = _Assign({
            bubbles: true,
            cancelable: true
        }, options);
        return _dispatchEvent(target, new Event(eventName, options));
    },
    _dispatchEventSimpleNoBubble = function (target, eventName) {
        return _dispatchEvent(target, new Event(eventName, {
            bubbles: false,
            cancelable: false
        }));
    },
    _dispatchCustomEventNoBubble = function (target, eventName, detail) {
        return _dispatchEvent(target, new CustomEvent(eventName, {
            bubbles: false,
            cancelable: false,
            detail: detail
        }));
    },
    _removeEventListener = function (target, type, listener, options) {
        target.removeEventListener(type, listener, options);
    },
    _removeEventListeners = function (eventListeners) {
        eventListeners.forEach(function (removeCallback) {
            removeCallback()
        });
        eventListeners.splice(0, eventListeners.length);
    },
    _addEventListenerWithRemover = function (target, type, listener, options) {
        options = options || {};
        target.addEventListener(type, listener, options);

        return target.removeEventListener.bind(target, type, listener, options);
    },
    _addEventListener = function (target, type, listener, options) {
        options = options || {};
        target.addEventListener(type, listener, options);
    },
    _addEventListenerOnce = function (target, type, listener) {
        target.addEventListener(type, listener, {
            once: true
        });
    },
    _dispatchPrivateEvent = function (target, event, data) {
        if (target.ssEvent && target.ssEvent[event]) {
            var listeners = target.ssEvent[event];
            listeners.forEach(function (listener) {
                listener(data);
            })
        }
    },
    _addPrivateEventListener = function (target, type, listener) {
        if (!target.ssEvent) {
            target.ssEvent = {};
        }
        if (!target.ssEvent[type]) {
            target.ssEvent[type] = [];
        }
        target.ssEvent[type].push(listener);
    },
    _addPrivateEventListenerWithRemover = function (target, type, listener) {
        _addPrivateEventListener(target, type, listener);

        return _removePrivateEventListener.bind(target, type, listener);
    },
    _removePrivateEventListener = function (type, listener) {
        if (this.ssEvent && this.ssEvent[type]) {
            var listeners = this.ssEvent[type];
            for (var i = listeners.length - 1; i >= 0; i--) {
                if (listeners[i] === listener) {
                    listeners.splice(i, 1);
                }
            }
        }
    },
    _getScrollTop = function () {
        return html.scrollTop;
    },
    _setScrollTop = function (top) {
        html.scrollTop = top;
    },
    _getScrollLeft = function () {
        return html.scrollLeft;
    },
    _getWidth = function (element) {
        return element.getBoundingClientRect().width;
    },
    _getHeight = function (element) {
        return element.getBoundingClientRect().height;
    },
    _getOffsetTop = function (element) {
        return document.scrollingElement.scrollTop + element.getBoundingClientRect().top;
    },
    _ready = function (fn) {
        if (document.readyState === "complete" || document.readyState === "interactive") {
            fn();
        } else {
            if (Document && Document.prototype && Document.prototype.addEventListener && Document.prototype.addEventListener !== document.addEventListener) {
                const fn2 = () => {
                    fn();

                    fn = () => {
                    };
                };

                document.addEventListener("DOMContentLoaded", fn2);

                document.addEventListener("readystatechange", () => {
                    if (document.readyState === "complete" || document.readyState === "interactive") {
                        fn2();
                    }
                });


                /**
                 * WP Rocket overwrites the document.addEventListener which prevents sliders to show.
                 * In that case we use the prototype method to use the native code.
                 */
                Document.prototype.addEventListener.call(document, "DOMContentLoaded", fn2);
            } else {
                document.addEventListener("DOMContentLoaded", fn);
            }
        }
    },
    _animateScroll = function (element, targetTop, duration, doneCallback) {
        if (Math.abs(element.scrollTop - targetTop) < 1) {
            /**
             * Android Chrome has a jumping bug if we change too small scrollTop
             * @see SSDEV-2524
             */
            if (doneCallback) {
                doneCallback();
            }
        } else {
            duration = Math.max(300, duration || 300);

            var currentScrollTop = element.scrollTop,
                diff = targetTop - currentScrollTop,
                startTimestamp = performance.now(),
                _raf = function (timestamp) {
                    var progress = Math.min(1, (timestamp - startTimestamp) / duration);
                    if (progress < 0.5) {
                        progress = 2 * progress * progress;
                    } else {
                        progress = -1 + (4 - 2 * progress) * progress;
                    }

                    element.scrollTop = currentScrollTop + progress * diff;
                    if (progress < 1) {
                        requestAnimationFrame(_raf);
                    } else if (doneCallback) {
                        doneCallback();
                    }
                };
            _raf(startTimestamp);
        }
    },
    _CreateSVGElement = function (tagName, attributes, parentNode) {
        var element = document.createElementNS("http://www.w3.org/2000/svg", tagName);
        if (attributes) {
            _SVGSetAttributes(element, attributes);

            if (parentNode) {
                parentNode.appendChild(element);
            }
        }
        return element;
    },
    _SVGSetAttribute = function (element, name, value) {
        element.setAttributeNS(null, name, value);
    },
    _SVGSetAttributes = function (element, attributes) {
        for (var k in attributes) {
            _SVGSetAttribute(element, k, attributes[k]);
        }
    },
    _ucFirst = function (s) {
        return s.charAt(0).toUpperCase() + s.slice(1);
    },
    _requestIdleCallback = navigator.userAgent.indexOf("+http://www.google.com/bot.html") > -1 ? function (cb) {
        cb();
    } : window.requestIdleCallback || function (cb) {
        return setTimeout(cb, 1);
    },
    _cancelIdleCallback = window.cancelIdleCallback || function (id) {
        clearTimeout(id);
    },
    _strip_tags = function (input, allowed) {
        allowed = (((allowed || '') + '')
            .toLowerCase()
            .match(/<[a-z][a-z0-9]*>/g) || [])
            .join('') // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
        var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
            commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi
        return (input + '').replace(commentsAndPhpTags, '')
            .replace(tags, function ($0, $1) {
                return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''
            })
    },
    _filter_allowed_html = function (input, extraTags = '') {
        return _filter_js_events(_strip_tags(input, '<a><span><sub><sup><em><i><var><cite><b><strong><small><bdo><br><img><picture><source><u><del><bdi><ins>' + extraTags));
    },
    _filter_js_events = function (input) {
        var blacklist = [
                'onclick',
                'onfocus',
                'ondrag',
                'onmouse',
                'onwheel',
                'onscroll',
                'ontouch',
                'onload',
                'onerror'
            ],
            blacklistRegExp = new RegExp(blacklist.join("|"), 'gi');
        return input.replace(blacklistRegExp, 'not-allowed');
    }

_ready(function () {
    body = document.body;
});_N2.d('Base64', function () {

    /**
     * atob and btoa supports ascii only characters, so we need custom base64 library!
     */

    var utf8_encode = function (string) {
            string = string.replace(/\r\n/g, "\n");
            var utftext = "";

            for (var n = 0; n < string.length; n++) {

                var c = string.charCodeAt(n);

                if (c < 128) {
                    utftext += String.fromCharCode(c);
                } else if ((c > 127) && (c < 2048)) {
                    utftext += String.fromCharCode((c >> 6) | 192);
                    utftext += String.fromCharCode((c & 63) | 128);
                } else {
                    utftext += String.fromCharCode((c >> 12) | 224);
                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                    utftext += String.fromCharCode((c & 63) | 128);
                }

            }

            return utftext;
        },
        utf8_decode = function (utftext) {
            var string = "";
            var i = 0;
            var c = c1 = c2 = 0;

            while (i < utftext.length) {

                c = utftext.charCodeAt(i);

                if (c < 128) {
                    string += String.fromCharCode(c);
                    i++;
                } else if ((c > 191) && (c < 224)) {
                    c2 = utftext.charCodeAt(i + 1);
                    string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                    i += 2;
                } else {
                    c2 = utftext.charCodeAt(i + 1);
                    c3 = utftext.charCodeAt(i + 2);
                    string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                    i += 3;
                }

            }

            return string;
        };
    return {

        // private property
        _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

        /**
         *
         * @param input
         * @returns {string}
         */
        encode: function (input) {
            var output = "";
            var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            var i = 0;

            input = utf8_encode(input);

            while (i < input.length) {

                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }

                output = output +
                    this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
                    this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

            }

            return output;
        },

        // public method for decoding
        decode: function (input) {
            var output = "";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;

            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

            while (i < input.length) {

                enc1 = this._keyStr.indexOf(input.charAt(i++));
                enc2 = this._keyStr.indexOf(input.charAt(i++));
                enc3 = this._keyStr.indexOf(input.charAt(i++));
                enc4 = this._keyStr.indexOf(input.charAt(i++));

                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;

                output = output + String.fromCharCode(chr1);

                if (enc3 != 64) {
                    output = output + String.fromCharCode(chr2);
                }
                if (enc4 != 64) {
                    output = output + String.fromCharCode(chr3);
                }

            }

            output = utf8_decode(output);

            return output;

        }
    };
});window.n2_ = function (text) {
    if (_N2._localization && _N2._localization[text] !== undefined) {
        return _N2._localization[text];
    }
    return text;
};

window.n2_sprintf = function (text) {
    var args = arguments;
    var index = 1;
    return text.replace(/%s/g, function () {
        return args[index++];
    });
};

_N2.r('$', function () {
    var $ = _N2.$;

    _N2._animationManager = null;
    _N2._browse = null;

    _N2._roundTo = 5;
    _N2._roundHelper = function (value) {
        if (_N2._roundTo <= 1) {
            return value;
        }

        return Math.round(value / _N2._roundTo) * _N2._roundTo;
    };

    $.fn.n2opener = function () {
        return this.each(function () {
            console.error('deprecated $.fn.n2opener');
            var opener = $(this).on("click", function (e) {
                opener.toggleClass("n2-active");
            });

            opener.siblings('span').on("click", function (e) {
                opener.toggleClass("n2-active");
            });

            opener.parent().on("mouseleave", function () {
                opener.removeClass("n2-active");
            });
            opener.find(".n2-button-menu").on("click", function (e) {
                e.stopPropagation();
                opener.removeClass("n2-active");
            });
        });
    };

    if (jQuery !== undefined) {
        jQuery(document).on('wp-collapse-menu', function () {
            $(window).trigger('resize');
        });
    }


    _N2._deepDiff = function () {
        return {
            map: function (obj1, obj2) {
                if (this.isValue(obj1)) {
                    if (obj1 !== undefined && obj1 != obj2) {
                        return obj1;
                    } else {
                        return undefined;
                    }
                }

                for (var key in obj2) {
                    if (this.isFunction(obj2[key])) {
                        continue;
                    }

                    obj1[key] = this.map(obj1[key], obj2[key]);
                    if (obj1[key] === undefined || ($.isPlainObject(obj1[key]) && $.isEmptyObject(obj1[key])) || (this.isArray(obj1[key]) && obj1[key].length == 0)) {
                        delete obj1[key];
                    }
                }


                return obj1;

            },

            isFunction: function (obj) {
                return {}.toString.apply(obj) === '[object Function]';
            },
            isArray: function (obj) {
                return {}.toString.apply(obj) === '[object Array]';
            },
            isObject: function (obj) {
                return {}.toString.apply(obj) === '[object Object]';
            },
            isValue: function (obj) {
                return !this.isObject(obj) && !this.isArray(obj);
            }
        }
    }();


    _N2._UnicodeToHTMLEntity = function (s) {
        try {
            var patt = /(?:[\uD800-\uDBFF][\uDC00-\uDFFF])/g,
                match;

            function surrogatePairToCodePoint(charCode1, charCode2) {
                return ((charCode1 & 0x3FF) << 10) + (charCode2 & 0x3FF) + 0x10000;
            }

            function stringToCodePointArray(str) {
                var codePoints = [],
                    i = 0,
                    charCode;
                while (i < str.length) {
                    charCode = str.charCodeAt(i);
                    if ((charCode & 0xF800) == 0xD800) {
                        codePoints.push(surrogatePairToCodePoint(charCode, str.charCodeAt(++i)));
                    } else {
                        codePoints.push(charCode);
                    }
                    ++i;
                }
                return '&#' + codePoints + ';';
            }

            while ((match = patt.exec(s))) {
                s = s.substr(0, match.index) + stringToCodePointArray(s.substr(match.index, patt.lastIndex - match.index)) + s.substr(patt.lastIndex);
            }
        } catch (e) {
            console.error(e);
            return s;
        }

        return s;
    };
});_N2.d('Popover', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param $container
     * @constructor
     */
    function Popover($container) {
        this.$container = $container;
        this.$content = $container.find('.n2_popover_content');

        this.$exit = this.$content.find('.n2_popover_content_exit')
            .on('click', this.hide.bind(this));

        this.positionCallback = this.position.bind(this);
        this.$trigger = $container.find('.n2_popover__trigger')
            .on("click", this.show.bind(this));
    }

    Popover.prototype.lazyLoad = function () {
        this.options = _Assign({
            horizontal: 'right',
            vertical: 'below',
            relatedclass: false
        }, this.$container.data());

        this.$content.appendTo('body');

        this.$inner = this.$content.find('.n2_popover_content_inner');

        this.$content.find(".n2_floating_menu__item")
            .on("click", (function (e) {
                if (!$(e.currentTarget).data('stay-open')) {
                    this.hide(e);
                }
            }).bind(this));

        this.lazyLoad = function () {
        };
    };

    Popover.prototype.position = function () {

        var at = this.$trigger[0].getBoundingClientRect(),
            my = this.$inner[0].getBoundingClientRect(),
            x = 0,
            y = 0,
            exitMargin = 0,
            order = 1;

        if (this.options.horizontal === 'left') {
            x = this.getXLeft(at, my);
            if (x < 0) {
                x = this.getXRight(at, my);
                exitMargin = at.x - x;
            }
        } else {
            x = this.getXRight(at, my);
            if (x < 0) {
                x = this.getXLeft(at, my);
            } else {
                exitMargin = at.x - x;
            }
        }

        if (this.options.vertical === 'above') {
            y = this.getYAbove(at, my);
            if (y < 0) {
                y = this.getYBelow(at, my);
            } else {
                order = 3;
            }
        } else {
            y = this.getYBelow(at, my);
            if (y + my.height > window.innerHeight) {
                y = this.getYAbove(at, my);
                order = 3;
            }
        }

        this.$content.css({
            transform: 'translate(' + Math.round(x) + 'px, ' + Math.round(y) + 'px)'
        });

        this.$exit.css({
            order: String(order),
            height: (at.height + 5) + 'px',
            width: Math.round(at.width),
            marginLeft: Math.round(exitMargin) + 'px'
        });
    };

    Popover.prototype.getXLeft = function (at, my) {
        return at.x;
    };

    Popover.prototype.getXRight = function (at, my) {
        return at.x - my.width + at.width;
    };

    Popover.prototype.getYAbove = function (at, my) {
        return at.y - my.height - 5;
    };

    Popover.prototype.getYBelow = function (at, my) {
        return at.y;
    };

    Popover.prototype.show = function (e) {
        if (e) e.preventDefault();
        this.lazyLoad();

        if (this.options.relatedclass) {
            this.$related = this.$container.closest('.' + this.options.relatedclass);
            this.$related.addClass(this.options.relatedclass + '--popover-visible');
        }

        this.$content
            .addClass("n2_popover_content--visible")
            .on('mouseleave', this.hide.bind(this));
        this.position();

        window.addEventListener('scroll', this.positionCallback, {
            capture: true,
            passive: true
        });
    };

    Popover.prototype.hide = function (e) {
        if (this.$related) {
            this.$related.removeClass(this.options.relatedclass + '--popover-visible');
            delete this.$related;
        }

        this.$content
            .off('mouseleave')
            .removeClass("n2_popover_content--visible");

        window.removeEventListener('scroll', this.positionCallback, {
            capture: true,
            passive: true
        });
    };

    Popover.prototype.setStates = function (states) {
        this.$content.find('a[data-state]')
            .each(function () {
                var $el = $(this),
                    matches = $el.data('state').match(/^(!)?(.*)$/);
                if (matches[1] === '!') {
                    $el.toggleClass('n2_floating_menu__item--has-state', !states[matches[2]]);
                } else {
                    $el.toggleClass('n2_floating_menu__item--has-state', states[matches[2]]);
                }
            });
    };

    $.fn.nextendPopover = function () {
        return this.each(function () {
            var $el = $(this);
            $el.data('popover', new Popover($el));
        });
    };

    return Popover;
});_N2.d('NextendHeadingPane', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param $node
     * @param headings
     * @param contents
     * @param identifier
     * @constructor
     */
    function NextendHeadingPane($node, headings, contents, identifier) {
        this.$node = $node.data('pane', this);
        this.headings = headings;
        this.contents = contents;
        this.tabNames = [];
        this.headings.each((function (i, el) {
            this.tabNames.push($(el).data('tab'));
        }).bind(this));
        this.identifier = identifier;

        this._active = headings.index(headings.filter('.n2-active'));

        for (var i = 0; i < headings.length; i++) {
            headings.eq(i).on('click', this.switchToPane.bind(this, i));
        }

        if (identifier) {
            var saved = localStorage.getItem(this.identifier + "-pane") || -1;
            if (saved != -1) {
                this.switchToPane(saved);
                return;
            }
        }
        this.hideAndShow();
    }


    NextendHeadingPane.prototype.switchToPane = function (i, e) {
        if (e) {
            e.preventDefault();
        }
        this.headings.eq(this._active).removeClass('n2-active');
        this.headings.eq(i).addClass('n2-active');
        this._active = i;
        this.hideAndShow();
        this.store(this._active);

        this.$node.triggerHandler('changetab');
    };

    NextendHeadingPane.prototype.hideAndShow = function () {
        $(this.contents[this._active]).css('display', 'block').trigger('activate');
        for (var i = 0; i < this.contents.length; i++) {
            if (i != this._active) {
                $(this.contents[i]).css('display', 'none');
            }
        }
    };

    NextendHeadingPane.prototype.store = function (i) {
        if (this.identifier) {
            localStorage.setItem(this.identifier + "-pane", i);
        }
    };

    NextendHeadingPane.prototype.showTabs = function (tabNames) {
        var activatedFirst = false;
        for (var i = 0; i < this.tabNames.length; i++) {
            if (tabNames.indexOf(this.tabNames[i]) != '-1') {
                this.headings.eq(i).css('display', '');
                $(this.contents[i]).css('display', '');
                if (i == this._active) {
                    activatedFirst = i;
                } else if (activatedFirst === false) {
                    activatedFirst = i;
                }
            } else {
                this.headings.eq(i).css('display', 'none');
                $(this.contents[i]).css('display', 'none');
            }
        }
        this.switchToPane(activatedFirst);
    };

    return NextendHeadingPane;
});_N2.d('Window', function () {

    var topOffset = false;

    function initTopOffset() {
        var wpadminbar = document.getElementById('wpadminbar')
        if (wpadminbar) {
            topOffset = wpadminbar.getBoundingClientRect().height;
        }

        Window.getTopOffset = function () {
            return topOffset;
        };

        return topOffset;
    }

    /**
     * @memberOf _N2
     *
     * @namespace
     */
    var Window = {
        getTopOffset: initTopOffset,
        getBottomOffset: function () {
            return 0;
        },
        getHeight: function () {
            return (window.innerHeight || html.clientHeight) - Window.getTopOffset()
        },
        getWidth: function () {
            return window.innerWidth || html.clientWidth;
        }
    };

    return Window;
});_N2.d('WindowManager', function () {

    var windows = ['main'],
        preventedActions = {};

    /**
     * @memberOf _N2
     */
    var WindowManager = {

        addWindow: function (name) {
            windows.push(name);
        },

        removeWindow: function () {
            windows.pop();
        },

        getCurrentWindow: function () {
            return windows[windows.length - 1];
        },

        actionPrevent: function (action) {
            this.actionCancelPreventRelease(action);
            preventedActions[action] = requestAnimationFrame(this.actionPreventRelease.bind(this, action));
        },
        actionPreventFunction: function (action) {
            return this.actionPrevent.bind(this, action);
        },
        actionCancelPreventRelease: function (action) {
            if (preventedActions[action] !== undefined) {
                cancelAnimationFrame(preventedActions[action]);
                delete preventedActions[action];
            }
        },
        actionPreventRelease: function (action) {
            delete preventedActions[action];
        },
        isActionPrevented: function (action) {
            return preventedActions[action] !== undefined;
        }
    };


    return WindowManager;
});_N2.d('LoadingScreen', ['$'], function () {
    var $ = _N2.$;

    var loader,
        timeout,
        visibleTime,
        minimumDuration = 300;

    _N2.r('documentReady', function () {
        loader = $('<div class="n2_loading_screen"><div class="n2_loading_screen__circle"></div></div>')
            .appendTo('body');
    });

    /**
     * @memberOf _N2
     */
    var LoadingScreen = {};

    LoadingScreen.startLoading = function () {
        if (timeout) {
            clearTimeout(timeout);
        }
        visibleTime = $.now();
        loader.addClass('n2_loading_screen--visible');
    };

    LoadingScreen.stopLoading = function () {
        var elapsed = $.now() - visibleTime;
        if (elapsed >= minimumDuration) {
            loader.removeClass('n2_loading_screen--visible');
        } else {
            timeout = setTimeout(function () {
                loader.removeClass('n2_loading_screen--visible');
            }, minimumDuration - elapsed);
        }
    };

    return LoadingScreen;
});_N2.d('AjaxHelper', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     */
    function AjaxHelper() {

    }

    AjaxHelper.query = {};

    var adminUrls = {};
    AjaxHelper.addAdminUrl = function (key, ajaxUrl) {
        adminUrls[key] = ajaxUrl;
    };

    AjaxHelper.getAdminUrl = function (key) {
        return adminUrls[key];
    };

    AjaxHelper.addAjaxArray = function (parts) {
        for (var k in parts) {
            AjaxHelper.query[k] = parts[k];
        }
    };

    AjaxHelper.makeAjaxUrl = function (url, queries) {
        var urlParts = url.split("?");
        if (urlParts.length < 2) {
            urlParts[1] = '';
        }
        var queryVariables = _N2.N2QueryString.parse(urlParts[1]);
        if (queries !== undefined) {
            for (var k in queries) {
                queryVariables[k] = queries[k];
            }
        }

        for (var k in AjaxHelper.query) {
            queryVariables[k] = AjaxHelper.query[k];
        }

        return _N2.N2QueryString.add_query_arg(queryVariables, urlParts[0]);
    };

    AjaxHelper.ajax = function (ajax) {
        _N2.LoadingScreen.startLoading();
        ajax.data = ajax.data || {}; // Fix for Polylang issue: https://wordpress.org/support/topic/ajaxprefilter-data-should-be-string/
        return $.ajax(ajax).always(function (response, status) {
            _N2.LoadingScreen.stopLoading();
            try {

                if (status) { //Json parse gives error on undefined when XHR aborted
                    if (status !== 'success') {
                        response = JSON.parse(response.responseText);
                    } else if (typeof response === 'string') {
                        response = JSON.parse(response);
                    }
                }
                if (response.redirect !== undefined) {
                    _N2.LoadingScreen.startLoading();
                    window.location.href = response.redirect;
                    return;
                }

                AjaxHelper.notification(response);
            } catch (e) {
                var pattern = /<body[^>]*>((.|[\n\r])*)<\/body>/im,
                    matches = pattern.exec(response.responseText);
                if (matches) {
                    _N2.ModalSafeHTML(matches[1]);
                } else {
                    console.error(response.responseText, response);
                }
            }
        });
    };

    AjaxHelper.notification = function (response) {

        if (response.notification !== undefined && response.notification) {
            for (var k in response.notification) {
                for (var i = 0; i < response.notification[k].length; i++) {
                    _N2.Notification[k](response.notification[k][i][0], response.notification[k][i][1]);
                }
            }
        }
    };

    AjaxHelper.getJSON = function (ajax) {
        _N2.LoadingScreen.startLoading();
        return $.getJSON(ajax)
            .always(function () {
                _N2.LoadingScreen.stopLoading();
            });
    };

    return AjaxHelper;
});_N2.d('Esc', ['$'], function () {
    var $ = _N2.$;

    /**
     * @alias _N2.Esc
     * @constructor
     */
    function Esc() {
        this.FiLo = [];
        this.doc = $(document);
        this.isListening = false;
    }

    Esc.prototype.add = function (callback) {
        this.FiLo.push(callback);

        if (!this.isListening) {
            this.doc
                .on('keydown.n2-esc', (function (e) {
                    if ((e.code === 'Escape' || e.code === 'Backspace')) {
                        if (!$(e.target).is("input, textarea")) {
                            e.preventDefault();
                            this.onClose();
                        } else if (e.code === 'Escape') {
                            e.preventDefault();
                            $(e.target).trigger("blur");
                        }
                    }
                }).bind(this))
                .on('n2Close.n2-esc', (function (e) {
                    this.onClose();
                }).bind(this));
            this.isListening = true;
        }
    };

    Esc.prototype.pop = function () {
        this.FiLo.pop();
        if (this.FiLo.length === 0) {
            this.doc.off('.n2-esc');
            this.isListening = false;
        }
    };

    Esc.prototype.onClose = function () {
        var ret = this.FiLo[this.FiLo.length - 1]();
        if (ret) {
            this.pop();
        }
    };

    return new Esc();
});_N2.d('tooltip', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function Tooltip() {
        this.$element = $('<div class="n2_tooltip"></div>');
        this.timeout = null;
        this.$tipFor = null;


        _ready(this.ready.bind(this));

    }

    Tooltip.prototype.ready = function () {
        this.$element.appendTo('body');
        this.add($('body'));
    };


    Tooltip.prototype.add = function ($parent) {
        $parent.find('[data-n2tip]')
            .off('.n2hastip')
            .on({
                'mouseenter.n2hastip': this.onEnter.bind(this)
            });
    };


    /**
     *
     * @param {jQuery} $el
     * @param {string|function} title
     * @param h
     * @param v
     */
    Tooltip.prototype.addElement = function ($el, title, h, v) {
        $el.data({
            'n2tip': title,
            'n2tipv': v,
            'n2tiph': h
        }).off('.n2hastip')
            .on({
                'mouseenter.n2hastip': this.onEnter.bind(this)
            });
    };

    /**
     *
     * @param {jQuery} $el
     */
    Tooltip.prototype.removeElement = function ($el) {
        $el.off('.n2hastip');
    };

    Tooltip.prototype.hide = function () {
        this.onLeave();
    };
    /**
     *
     * @param {MouseEvent} e
     */
    Tooltip.prototype.onEnter = function (e) {

        /**
         * @type {MouseEvent}
         */
        this.lastE = e;
        if (this.timeout) {
            clearTimeout(this.timeout);
        }
        this.$tipFor = $(e.currentTarget)
            .on({
                'mousemove.n2tip': this.onMove.bind(this),
                'mouseleave.n2tip': this.onLeave.bind(this)
            });

        this.timeout = setTimeout((function () {
            var v = this.$tipFor.data('n2tipv'),
                h = this.$tipFor.data('n2tiph');
            if (v === undefined) {
                v = 10;
            }
            if (h === undefined) {
                h = 10;
            }
            var title = this.$tipFor.data('n2tip');
            if (typeof title === 'function') {
                title = title.call();
            }
            this.$element.css({
                margin: v + 'px ' + h + 'px'
            })
                .html(title)
                .addClass('n2_tooltip--active');
            this.onMove(this.lastE);
        }).bind(this), 500);
    };


    Tooltip.prototype.onMove = function (e) {
        this.lastE = e;
        this.$element.css({
            left: Math.min(e.clientX, window.innerWidth - this.$element.outerWidth() - 30) + 'px',
            top: Math.min(e.clientY, window.innerHeight - this.$element.height() - 20) + 'px',
        });
    };


    Tooltip.prototype.onLeave = function () {
        if (this.timeout) {
            clearTimeout(this.timeout);
        }
        if (this.$tipFor) {
            this.$tipFor.off('.n2tip');
            this.$tipFor = null;
            this.$element.removeClass('n2_tooltip--active').css('margin', '');
        }
    };


    /**
     * Used for parent picker
     *
     * @constructor
     */
    function TooltipMouse() {
        this.isVisible = false;
        this.$body = $('body');
        this.$element = $('<div class="n2_tooltip"></div>').appendTo(this.$body);
    }

    TooltipMouse.prototype.show = function (text, e) {
        if (this.isVisible) {
            this.$element.html(text);
        } else {
            this.isVisible = true;
            this.$body.on('mousemove.tooltipMouse', this.mouseMove.bind(this));
            this.mouseMove(e);
            this.$element.html(text).addClass('n2_tooltip--active');
        }
    };


    TooltipMouse.prototype.mouseMove = function (e) {
        this.$element.css({
            left: Math.min(e.clientX + 10, window.innerWidth - this.$element.outerWidth() - 32) + 'px',
            top: Math.min(e.clientY + 10, window.innerHeight - this.$element.height() - 40) + 'px',
        });
    };


    TooltipMouse.prototype.hide = function () {
        this.$body.off('mousemove.tooltipMouse');
        this.$element.removeClass('n2_tooltip--active').html('');
        this.isVisible = false;
    };


    _N2._tooltip = new Tooltip();

    _ready(function () {
        _N2._tooltipMouse = new TooltipMouse();
    });

    return _N2._tooltip;
});_N2.d('FormTipManager', ['$'], function () {
    var $ = _N2.$;

    var i = 1;

    /**
     *
     * @param $el
     * @constructor
     */
    function FormTip($el) {
        this.$el = $el;

        this.id = i++;

        this.isVisible = false;
        this.$tip = false;

        this.ui = 'el';

        this.$el.on({
            'mouseenter.formTip': this.onMouseEnter.bind(this),
            'mouseleave.formTip': this.onMouseLeave.bind(this)
        });
    }

    FormTip.prototype.onMouseEnter = function () {
        if (this.mouseLeaveTimeout) {
            clearTimeout(this.mouseLeaveTimeout);
        }
        this.mouseEnterTimeout = setTimeout(this.onMouseEnterTimeout.bind(this), 300);
    };

    FormTip.prototype.onMouseEnterTimeout = function () {
        delete this.mouseEnterTimeout;
        if (!this.isVisible) {
            this.ui = 'el';
            this.show();
            this.isVisible = true;
        }
    };

    FormTip.prototype.onMouseLeave = function () {
        if (this.mouseEnterTimeout) {
            clearTimeout(this.mouseEnterTimeout);
        }
        this.mouseLeaveTimeout = setTimeout(this.onMouseLeaveTimeout.bind(this), 300);
    };

    FormTip.prototype.onMouseLeaveTimeout = function () {
        delete this.mouseLeaveTimeout;
        if (this.isVisible && this.ui === 'el') {
            this.hide();
        }
    };

    FormTip.prototype.show = function () {
        if (this.$tip === false) {
            this.buildTip();
        }

        this.$tip.addClass('n2_form_tip--visible');

        $(window)
            .on('scroll.formTip' + this.id, this.positionTip.bind(this))
            .on('resize.formTip' + this.id, this.positionTip.bind(this));

        this.positionTip();
    };

    FormTip.prototype.hide = function () {

        $(window).off('.formTip' + this.id);
        this.$tip.removeClass('n2_form_tip--visible');
        this.isVisible = false;
    };

    FormTip.prototype.stopPropagation = function (e) {
        e.stopPropagation();
    };

    FormTip.prototype.buildTip = function () {
        this.$tip = $('<div class="n2_form_tip"></div>')
            .on({
                'mouseenter': this.onTipMouseEnter.bind(this),
                'mouseleave': this.onTipMouseLeave.bind(this),
                'mousedown': this.stopPropagation.bind(this),
                'mouseup': this.stopPropagation.bind(this),
                'click': this.stopPropagation.bind(this)
            })
            .appendTo('body');

        $('<div class="n2_form_tip__overlay"></div>')
            .appendTo(this.$tip);

        var $content = $('<div class="n2_form_tip__content"></div>')
            .appendTo(this.$tip);

        if (this.$el.data('tip-label')) {
            $('<div class="n2_form_tip__label"></div>')
                .text(this.$el.data('tip-label'))
                .appendTo($content)
        }

        this.$description = $('<div class="n2_form_tip__description"></div>')
            .html(this.$el.data('tip-description'))
            .appendTo($content);

        if (this.$el.data('tip-link')) {
            $('<a class="n2_form_tip__link" href="' + this.$el.data('tip-link') + '" target="_blank">' + n2_('Open docs') + '</a>')
                .appendTo($content)
        }
    };

    FormTip.prototype.setDescription = function (description) {
        if (this.$description) {
            this.$description.html(description);
        } else {
            this.$el.data('tip-description', description);
        }
    };

    FormTip.prototype.onTipMouseEnter = function () {
        this.ui = 'tip';
    };

    FormTip.prototype.onTipMouseLeave = function () {
        this.hide();
    };

    FormTip.prototype.positionTip = function () {
        this.$tip.css({
            left: '0',
            top: '0'
        });
        var windowWidth = window.innerWidth,
            windowHeight = window.innerHeight,
            targetBoundingClientRect = this.$el[0].getBoundingClientRect(),
            tipBoundingClientRect = this.$tip[0].getBoundingClientRect(),
            horizontal,
            vertical,
            isOutOfWindow,
            position = {
                left: '0',
                top: '0'
            };

        if (n2const.rtl.isRtl) {
            isOutOfWindow = targetBoundingClientRect.left > tipBoundingClientRect.width;
        } else {
            isOutOfWindow = windowWidth - targetBoundingClientRect.left < tipBoundingClientRect.width;
        }

        if (isOutOfWindow) {
            horizontal = 'right';
            position.left = targetBoundingClientRect.left + targetBoundingClientRect.width - tipBoundingClientRect.width + 'px';
        } else {
            horizontal = 'left';
            position.left = targetBoundingClientRect.left + 'px';
        }

        if (targetBoundingClientRect.top > tipBoundingClientRect.height) {
            vertical = 'bottom';
            position.top = targetBoundingClientRect.top - tipBoundingClientRect.height + 'px';
        } else {
            vertical = 'top';
            position.top = targetBoundingClientRect.top + targetBoundingClientRect.height + 'px';
        }

        this.$tip
            .attr('data-position-horizontal', horizontal)
            .attr('data-position-vertical', vertical)
            .css(position);
    };

    /**
     * @memberOf _N2
     *
     * @type {{add: add}}
     */
    var FormTipManager = {
        add: function ($container) {

            $container.find('[data-tip-description]')
                .each(function () {
                    var $el = $(this);
                    if (!$el.data('formTip')) {
                        $el.data('formTip', new FormTip($el));
                    }
                });
        }
    };
    _N2.r('windowLoad', function () {
        FormTipManager.add($('body'));
    });

    return FormTipManager;
});/**
 * Convert 8 char hexadecimal color into RGBA color
 * @param 8 characters of hexadecimal color value. Last two character stands for alpha 0-255
 * @returns RGBA representation string
 */

window.N2Color = {
    hex2rgba: function (str) {
        
        if (str[0] === '#') {
            str = str.substring(1);
        }

        if (str.length === 6) {
            str += 'ff';
        }
        var num = parseInt(str, 16); // Convert to a number
        return [num >> 24 & 255, num >> 16 & 255, num >> 8 & 255, (num & 255) / 255];
    },
    hex2rgbaCSS: function (str) {
        return 'RGBA(' + N2Color.hex2rgba(str).join(',') + ')';
    },
    hexdec: function (hex_string) {
        hex_string = (hex_string + '').replace(/[^a-f0-9]/gi, '');
        return parseInt(hex_string, 16);
    },

    hex2alpha: function (str) {
        if (str.length !== 8) {
            return 1;
        }
        var num = parseInt(str, 16); // Convert to a number
        return ((num & 255) / 255).toFixed(3);
    },
    colorizeSVG: function (str, color) {
        var parts = str.split('base64,');
        if (parts.length === 1) {
            return str;
        }
        parts[1] = _N2.Base64.encode(_N2.Base64.decode(parts[1]).replace('fill="#FFF"', 'fill="#' + color.substr(0, 6) + '"').replace('opacity="1"', 'opacity="' + N2Color.hex2alpha(color) + '"'));
        return parts.join('base64,');
    },
    colorToSVG: function (str) {
        var num = parseInt(str, 16); // Convert to a number

        return [str.substr(0, 6), (num & 255) / 255];
    }
};/*
 query-string
 Parse and stringify URL query strings
 https://github.com/sindresorhus/query-string
 by Sindre Sorhus
 MIT License
 */
_N2.d('N2QueryString', function () {
    'use strict';

    /**
     * @memberOf _N2
     *
     * @type {{parse: parse, stringify: function(*=): string}}
     */
    var N2QueryString = {
        parse: function (str) {
            if (typeof str !== 'string') {
                return {};
            }

            str = str.trim().replace(/^(\?|#)/, '');

            if (!str) {
                return {};
            }

            return str.trim().split('&').reduce(function (ret, param) {
                var parts = param.replace(/\+/g, ' ').split('=');
                var key = parts[0];
                var val = parts[1];

                key = decodeURIComponent(key);
                // missing `=` should be `null`:
                // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
                val = val === undefined ? null : decodeURIComponent(val);

                if (!ret.hasOwnProperty(key)) {
                    ret[key] = val;
                } else if (Array.isArray(ret[key])) {
                    ret[key].push(val);
                } else {
                    ret[key] = [ret[key], val];
                }

                return ret;
            }, {});
        },
        stringify: function (obj) {
            return obj ? Object.keys(obj).map(function (key) {
                var val = obj[key];

                if (Array.isArray(val)) {
                    return val.map(function (val2) {
                        return encodeURIComponent(key) + '[]=' + encodeURIComponent(val2);
                    }).join('&');
                }

                return encodeURIComponent(key) + '=' + encodeURIComponent(val);
            }).join('&') : '';

        },

        add_query_arg: function (queryVariables, url) {
            var urlParts = url.split("?");
            if (urlParts.length < 2) {
                urlParts[1] = '';
            }
            var parsed = this.parse(urlParts[1]);
            for (var k in queryVariables) {
                if (queryVariables.hasOwnProperty(k)) {
                    parsed[k] = queryVariables[k];
                }
            }
            return urlParts[0] + '?' + this.stringify(parsed);
        }
    };

    return N2QueryString;
});
!function (g) {
    var $0 = [], // result
        $1 = [], // tail
        $2 = [], // blocks
        $3 = [], // s1
        $4 = ("0123456789abcdef").split(""), // hex
        $5 = [], // s2
        $6 = [], // state
        $7 = false, // is state created
        $8 = 0, // len_cache
        $9 = 0, // len
        BUF = [];

    // use Int32Array if defined
    if (g.Int32Array) {
        $1 = new Int32Array(16);
        $2 = new Int32Array(16);
        $3 = new Int32Array(4);
        $5 = new Int32Array(4);
        $6 = new Int32Array(4);
        BUF = new Int32Array(4);
    } else {
        var i;
        for (i = 0; i < 16; i++) $1[i] = $2[i] = 0;
        for (i = 0; i < 4; i++) $3[i] = $5[i] = $6[i] = BUF[i] = 0;
    }

    // fill s1
    $3[0] = 128;
    $3[1] = 32768;
    $3[2] = 8388608;
    $3[3] = -2147483648;

    // fill s2
    $5[0] = 0;
    $5[1] = 8;
    $5[2] = 16;
    $5[3] = 24;

    function encode(s) {
        var utf = enc = "",
            start = end = 0;

        for (var i = 0, j = s.length; i < j; i++) {
            var c = s.charCodeAt(i);

            if (c < 128) {
                end++;
                continue;
            } else if (c > 127 && c < 2048)
                enc = String.fromCharCode((c >> 6) | 192, (c & 63) | 128);
            else
                enc = String.fromCharCode((c >> 12) | 224, ((c >> 6) & 63) | 128, (c & 63) | 128);

            if (end > start)
                utf += s.slice(start, end);

            utf += enc;
            start = end = i + 1;
        }

        if (end > start)
            utf += s.slice(start, j);

        return utf;
    }

    function md5_update(s) {
        var i, I;

        s += "";
        $7 = false;
        $8 = $9 = s.length;

        if ($9 > 63) {
            getBlocks(s.substring(0, 64));
            md5cycle($2);
            $7 = true;

            for (i = 128; i <= $9; i += 64) {
                getBlocks(s.substring(i - 64, i));
                md5cycleAdd($2);
            }

            s = s.substring(i - 64);
            $9 = s.length;
        }

        $1[0] = 0;
        $1[1] = 0;
        $1[2] = 0;
        $1[3] = 0;
        $1[4] = 0;
        $1[5] = 0;
        $1[6] = 0;
        $1[7] = 0;
        $1[8] = 0;
        $1[9] = 0;
        $1[10] = 0;
        $1[11] = 0;
        $1[12] = 0;
        $1[13] = 0;
        $1[14] = 0;
        $1[15] = 0;

        for (i = 0; i < $9; i++) {
            I = i % 4;
            if (I === 0)
                $1[i >> 2] = s.charCodeAt(i);
            else
                $1[i >> 2] |= s.charCodeAt(i) << $5[I];
        }
        $1[i >> 2] |= $3[i % 4];

        if (i > 55) {
            if ($7) md5cycleAdd($1);
            else {
                md5cycle($1);
                $7 = true;
            }

            return md5cycleAdd([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, $8 << 3, 0]);
        }

        $1[14] = $8 << 3;

        if ($7) md5cycleAdd($1);
        else md5cycle($1);
    }

    function getBlocks(s) {
        for (var i = 16; i--;) {
            var I = i << 2;
            $2[i] = s.charCodeAt(I) + (s.charCodeAt(I + 1) << 8) + (s.charCodeAt(I + 2) << 16) + (s.charCodeAt(I + 3) << 24);
        }
    }

    function md5(data, ascii, arrayOutput) {
        md5_update(ascii ? data : encode(data));

        var tmp = $6[0];
        $0[1] = $4[tmp & 15];
        $0[0] = $4[(tmp >>= 4) & 15];
        $0[3] = $4[(tmp >>= 4) & 15];
        $0[2] = $4[(tmp >>= 4) & 15];
        $0[5] = $4[(tmp >>= 4) & 15];
        $0[4] = $4[(tmp >>= 4) & 15];
        $0[7] = $4[(tmp >>= 4) & 15];
        $0[6] = $4[(tmp >>= 4) & 15];

        tmp = $6[1];
        $0[9] = $4[tmp & 15];
        $0[8] = $4[(tmp >>= 4) & 15];
        $0[11] = $4[(tmp >>= 4) & 15];
        $0[10] = $4[(tmp >>= 4) & 15];
        $0[13] = $4[(tmp >>= 4) & 15];
        $0[12] = $4[(tmp >>= 4) & 15];
        $0[15] = $4[(tmp >>= 4) & 15];
        $0[14] = $4[(tmp >>= 4) & 15];

        tmp = $6[2];
        $0[17] = $4[tmp & 15];
        $0[16] = $4[(tmp >>= 4) & 15];
        $0[19] = $4[(tmp >>= 4) & 15];
        $0[18] = $4[(tmp >>= 4) & 15];
        $0[21] = $4[(tmp >>= 4) & 15];
        $0[20] = $4[(tmp >>= 4) & 15];
        $0[23] = $4[(tmp >>= 4) & 15];
        $0[22] = $4[(tmp >>= 4) & 15];

        tmp = $6[3];
        $0[25] = $4[tmp & 15];
        $0[24] = $4[(tmp >>= 4) & 15];
        $0[27] = $4[(tmp >>= 4) & 15];
        $0[26] = $4[(tmp >>= 4) & 15];
        $0[29] = $4[(tmp >>= 4) & 15];
        $0[28] = $4[(tmp >>= 4) & 15];
        $0[31] = $4[(tmp >>= 4) & 15];
        $0[30] = $4[(tmp >>= 4) & 15];

        return arrayOutput ? $0 : $0.join("");
    }

    function R(q, a, b, x, s1, s2, t) {
        a += q + x + t;
        return ((a << s1 | a >>> s2) + b) << 0;
    }

    function md5cycle(k) {
        md5_rounds(0, 0, 0, 0, k);

        $6[0] = (BUF[0] + 1732584193) << 0;
        $6[1] = (BUF[1] - 271733879) << 0;
        $6[2] = (BUF[2] - 1732584194) << 0;
        $6[3] = (BUF[3] + 271733878) << 0;
    }

    function md5cycleAdd(k) {
        md5_rounds($6[0], $6[1], $6[2], $6[3], k);

        $6[0] = (BUF[0] + $6[0]) << 0;
        $6[1] = (BUF[1] + $6[1]) << 0;
        $6[2] = (BUF[2] + $6[2]) << 0;
        $6[3] = (BUF[3] + $6[3]) << 0;
    }

    function md5_rounds(a, b, c, d, k) {
        var bc, da;

        if ($7) {
            a = R(((c ^ d) & b) ^ d, a, b, k[0], 7, 25, -680876936);
            d = R(((b ^ c) & a) ^ c, d, a, k[1], 12, 20, -389564586);
            c = R(((a ^ b) & d) ^ b, c, d, k[2], 17, 15, 606105819);
            b = R(((d ^ a) & c) ^ a, b, c, k[3], 22, 10, -1044525330);
        } else {
            a = k[0] - 680876937;
            a = ((a << 7 | a >>> 25) - 271733879) << 0;
            d = k[1] - 117830708 + ((2004318071 & a) ^ -1732584194);
            d = ((d << 12 | d >>> 20) + a) << 0;
            c = k[2] - 1126478375 + (((a ^ -271733879) & d) ^ -271733879);
            c = ((c << 17 | c >>> 15) + d) << 0;
            b = k[3] - 1316259209 + (((d ^ a) & c) ^ a);
            b = ((b << 22 | b >>> 10) + c) << 0;
        }

        a = R(((c ^ d) & b) ^ d, a, b, k[4], 7, 25, -176418897);
        d = R(((b ^ c) & a) ^ c, d, a, k[5], 12, 20, 1200080426);
        c = R(((a ^ b) & d) ^ b, c, d, k[6], 17, 15, -1473231341);
        b = R(((d ^ a) & c) ^ a, b, c, k[7], 22, 10, -45705983);
        a = R(((c ^ d) & b) ^ d, a, b, k[8], 7, 25, 1770035416);
        d = R(((b ^ c) & a) ^ c, d, a, k[9], 12, 20, -1958414417);
        c = R(((a ^ b) & d) ^ b, c, d, k[10], 17, 15, -42063);
        b = R(((d ^ a) & c) ^ a, b, c, k[11], 22, 10, -1990404162);
        a = R(((c ^ d) & b) ^ d, a, b, k[12], 7, 25, 1804603682);
        d = R(((b ^ c) & a) ^ c, d, a, k[13], 12, 20, -40341101);
        c = R(((a ^ b) & d) ^ b, c, d, k[14], 17, 15, -1502002290);
        b = R(((d ^ a) & c) ^ a, b, c, k[15], 22, 10, 1236535329);

        a = R(((b ^ c) & d) ^ c, a, b, k[1], 5, 27, -165796510);
        d = R(((a ^ b) & c) ^ b, d, a, k[6], 9, 23, -1069501632);
        c = R(((d ^ a) & b) ^ a, c, d, k[11], 14, 18, 643717713);
        b = R(((c ^ d) & a) ^ d, b, c, k[0], 20, 12, -373897302);
        a = R(((b ^ c) & d) ^ c, a, b, k[5], 5, 27, -701558691);
        d = R(((a ^ b) & c) ^ b, d, a, k[10], 9, 23, 38016083);
        c = R(((d ^ a) & b) ^ a, c, d, k[15], 14, 18, -660478335);
        b = R(((c ^ d) & a) ^ d, b, c, k[4], 20, 12, -405537848);
        a = R(((b ^ c) & d) ^ c, a, b, k[9], 5, 27, 568446438);
        d = R(((a ^ b) & c) ^ b, d, a, k[14], 9, 23, -1019803690);
        c = R(((d ^ a) & b) ^ a, c, d, k[3], 14, 18, -187363961);
        b = R(((c ^ d) & a) ^ d, b, c, k[8], 20, 12, 1163531501);
        a = R(((b ^ c) & d) ^ c, a, b, k[13], 5, 27, -1444681467);
        d = R(((a ^ b) & c) ^ b, d, a, k[2], 9, 23, -51403784);
        c = R(((d ^ a) & b) ^ a, c, d, k[7], 14, 18, 1735328473);
        b = R(((c ^ d) & a) ^ d, b, c, k[12], 20, 12, -1926607734);

        bc = b ^ c;
        a = R(bc ^ d, a, b, k[5], 4, 28, -378558);
        d = R(bc ^ a, d, a, k[8], 11, 21, -2022574463);
        da = d ^ a;
        c = R(da ^ b, c, d, k[11], 16, 16, 1839030562);
        b = R(da ^ c, b, c, k[14], 23, 9, -35309556);
        bc = b ^ c;
        a = R(bc ^ d, a, b, k[1], 4, 28, -1530992060);
        d = R(bc ^ a, d, a, k[4], 11, 21, 1272893353);
        da = d ^ a;
        c = R(da ^ b, c, d, k[7], 16, 16, -155497632);
        b = R(da ^ c, b, c, k[10], 23, 9, -1094730640);
        bc = b ^ c;
        a = R(bc ^ d, a, b, k[13], 4, 28, 681279174);
        d = R(bc ^ a, d, a, k[0], 11, 21, -358537222);
        da = d ^ a;
        c = R(da ^ b, c, d, k[3], 16, 16, -722521979);
        b = R(da ^ c, b, c, k[6], 23, 9, 76029189);
        bc = b ^ c;
        a = R(bc ^ d, a, b, k[9], 4, 28, -640364487);
        d = R(bc ^ a, d, a, k[12], 11, 21, -421815835);
        da = d ^ a;
        c = R(da ^ b, c, d, k[15], 16, 16, 530742520);
        b = R(da ^ c, b, c, k[2], 23, 9, -995338651);

        a = R(c ^ (b | ~d), a, b, k[0], 6, 26, -198630844);
        d = R(b ^ (a | ~c), d, a, k[7], 10, 22, 1126891415);
        c = R(a ^ (d | ~b), c, d, k[14], 15, 17, -1416354905);
        b = R(d ^ (c | ~a), b, c, k[5], 21, 11, -57434055);
        a = R(c ^ (b | ~d), a, b, k[12], 6, 26, 1700485571);
        d = R(b ^ (a | ~c), d, a, k[3], 10, 22, -1894986606);
        c = R(a ^ (d | ~b), c, d, k[10], 15, 17, -1051523);
        b = R(d ^ (c | ~a), b, c, k[1], 21, 11, -2054922799);
        a = R(c ^ (b | ~d), a, b, k[8], 6, 26, 1873313359);
        d = R(b ^ (a | ~c), d, a, k[15], 10, 22, -30611744);
        c = R(a ^ (d | ~b), c, d, k[6], 15, 17, -1560198380);
        b = R(d ^ (c | ~a), b, c, k[13], 21, 11, 1309151649);
        a = R(c ^ (b | ~d), a, b, k[4], 6, 26, -145523070);
        d = R(b ^ (a | ~c), d, a, k[11], 10, 22, -1120210379);
        c = R(a ^ (d | ~b), c, d, k[2], 15, 17, 718787259);
        b = R(d ^ (c | ~a), b, c, k[9], 21, 11, -343485551);

        BUF[0] = a;
        BUF[1] = b;
        BUF[2] = c;
        BUF[3] = d;
    }

    g.md5 = g.md5 || md5;
}(window);
_N2.d('CssInjection', function () {

    /**
     * @memberOf _N2
     * @param {string} baseClassName
     * @param {string} css
     * @constructor
     */
    function CssInjection(baseClassName, css) {

        this.baseClassName = baseClassName;

        this.style = _CreateElement('style');

        this.style.setAttribute('data-base-class-name', baseClassName);
        this.style.setAttribute('type', 'text/css');
        if (this.style.styleSheet) {
            this.style.styleSheet.cssText = css;
        } else {
            this.style.appendChild(document.createTextNode(css));
        }

        body.appendChild(this.style);
    }

    /**
     *
     * @returns {string}
     */
    CssInjection.prototype.getBaseClassName = function () {
        return this.baseClassName;
    };

    CssInjection.prototype.destroy = function () {
        this.style.parentNode.removeChild(this.style);
    };

    return CssInjection;
});_N2.d('NextendCSS', function () {


    /**
     *
     * @constructor
     */
    function NextendCSS() {
        this.style = '';
    }

    /**
     *
     * @param baseClassName
     * @param css
     * @returns {_N2.CssInjection}
     */
    NextendCSS.prototype.add = function (baseClassName, css) {

        return new _N2.CssInjection(baseClassName, css);
    };

    NextendCSS.prototype.deleteRule = function (selectorText) {
        var selectorText1 = selectorText.toLowerCase();
        var selectorText2 = selectorText1.replace('.', '\\.');
        for (var j = document.styleSheets.length - 1; j >= 0; j--) {
            var rules = this._getRulesArray(j);
            for (var i = 0; rules && i < rules.length; i++) {
                if (rules[i].selectorText) {
                    var lo = rules[i].selectorText.toLowerCase();
                    if ((lo == selectorText1) || (lo == selectorText2)) {
                        if (document.styleSheets[j].cssRules) {
                            document.styleSheets[j].deleteRule(i);
                        } else {
                            document.styleSheets[j].removeRule(i);
                        }
                    }
                }
            }
        }
        return true;
    };

    NextendCSS.prototype._getRulesArray = function (i) {
        var crossrule = null;
        try {
            if (document.styleSheets[i].cssRules)
                crossrule = document.styleSheets[i].cssRules;
            else if (document.styleSheets[i].rules)
                crossrule = document.styleSheets[i].rules;
        } catch (e) {
        }
        return (crossrule);
    };

    /**
     * @type {NextendCSS}
     */
    _N2._css = new NextendCSS();

    return _N2._css;
});
_N2.d('ImageHelper', function () {

    /**
     * @memberOf _N2
     *
     * @param parameters
     * @constructor
     */
    function ImageHelper(parameters) {
        /**
         * @type {_N2.ImageHelper}
         */
        _N2._imageHelper = this;
        this.parameters = _Assign({
            siteKeywords: [],
            imageUrls: [],
            wordpressUrl: '',
            placeholderImage: '',
            placeholderRepeatedImage: '',
            protocolRelative: 1
        }, parameters);
    }

    ImageHelper.prototype.protocolRelative = function (image) {
        if (this.parameters.protocolRelative) {
            return image.replace(/^http(s)?:\/\//, '//');
        }
        return image;
    };

    ImageHelper.prototype.dynamic = function (image) {
        var imageUrls = this.parameters.imageUrls,
            keywords = this.parameters.siteKeywords,
            _image = this.protocolRelative(image);
        for (var i = 0; i < keywords.length; i++) {
            if (_image.indexOf(imageUrls[i]) === 0) {
                image = keywords[i] + _image.slice(imageUrls[i].length);
                break;
            }
        }
        return image;
    };

    ImageHelper.prototype.fixed = function (image) {
        if (typeof image === 'string') {
            var imageUrls = this.parameters.imageUrls,
                keywords = this.parameters.siteKeywords;

            for (var i = 0; i < keywords.length; i++) {
                if (image.indexOf(keywords[i]) === 0) {
                    image = imageUrls[i] + image.slice(keywords[i].length);
                    break;
                }
            }
        }
        return image;
    };
    ImageHelper.prototype.openLightbox = function (callback) {
        _N2.PlatformImageChooser.single(callback);
    };

    ImageHelper.prototype.openMultipleLightbox = function (callback) {
        _N2.PlatformImageChooser.multiple(callback);
    };

    ImageHelper.prototype.getPlaceholder = function () {
        return this.fixed(this.parameters.placeholderImage);
    };

    ImageHelper.prototype.getRepeatedPlaceholder = function () {
        return this.fixed(this.parameters.placeholderRepeatedImage);
    };

    return ImageHelper;
});_N2.d('PlatformImageChooser', ['$'], function () {

    /**
     *
     * @memberOf _N2
     * @constructor
     */
    function PlatformImageChooser() {

    }

    PlatformImageChooser.single = function (callback) {
        _N2.WindowManager.addWindow("imagechooser");

        var attributes = {
            states: [new wp.media.controller.Library({
                filterable: "all",
                priority: 20
            })]
        };
        /*
        filterable: 'uploaded',
        library: wp.media.query({ type: 'image' }),
         */

        if (wp.media.controller.EditImage !== undefined) {
            attributes.states.push(new wp.media.controller.EditImage());
        }

        var frame = wp.media(attributes);

        // edit image view
        // source: media-views.js:2410 editImageContent()
        frame.on("content:render:edit-image", function () {

            var image = this.state().get("image"),
                view = new wp.media.view.EditImage({model: image, controller: this}).render();

            this.content.set(view);

            // after creating the wrapper view, load the actual editor via an ajax call
            view.loadEditor();

        }, frame);

        frame.on("select", (function () {
            var attachment = frame.state().get("selection").first().toJSON();
            callback(_N2._imageHelper.dynamic(attachment.url), {alt: attachment.alt});
        }).bind(this));
        frame.on("close", function () {
            _N2.WindowManager.removeWindow();
            setTimeout(function () {
                _N2.Esc.pop();
            }, 50)
        });
        _N2.Esc.add(function () {
            return false;
        });
        frame.open();
    };

    PlatformImageChooser.multiple = function (callback) {
        _N2.WindowManager.addWindow("imagechooser");
        var attributes = {
            states: [new wp.media.controller.Library({
                filterable: "all",
                multiple: "add",
                priority: 20
            })]
        };

        if (wp.media.controller.EditImage !== undefined) {
            attributes.states.push(new wp.media.controller.EditImage());
        }

        var frame = wp.media(attributes);

        // edit image view
        // source: media-views.js:2410 editImageContent()
        frame.on("content:render:edit-image", function () {

            var image = this.state().get("image"),
                view = new wp.media.view.EditImage({model: image, controller: this}).render();

            this.content.set(view);

            // after creating the wrapper view, load the actual editor via an ajax call
            view.loadEditor();

        }, frame);

        frame.on("select", (function () {
            var attachments = frame.state().get("selection").toJSON(),
                images = [];

            for (var i = 0; i < attachments.length; i++) {
                var attachment = attachments[i];
                images.push({
                    title: attachment.title,
                    description: attachment.description,
                    media: _N2._imageHelper.dynamic(attachment.url),
                    alt: attachment.alt
                })
            }
            callback(images);
        }).bind(this));
        frame.on("close", function () {
            _N2.WindowManager.removeWindow();
            setTimeout(function () {
                _N2.Esc.pop();
            }, 50)
        });
        frame.open();
        _N2.Esc.add(function () {
            return false;
        });
    };

    return PlatformImageChooser;
});_N2.d('AbstractModal', ['$'], function () {
    var $ = _N2.$;

    /**
     *
     * @memberOf _N2
     *
     * @abstract
     * @constructor
     */
    function AbstractModal(type, options) {
        AbstractModal.counter++;

        this.id = AbstractModal.counter;
        this.type = type;

        this.buttons = [];

        this.options = _Assign({
            hasClose: true,
            width: false,
            destroyOnHide: false
        }, options);

        this.$modal = $('<div class="n2 n2_modal" data-modal-type="' + this.type + '"></div>')
            .appendTo('body');

        if (this.options.hasClose) {
            $('<div class="n2_modal__overlay"></div>')
                .on('click', this.hide.bind(this))
                .appendTo(this.$modal)
        }

        this.$modalWindow = $('<div class="n2_modal__window"></div>')
            .appendTo(this.$modal);

        if (this.options.width !== false) {
            this.$modalWindow.css('width', this.options.width + 'px');
        }

        this.renderModal();
    }

    AbstractModal.counter = 0;

    /**
     * @abstract
     */
    AbstractModal.prototype.renderModal = function () {

    };

    AbstractModal.prototype.show = function () {

        if (document.activeElement && document.activeElement !== body) {
            document.activeElement.blur();
        }

        _N2.WindowManager.addWindow("modal");
        if (this.options.hasClose) {
            _N2.Esc.add((function () {
                this.hide('esc');
                return true;
            }).bind(this));
        }

        this.$modal.addClass('n2_modal--visible');
    };

    AbstractModal.prototype.hide = function (e) {
        _N2.WindowManager.removeWindow();
        if (e !== 'esc') {
            _N2.Esc.pop();

            if (e) {
                e.preventDefault();
            }
        }

        this.$modal.removeClass('n2_modal--visible');

        if (this.options.destroyOnHide) {
            this.$modal.remove();
        }

        if (document.activeElement && document.activeElement !== body) {
            document.activeElement.blur();
        }
    };

    AbstractModal.prototype.getBody = function () {
        return this.$modalBody;
    };

    AbstractModal.prototype.addContent = function ($content) {
        $content.appendTo(this.$modalBody);
    };

    AbstractModal.prototype.clearButtons = function () {
        for (var k in this.buttons) {
            this.buttons[k].remove();
        }
        this.buttons = [];
        this.$modalButtons.html('')
            .attr('data-buttons', 0);
    };

    AbstractModal.prototype.addButton = function ($button, prepend) {

        prepend = prepend || false;

        var $buttonContainer = $('<div class="n2_modal__button"></div>')
            .append($button);
        if (prepend) {
            $buttonContainer.prependTo(this.$modalButtons);
        } else {
            $buttonContainer.appendTo(this.$modalButtons);
        }
        this.buttons.push($buttonContainer);

        this.$modalButtons
            .attr('data-buttons', this.buttons.length);

        return $buttonContainer;
    };

    return AbstractModal;
});_N2.d('ModalDeleteConfirm', ['$'], function () {
    var $ = _N2.$;

    /**
     *
     * @memberOf _N2
     *
     * @param label
     * @param callback
     * @function
     */
    function ModalDeleteConfirm(label, callback) {

        var deleteModal = new _N2.ModalSimple('delete', {
            destroyOnHide: true
        });

        deleteModal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--delete', 'red'));
        deleteModal.addContent(_N2.UI.modalHeading(n2_('Are you sure?')));

        deleteModal.addContent(_N2.UI.modalParagraphHTML(n2_sprintf(n2_('You\'re about to %s. '), label) + n2_sprintf(n2_('The deletion is irreversible, and it\'s not possible to recover %s.'), label)).css('margin-bottom', '19px'));

        var $cancelButton = _N2.UI.modalButton(n2_('Cancel'), 'grey')
            .on('click', function (e) {
                e.preventDefault();
                deleteModal.hide();
            });
        deleteModal.addButton($cancelButton);

        var $deleteButton = _N2.UI.modalButton(n2_('Delete'), 'red')
            .on('click', function (e) {
                e.preventDefault();
                callback();
                deleteModal.hide(e);
            });
        deleteModal.addButton($deleteButton);

        deleteModal.show();
    }

    ModalDeleteConfirm.href = function (e, element, label) {
        e.preventDefault();

        ModalDeleteConfirm(label, function () {
            window.location.href = $(element).attr('href');
        });
    };

    return ModalDeleteConfirm;
});_N2.d('ModalIframe', ['$', 'AbstractModal'], function () {
    var $ = _N2.$;

    /**
     *
     * @memberOf _N2
     *
     * @constructor
     * @augments _N2.AbstractModal
     */
    function ModalIframe(type, options) {

        _N2.AbstractModal.prototype.constructor.call(this, type, _Assign({}, options));
    }

    ModalIframe.prototype = Object.create(_N2.AbstractModal.prototype);
    ModalIframe.prototype.constructor = ModalIframe;

    ModalIframe.prototype.renderModal = function () {

        this.$modal.addClass('n2_modal--iframe');

        this.$frame = $('<iframe name="' + this.type + '" src="about:blank" style="width: 100%;height:100%;" allowfullscreen></iframe>')
            .appendTo(this.$modalWindow)
    };

    ModalIframe.prototype.setSrc = function (src) {
        this.$frame
            .attr('src', src);
    };


    ModalIframe.prototype.hide = function (e) {
        _N2.AbstractModal.prototype.hide.call(this, e);
        this.$frame
            .attr('src', 'about:blank');
    };

    return ModalIframe;
});_N2.d('ModalLightbox', ['$', 'AbstractModal'], function () {
    var $ = _N2.$;

    /**
     *
     * @memberOf _N2
     *
     * @constructor
     * @augments _N2.AbstractModal
     */
    function ModalLightbox(title, type, options) {

        this.title = title;

        _N2.AbstractModal.prototype.constructor.call(this, type, _Assign({}, options));
    }

    ModalLightbox.prototype = Object.create(_N2.AbstractModal.prototype);
    ModalLightbox.prototype.constructor = ModalLightbox;

    ModalLightbox.prototype.renderModal = function () {

        this.$modal.addClass('n2_modal--lightbox');

        this.$modalTopBar = $('<div class="n2_modal__top_bar"></div>')
            .appendTo(this.$modalWindow);

        this.$title = $('<div class="n2_modal__top_bar_title"></div>')
            .text(this.title)
            .appendTo(this.$modalTopBar);

        this.$modalButtons = $('<div class="n2_modal__top_bar_buttons" data-buttons="0"></div>')
            .appendTo(this.$modalTopBar);

        if (this.options.hasClose) {

            var $closeButton = _N2.UI.modalButton(n2_('Close'), 'grey-dark')
                .on('click', this.hide.bind(this));
            this.addButton($closeButton);
        }

        this.$modalBody = $('<div class="n2_modal__body"></div>')
            .appendTo(this.$modalWindow);
    };

    ModalLightbox.prototype.setTitle = function (title) {
        this.title = title;
        this.$title.text(title);
    };


    return ModalLightbox;
});_N2.d('ModalSimple', ['$', 'AbstractModal'], function () {
    var $ = _N2.$;

    /**
     *
     * @memberOf _N2
     *
     * @constructor
     * @augments _N2.AbstractModal
     */
    function ModalSimple(type, options) {
        _N2.AbstractModal.prototype.constructor.call(this, type, _Assign({
            width: 500
        }, options));
    }

    ModalSimple.prototype = Object.create(_N2.AbstractModal.prototype);
    ModalSimple.prototype.constructor = ModalSimple;

    ModalSimple.prototype.renderModal = function () {

        this.$modal.addClass('n2_modal--simple');

        this.$modalBodyOuter = $('<div class="n2_modal__body_outer"></div>')
            .appendTo(this.$modalWindow);

        if (this.options.hasClose) {
            var $closeWrap = $('<div class="n2_modal__close_wrap"></div>')
                .appendTo(this.$modalBodyOuter);

            $('<div class="n2_modal__close"><i class="ssi_16 ssi_16--remove"></i></div>')
                .on('click', this.hide.bind(this))
                .appendTo($closeWrap);
        }

        this.$modalBody = $('<div class="n2_modal__body"></div>')
            .appendTo(this.$modalBodyOuter);

        this.$modalButtons = $('<div class="n2_modal__buttons" data-buttons="0"></div>')
            .appendTo(this.$modalWindow);
    };


    return ModalSimple;
});_N2.d('PreviewPopup', ['$'], function () {

    /**
     *
     * @memberOf _N2
     *
     * @param url
     * @param id
     * @returns {Window}
     */
    function PreviewPopup(url, id) {
        var params = [
            'height=' + screen.availHeight,
            'width=' + screen.availWidth,
            'toolbar=yes,scrollbars=yes,resizable=yes'
        ].join(',');


        var popup = window.open(url, id, params);
        popup.moveTo(0, 0);
        popup.resizeTo(screen.availWidth, screen.availHeight);

        return popup;
    }

    return PreviewPopup;
});_N2.d('ModalGeneratorRecordViewer', ['$'], function () {
    var $ = _N2.$;


    function displayTable($parent, headings, rows) {

        var $table = $('<div class="n2_generator_records_table"></div>')
            .css('grid-template-columns', '40px repeat(' + (headings.length - 1) + ', auto)')
            .appendTo($parent);

        var i = 0;
        $('<div class="n2_generator_records_table__heading" data-col="1"></div>')
            .text(headings[i])
            .appendTo($table);

        for (i = 1; i < headings.length; i++) {
            $('<div class="n2_generator_records_table__heading"></div>')
                .text(headings[i])
                .appendTo($table);
        }

        for (i = 0; i < rows.length; i++) {
            var j = 0;

            $('<div class="n2_generator_records_table__heading" data-col="1"></div>')
                .text(rows[i][j])
                .appendTo($table);

            for (j = 1; j < rows[i].length; j++) {
                $('<div class="n2_generator_records_table__cell"></div>')
                    .html(rows[i][j])
                    .appendTo($table);
            }
        }

        return $table;
    }

    /**
     *
     * @memberOf _N2
     *
     * @param headings
     * @param rows
     */
    function ModalGeneratorRecordViewer(headings, rows) {
        this.createRecordModal = new _N2.ModalSimple('generator-record-viewer', {
            width: 1000,
            destroyOnHide: true
        });

        this.createRecordModal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--data', 'blue'));
        this.createRecordModal.addContent(_N2.UI.modalHeading(n2_('Records')));

        displayTable(this.createRecordModal.getBody(), headings, rows);

        this.createRecordModal.show();
    }

    return ModalGeneratorRecordViewer;
});_N2.d('ModalHotkey', ['$'], function () {
    var $ = _N2.$;

    function ModalHotkeyManager() {
        var hotkeys = this.getHotkeys();

        this.modal = new _N2.ModalSimple('hotkey', {
            width: 800
        });

        this.modal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--keyboard', 'blue'));
        this.modal.addContent(_N2.UI.modalHeading(n2_('Keyboard shortcuts')));

        var $container = $('<div class="n2_modal__hotkey"></div>');
        this.modal.addContent($container);

        for (var column = 0; column < hotkeys.length; column++) {
            var $col = $('<div class="n2_modal__hotkey_column"></div>').appendTo($container);
            for (var k = 0; k < hotkeys[column].length; k++) {
                var mainTitle = hotkeys[column][k][0];
                var hotkeyArray = hotkeys[column][k][1];

                var $hotkeyGroup = $('<div class="n2_modal__hotkey_group"></div>').appendTo($col);
                $('<div class="n2_modal__hotkey_title">' + mainTitle + '</div>').appendTo($hotkeyGroup);

                for (var i = 0; i < hotkeyArray.length; i++) {
                    var $row = $('<div class="n2_modal__hotkey_row"></div>').appendTo($hotkeyGroup);

                    var text = hotkeyArray[i][0];
                    $('<div class="n2_modal__hotkey_text">' + text + '</div>').appendTo($row);

                    var $keys = $('<div class="n2_modal__hotkey_keys"></div>').appendTo($row);
                    var keys = hotkeyArray[i][1];
                    for (var j = 0; j < keys.length; j++) {
                        $('<div class="n2_modal__hotkey_key">' + keys[j] + '</div>').appendTo($keys);
                    }
                }
            }
        }

        var $button = _N2.UI.modalButton(n2_('Got it'), 'blue');
        this.modal.addButton($button);

        $button.on('click', (function (e) {
            this.modal.hide();
        }).bind(this));
    }

    /**
     *
     * @returns {{}[]}
     */
    ModalHotkeyManager.prototype.getHotkeys = function () {
        var ctrl = 'ctrl',
            col1 = [],
            col2 = [];

        if (this.isMac()) {
            ctrl = 'cmd';
        }

        var general = [n2_('General'), [
            [n2_('Content tab'), ['q']],
            [n2_('Style tab'), ['w']]
        ]];

        col1.push(general);

        general[1].push(
            [n2_('Add Layer'), ['a']],
            [n2_('Layer List'), ['s']]
        );


        var view = [n2_('View'), [
            [n2_('Preview'), [ctrl, '1']],
            [n2_('Desktop'), [ctrl, '2']],
            [n2_('Tablet'), [ctrl, '3']],
            [n2_('Mobile'), [ctrl, '4']]
        ]];

        col1.push(view);

        var adjust = [n2_('Adjust'), [
            [n2_('Move (Absolute)'), [n2_('Arrows')]],
            [n2_('Align (Absolute)'), [n2_('Numeric keys')]]
        ]];

        col1.push(adjust);

        var action = [n2_('Action'), [
            [n2_('Delete'), ['del']],
            [n2_('Duplicate'), [ctrl, 'd']],
            [n2_('Copy'), [ctrl, 'c']],
            [n2_('Paste'), [ctrl, 'v']],
            [n2_('Undo'), [ctrl, 'z']],
            [n2_('Redo'), [ctrl, 'shift', 'z']],
            [n2_('Save'), [ctrl, 's']]
        ]];

        col2.push(action);

        var deviceSpecific = [n2_('Clear device specific settings'), [
            [n2_('Current layer, current device'), ['shift', 'e']],
            [n2_('Current layer, all devices'), ['shift', 'alt', 'e']],
            [n2_('All layers, current device'), ['shift', 'r']],
            [n2_('All layers, all devices'), ['shift', 'alt', 'r']],
        ]];

        col2.push(deviceSpecific);

        return [col1, col2];
    };

    ModalHotkeyManager.prototype.isMac = function () {
        return navigator.platform.toUpperCase().indexOf('MAC') >= 0;
    };

    ModalHotkeyManager.prototype.show = function () {
        this.modal.show();
    };

    var modalManager = false;

    /**
     * @memberOf _N2
     */
    function ModalHotkey() {
    }

    ModalHotkey.show = function () {
        if (!modalManager) {
            modalManager = new ModalHotkeyManager();
        }

        modalManager.show();
    };

    return ModalHotkey;
});_N2.d('ModalSafeHTML', ['$'], function () {
    var $ = _N2.$;

    /**
     *
     * @memberOf _N2
     *
     * @param html
     */
    function ModalSafeHTML(html) {
        var modal = new _N2.ModalSimple('safe-html', {
            destroyOnHide: false,
            width: 440
        });

        modal.addContent(_N2.UI.modalHeading(n2_('Unexpected response')));

        var $html = $(html.replace(/document\.write/g, 'n2Write'));
        window.n2Write = function (buffer) {
            modal.addContent($('<span></span>').html(buffer))
        };
        $html.each(function (i, el) {
            modal.addContent($(el));
        });
        delete window.n2Write;

        modal.show();
    }

    return ModalSafeHTML;
});
_N2.d('ModalUrlTabPro', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param container
     * @param {UrlModalManager} modalManager
     * @constructor
     */
    function ModalUrlTabPro(container, modalManager) {

        $('.n2_free_need_more').eq(0).clone().appendTo(container);

    }

    ModalUrlTabPro.prototype.show = function (originalValue) {

    };

    return ModalUrlTabPro;
});
_N2.d('ModalUrlTabUrl', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param container
     * @param {UrlModalManager} modalManager
     * @constructor
     */
    function ModalUrlTabUrl(container, modalManager) {

        this.modalManager = modalManager;

        this.cache = {};

        var $row1 = _N2.UI.modalFormRow(true)
            .appendTo(container);

        this.$url = _N2.UI.modalFieldText($row1, 'url', n2_('URL'), '');

        var $row2 = _N2.UI.modalFormRow(true)
            .appendTo(container);

        this.$searchKeyword = _N2.UI.modalFieldText($row2, 'search-keyword', n2_('Search keyword'), '')
            .on('keyup', this.search.bind(this))
            .trigger("focus");

        var $row3 = _N2.UI.modalFormRow(true)
            .appendTo(container);

        this.$postSelector = _N2.UI.modalSelectList($row3, 'url-post', n2_('Posts'), (function (value) {
            this.$url.val(value.link);
        }).bind(this), {
            mode: 'insert'
        });
    }

    ModalUrlTabUrl.prototype.show = function (originalValue) {
        this.$url.val(originalValue);
        this.$searchKeyword.val('');

        this.search();
    };

    ModalUrlTabUrl.prototype.search = function () {
        this.searchString = this.$searchKeyword.val();

        this.getAjaxSearchResult(this.searchString)
            .done((function (r) {
                if (this.$searchKeyword.val() === this.searchString) {
                    this.$postSelector.removeOptions();
                    var rows = r.data;
                    for (var i = 0; i < rows.length; i++) {
                        this.$postSelector.addOption(rows[i].title, rows[i].info, rows[i]);
                    }
                }
            }).bind(this));
    };

    ModalUrlTabUrl.prototype.getAjaxSearchResult = function (search) {
        if (this.cache[search] === undefined) {
            this.cache[search] = $.ajax({
                type: "POST",
                url: _N2.AjaxHelper.makeAjaxUrl(this.modalManager.ajaxUrl),
                data: {
                    keyword: search
                },
                dataType: 'json'
            });
        }
        return this.cache[search];
    };

    ModalUrlTabUrl.prototype.getResult = function () {
        return this.$url.val();
    };

    return ModalUrlTabUrl;
});_N2.d('ModalUrl', ['$'], function () {

    /**
     * @memberOf window
     */
    function UrlModalManager(ajaxUrl) {

        this.ajaxUrl = ajaxUrl;
        this.activeTab = '';

        this.modal = new _N2.ModalSimple('url-modal');

        this.modal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--link', 'green')
            .css('margin-bottom', '20px'));


        this.uiTabs = _N2.UI.modalTabs(this.modal.getBody(), 'url-tabs', {
            'url': n2_('URL'),
            'lightbox': n2_('Lightbox'),
            'action': n2_('Action')
        }, this.onTabActiveChange.bind(this));

        this.tabs = {};
        this.tabs['url'] = new _N2.ModalUrlTabUrl(this.uiTabs.tabs.url.$content, this);
        this.tabs['lightbox'] = new _N2.ModalUrlTabPro(this.uiTabs.tabs.lightbox.$content);
        this.tabs['action'] = new _N2.ModalUrlTabPro(this.uiTabs.tabs.action.$content);
    

        var $insertButton = _N2.UI.modalButton(n2_('Insert'), 'green');
        this.modal.addButton($insertButton);

        $insertButton.on('click', (function (e) {
            e.preventDefault();
            this.setCallback(this.tabs[this.activeTab].getResult());
            this.modal.hide();
        }).bind(this));
    }

    UrlModalManager.prototype.onTabActiveChange = function (activeTab) {
        this.activeTab = activeTab;
        this.tabs[activeTab].show(this.originalValue);
    };

    UrlModalManager.prototype.show = function (originalValue, setCallback) {
        this.originalValue = originalValue;
        this.setCallback = setCallback;

        if (originalValue.match(/lightbox\[(.+)\]/)) {
            this.uiTabs.setActiveTab('lightbox');
        } else if (originalValue.match(/ScrollTo|ScrollToAlias|SlideEvent|ToSlide|ToSlideID|\[(.+)\]/)
            || originalValue.match(/PreviousSlide|NextSlide|\[\]/)) {
            this.uiTabs.setActiveTab('action');
        } else {
            this.uiTabs.setActiveTab('url');
        }

        this.modal.show();
    };

    var modalManager = false;

    /**
     * @memberOf _N2
     */
    function ModalUrl() {

    }

    ModalUrl.show = function (originalValue, ajaxUrl, setCallback) {
        if (!modalManager) {
            modalManager = new UrlModalManager(ajaxUrl);
        }

        modalManager.show(originalValue, setCallback);
    };

    return ModalUrl;
});_N2.d('UI', ['$'], function () {
    var $ = _N2.$;

    /**
     *
     * @memberOf _N2
     */
    var UI = {};

    UI.modalButton = function (label, color, size) {
        size = size || 'big';
        return $('<a class="n2_button ' + ('n2_button--' + size) + ' n2_button--' + color + '" href="#"><span class="n2_button__label">' + label + '</span></a>');
    };

    UI.modalIcon = function (icon, color) {
        return $('<div class="n2_modal__icon n2_modal__icon--' + color + '"><i class="' + icon + '"></i></div>');
    };

    UI.modalHeading = function (text) {
        return $('<div class="n2_modal__heading"></div>')
            .text(text);
    };

    UI.modalSubHeading = function (text) {
        return $('<div class="n2_modal__subheading"></div>')
            .text(text);
    };

    UI.modalParagraph = function (text) {
        return $('<div class="n2_modal__paragraph"></div>')
            .text(text);
    };

    UI.modalParagraphHTML = function (text) {
        return $('<div class="n2_modal__paragraph"></div>')
            .html(text);
    };

    UI.modalInput = function (id, type, value, placeholder) {
        return $('<input class="n2_modal__input" type="' + type + '" id="' + id + '" name="' + id + '" value="' + value + '" ' + (placeholder !== undefined ? 'placeholder="' + placeholder + '"' : '') + '>');
    };

    /**
     *
     * @param label string
     * @returns {{$table: jQuery, $content: jQuery}}
     */
    UI.modalFormTable = function (label) {
        var $table = $('<div class="n2_form__table"></div>');

        $('<div class="n2_form__table_label"></div>')
            .append($('<div class="n2_form__table_label_title"></div>').text(label))
            .appendTo($table);

        return {
            $table: $table,
            $content: $('<div class="n2_form__table_preview"></div>')
                .appendTo($table)
        };
    };

    UI.modalFormContainer = function ($parent, name) {

        return $('<div class="n2_modal__form_container" data-field="' + name + '"></div>')
            .appendTo($parent);
    };

    UI.modalHiddenContainer = function ($parent) {

        return $('<div class="n2_modal__container_hidden"></div>')
            .appendTo($parent);
    };

    UI.modalFormRow = function (fullWidth) {

        return $('<div class="n2_modal__form_row"></div>')
            .toggleClass('n2_modal__form_row--fullwidth', fullWidth === true);
    };

    UI.modalFormRowLarge = function (fullWidth) {

        return $('<div class="n2_modal__form_row n2_modal__form_row--large"></div>')
            .toggleClass('n2_modal__form_row--fullwidth', fullWidth === true);
    };

    /**
     *
     * @param {jQuery} $parent
     * @param name
     * @param label
     * @returns {{$element: jQuery, $field: jQuery}}
     */
    UI.modalFieldWrapper = function ($parent, name, label) {

        var $field = $('<div class="n2_field" data-field="' + name + '"></div>')
            .appendTo($parent);

        if (label === false) {
            $field.addClass('n2_field--label-none');
        } else if (label === '') {
            $field.addClass('n2_field--label-placeholder');
        }


        if (label) {
            $('<div class="n2_field__label"><label for="modal-field-' + name + '">' + label + '</label></div>')
                .appendTo($field);
        }

        return {
            $field: $field,
            $element: $('<div class="n2_field__element"></div>')
                .appendTo($field)
        };
    };

    UI.modalFieldText = function ($parent, name, label, value) {

        var field = UI.modalFieldWrapper($parent, name, label);

        var $wrap = $('<div class="n2_field_text"></div>')
                .appendTo(field.$element),
            $input = $('<input type="text" id="modal-field-' + name + '" name="' + name + '" autocomplete="off">')
                .val(value)
                .appendTo($wrap);

        new _N2.FormElementText('modal-field-' + name);

        return $input;
    };

    /**
     *
     * @param $parent
     * @param name
     * @param label
     * @param value
     * @param {=} options
     * @returns {jQuery}
     */
    UI.modalOnOff = function ($parent, name, label, value, options) {
        var field = UI.modalFieldWrapper($parent, name, label);

        var $wrap = $('<div class="n2_field_onoff"><div class="n2_field_onoff__slider"><div class="n2_field_onoff__slider_bullet"></div></div>' +
                '<div class="n2_field_onoff__labels">' +
                '<div class="n2_field_onoff__label n2_field_onoff__label_off">' + n2_('Off') + '</div>' +
                '<div class="n2_field_onoff__label n2_field_onoff__label_on">' + n2_('On') + '</div>' +
                '</div></div>')
                .appendTo(field.$element),
            $input = $('<input id="modal-field-' + name + '" name="' + name + '" value="' + value + '" type="hidden" autocomplete="off">').val(value)
                .appendTo($wrap);


        new _N2.FormElementOnoff('modal-field-' + name, options);

        return $input;
    };

    UI.modalSelect = function ($parent, name, label, options, value, params) {

        params = _Assign({
            isMultiple: false,
            isOrderable: false,
            orderDirection: 'ASC',
            keepFirst: true
        }, params);

        let field = UI.modalFieldWrapper($parent, name, label);

        /*other functions wait Jquery object*/
        let $input = $(_CreateElementIn(field.$element[0], 'input', null, {
            'id': 'modal-field-' + name,
            'name': name,
            'value': value,
            'type': 'hidden',
            'autocomplete': 'off'
        }));

        let container = _CreateElementDivIn(field.$element[0], 'n2_field_select');

        let select = _CreateElementIn(container, 'select', null, {
            'id': 'modal-field-' + name + '_select',
            'name': 'select' + name,
            'autocomplete': 'off'
        });

        if (params.isMultiple) {
            _NodeSetAttributes(select, {
                multiple: 'multiple',
                size: 8
            });
        }

        if (params.isOrderable) {
            options = Object.entries(options);

            if (params.keepFirst) {
                params.keepFirst = options.shift();
            }

            options.sort((a, b) => {
                let ordering = (params.orderDirection === 'ASC') ? a[1] < b[1] : a[1] > b[1];
                if (ordering) {
                    return -1;
                } else {
                    return 1;
                }
            });

            if (params.keepFirst) {
                options.unshift(params.keepFirst);
            }

            options.forEach(option => {
                _CreateElementIn(select, 'option', null, {value: option[0]}).innerText = option[1];
            });
        } else {
            for (var key in options) {
                _CreateElementIn(select, 'option', null, {value: key}).innerText = options[key];
            }
        }

        let fieldlist = new _N2.FormElementList('modal-field-' + name);
        fieldlist.insideChange(value);

        return $input;
    };

    UI.modalNumber = function ($parent, name, label, value, options) {

        options = _Assign({
            unit: false,
            min: -Number.MAX_VALUE,
            max: Number.MAX_VALUE,
            wide: 4,
            sublabel: ''
        }, options);

        var width = 100;
        switch (options.wide) {
            case 2:
                width = 20;
                break;
            case 3:
                width = 26;
                break;
            case 4:
                width = 32;
                break;
            case 5:
                width = 44;
                break;
            case 6:
                width = 60;
                break;
        }

        var field = UI.modalFieldWrapper($parent, name, label);

        var $wrap = $('<div class="n2_field_text n2_field_number"></div>')
            .appendTo(field.$element);

        if (options.sublabel !== '') {
            $('<div class="n2_field_number__unit"></div>').text(options.sublabel)
                .appendTo($wrap);
        }

        var $input = $('<input id="modal-field-' + name + '" name="' + name + '" value="' + value + '" type="text" autocomplete="off">')
            .css('width', width + 'px')
            .appendTo($wrap);

        if (options.unit) {
            $('<div class="n2_field_number__unit"></div>').text(options.unit)
                .appendTo($wrap);
        }

        new _N2.FormElementNumber('modal-field-' + name, options.min, options.max);

        return $input;
    };

    UI.modalTextarea = function ($parent, name, label) {

        var field = UI.modalFieldWrapper($parent, name, label);

        var $wrap = $('<div class="n2_field_textarea"></div>')
            .appendTo(field.$element);

        return $('<textarea name="' + name + '" autocomplete="off"></textarea>')
            .appendTo($wrap);
    };

    UI.modalTable = function ($parent, name, label, data) {
        var field = UI.modalFieldWrapper($parent, name, label);
        field.$field.addClass('n2_field_table');

        var $wrap = $('<div class="n2_field_table__content"></div>')
            .css('grid-template-columns', 'repeat(' + data[0].length + ', auto)')
            .appendTo(field.$element);


        for (var i = 0; i < data.length; i++) {
            for (var j = 0; j < data[i].length; j++) {
                $('<div class="n2_field_table__cell">' + data[i][j] + '</div>')
                    .appendTo($wrap);
            }

        }

        return $wrap;
    };

    UI.modalSelectList = function ($parent, name, label, callback, options) {
        options = _Assign({
            mode: 'select'
        }, options);

        var field = UI.modalFieldWrapper($parent, name, label);

        var $wrap = $('<div class="n2_field_select_list"></div>')
            .appendTo(field.$element);


        return {
            getField: function () {
                return field.$field;
            },
            addOption: function (primary, secondary, value) {
                $('<div class="n2_field_select_list__option"></div>')
                    .on('click', (function (value, e) {
                        if (options.mode === 'select') {
                            field.$field.find('.n2_field_select_list__option--selected')
                                .removeClass('n2_field_select_list__option--selected');
                            $(e.currentTarget).addClass('n2_field_select_list__option--selected');
                        }
                        callback(value);
                    }).bind(this, value))
                    .append($('<div class="n2_field_select_list__option_primary"></div>').text(primary))
                    .append($('<div class="n2_field_select_list__option_secondary"></div>').text(secondary))
                    .appendTo($wrap);
            },
            removeOptions: function () {
                $wrap.html('');
            }
        };
    };

    UI.modalGeneratorVariableList = function ($parent, variableList, label, name, pluralName, callback) {

        $parent.addContent(this.modalHeading(n2_(label)));
        var $row = _N2.UI.modalFormRow(true);
        $parent.addContent($row);

        var field = _N2.UI.modalFieldWrapper($row, name, pluralName);

        var $variablesContainer = $('<div class="n2_generator_variables"></div>')
                .appendTo(field.$element),
            $variables;

        var variableArray = [];
        if (Number.isInteger(variableList)) {
            for (var i = 1; i <= variableList; i++) {
                variableArray[i] = i;
            }
        } else {
            variableArray = variableList;
        }

        for (var j in variableArray) {
            var $button = $('<div class="n2_generator_variables__variable"></div>');
            $button
                .text(j)
                .on('click', (function (variableKey) {
                    $variables.removeClass('n2_generator_variables__variable--selected');
                    this.addClass('n2_generator_variables__variable--selected');
                    callback(variableKey);
                }).bind($button, j))
                .appendTo($variablesContainer);
        }

        $variables = $variablesContainer.find('.n2_generator_variables__variable');
        $variables.eq(0).addClass('n2_generator_variables__variable--selected');
    };

    UI.modalTabs = function ($parent, name, options, onchange) {
        var $element = $('<div class="n2_modal_tab"></div>')
                .appendTo($parent),
            $tabsContainer = $('<div class="n2_modal_tab__tabs"></div>').appendTo($element),
            $tabContentsContainer = $('<div class="n2_modal_tab__tabs_contents"></div>').appendTo($element),
            active = false,
            tabs = {};

        function setActiveTab(value) {
            if (active && active !== value) {
                tabs[active].$button.removeClass('n2_modal_tab__tabs_button--active');
                tabs[active].$content.removeClass('n2_modal_tab__tabs_content--active');
            }

            active = value;
            tabs[active].$button.addClass('n2_modal_tab__tabs_button--active');
            tabs[active].$content.addClass('n2_modal_tab__tabs_content--active');

            onchange(active);
        }

        for (var k in options) {

            tabs[k] = {
                $button: $('<div class="n2_modal_tab__tabs_button" data-tab="' + k + '">' + options[k] + '</div>')
                    .on('click', setActiveTab.bind(this, k))
                    .appendTo($tabsContainer),
                $content: $('<div class="n2_modal_tab__tabs_content" data-tab="' + k + '"></div>')
                    .appendTo($tabContentsContainer)
            };
        }

        return {
            tabs: tabs,
            setActiveTab: setActiveTab
        };
    };

    UI.modalFieldSelectIcon = function ($parent, name, label, options, value, parameters) {

        var field = UI.modalFieldWrapper($parent, name, label);

        var $wrap = $('<div class="n2_field_select_icon"></div>').appendTo(field.$element);

        for (var k in options) {
            $('<div class="n2_field_select_icon__option"></div>')
                .attr('data-value', k)
                .toggleClass('n2_field_select_icon__option--selected', k === value)
                .append('<div class="n2_field_select_icon__option_icon"><i class="' + options[k].icon + '"></i></div>')
                .append('<div class="n2_field_select_icon__option_label">' + options[k].label + '</div>')
                .append('<div class="n2_field_select_icon__selected_marker"><i class="ssi_16 ssi_16--check"></i></div>')
                .appendTo($wrap);
        }

        var $input = $('<input id="modal-field-' + name + '" name="' + name + '" value="' + value + '" type="hidden">')
            .appendTo(field.$element);

        new _N2.FormElementSelectIcon('modal-field-' + name, parameters);

        return $input;
    };

    UI.modalNotice = function ($parent, text, type) {

        var field = UI.modalFieldWrapper($parent, 'notice', n2_('Notice'));

        return $('<div class="n2_field_message n2_field_message--' + type + '"></div>')
            .html(text)
            .appendTo(field.$element);
    };

    return UI;

});_N2.d('AutocompleteSimple', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param values
     * @constructor
     */
    function AutocompleteSimple(id, values) {

        var $el = $('#' + id);

        new _N2.UIAutocomplete($el, {
            positionTo: '.n2_autocomplete_position_to',
            y: -2,
            appendTo: function () {
                return $el.closest('.n2_container_scrollable, body')
            },
            source: function () {
                return values;
            },
            select: function (event, ui) {
                $(this).val(ui.value).trigger('change');
            }
        });
    }

    return AutocompleteSimple;
});const ColorConvert = {
    /**
     *
     * @param {Number} dec
     * @return {string}
     * @constructor
     */
    DecToHex: function (dec) {
        var hex = Math.round(dec).toString(16);
        return hex.length === 1 ? '0' + hex : hex;
    },
    HexToDec: function (hex) {
        return parseInt(hex, 16);
    },

    HSVtoRGB: function (h, s, v) {
        var r, g, b, i, f, p, q, t;
        i = Math.floor(h * 6);
        f = h * 6 - i;
        p = v * (1 - s);
        q = v * (1 - f * s);
        t = v * (1 - (1 - f) * s);
        switch (i % 6) {
            case 0:
                r = v;
                g = t;
                b = p;
                break;
            case 1:
                r = q;
                g = v;
                b = p;
                break;
            case 2:
                r = p;
                g = v;
                b = t;
                break;
            case 3:
                r = p;
                g = q;
                b = v;
                break;
            case 4:
                r = t;
                g = p;
                b = v;
                break;
            case 5:
                r = v;
                g = p;
                b = q;
                break;
        }

        return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
    },

    HSVtoHex6: function (h, s, v) {
        var rgb = ColorConvert.HSVtoRGB(h, s, v);

        return ColorConvert.DecToHex(rgb[0]) + ColorConvert.DecToHex(rgb[1]) + ColorConvert.DecToHex(rgb[2]);
    },

    RGBtoHSV: function (r, g, b) {
        var v = Math.max(r, g, b),
            c = v - Math.min(r, g, b),
            h = c && ((v == r) ? (g - b) / c : ((v == g) ? 2 + (b - r) / c : 4 + (r - g) / c));
        return [(60 * (h < 0 ? h + 6 : h)) / 360, (v && c / v), v / 255];
    },

    Hex6toHSV: function (hex6) {

        return ColorConvert.RGBtoHSV(ColorConvert.HexToDec(hex6.substr(0, 2)), ColorConvert.HexToDec(hex6.substr(2, 2)), ColorConvert.HexToDec(hex6.substr(4, 2)));
    },

    Hex8toRGBA: function (hex8) {
        var alpha = 1;
        if (hex8.length === 8) {
            alpha = ColorConvert.HexToDec(hex8.substr(6, 2)) / 255
        } else if (hex8.length === 3) {
            hex8 = hex8[0] + hex8[0] + hex8[1] + hex8[1] + hex8[2] + hex8[2];
        }

        return [ColorConvert.HexToDec(hex8.substr(0, 2)), ColorConvert.HexToDec(hex8.substr(2, 2)), ColorConvert.HexToDec(hex8.substr(4, 2)), alpha];
    },

    Hex8toCssRGBA: function (hex8) {
        var rgba = ColorConvert.Hex8toRGBA(hex8);
        for (var i = 0; i < rgba.length; i++) {
            if (isNaN(rgba[i])) {
                rgba[i] = 0;
                rgba[3] = 0;
            }
        }

        return 'RGBA(' + rgba.join(',') + ')';
    }
};
_N2.d('ColorPicker', function () {

    class ColorPicker {
        constructor() {
            this.___currentHue = 0;
            this.___currentSaturation = 0;
            this.___currentValue = 0;
            this.___currentOpacity = 0;

            this.___listeners = [];

            this.___palette = new Palette(this);
            this.___canvas = new ColorPickerCanvas(this);
            this.___hue = new ColorPickerHue(this);
            this.___opacity = new ColorPickerOpacity(this);
        }

        ___initUI() {
            if (!this.___colorPicker) {
                this.___colorPicker = _CreateElementDiv();
                _NodeAddClass(this.___colorPicker, 'n2_color_picker');

                this.___palette.initUI(this.___colorPicker);

                this.___canvas.initUI(this.___colorPicker);
                this.___hue.initUI(this.___colorPicker);
                this.___opacity.initUI(this.___colorPicker);

                document.body.appendChild(this.___colorPicker);
            }
        }

        get area() {
            return this.___colorPicker;
        }

        get rgb() {
            return ColorConvert.HSVtoRGB(this.___currentHue, this.___currentSaturation, this.___currentValue);
        }

        get rgba() {
            var rgba = ColorConvert.HSVtoRGB(this.___currentHue, this.___currentSaturation, this.___currentValue);
            rgba.push(this.opacity);
            return rgba;
        }

        get hex() {
            return ColorConvert.HSVtoHex6(this.___currentHue, this.___currentSaturation, this.___currentValue);
        }

        get hexa() {

            return ColorConvert.HSVtoHex6(this.___currentHue, this.___currentSaturation, this.___currentValue) + ColorConvert.DecToHex(Math.round(this.opacity * 255));
        }

        get opacity() {
            return this.___currentOpacity;
        }

        get cssRGB() {
            return '#' + this.hex;
        }

        get cssRGBA() {
            return 'RGBA(' + this.rgba.join(',') + ')';
        }

        get currentHue() {
            return this.___currentHue;
        }

        get currentSaturation() {
            return this.___currentSaturation;
        }

        get currentValue() {
            return this.___currentValue;
        }

        get currentOpacity() {
            return this.___currentOpacity;
        }

        updateUI() {

            this.___canvas.updateHue(this.___currentHue);
            this.___opacity.update();
        }

        setHue(hue) {

            this.___currentHue = hue;

            this.updateUI();

            this.updateTargetValue();
        }

        setSaturationValue(saturation, value) {

            this.___currentSaturation = saturation;
            this.___currentValue = value;

            this.updateUI();

            this.updateTargetValue();
        }

        setOpacity(opacity) {

            this.___currentOpacity = opacity;

            this.updateTargetValue();
        }

        show(target, value, isAlpha, notifySaveCallback, notifyChangeCallback) {

            this.___target = target;

            this.___originalValue = value;
            this.___isChanged = false;

            if ((value.length !== 6 && value.length !== 8) || (value.length > 0 && value.charAt(0) === '{')) {
                value = 'ffffff';
            }

            this.___hasOpacity = isAlpha;

            this.___notifySaveCallback = notifySaveCallback;
            this.___notifyChangeCallback = notifyChangeCallback || function () {
            };

            this.___initUI();

            if (this.___hasOpacity) {
                this.___size = {
                    width: 390,
                    height: 184
                };
            } else {
                this.___size = {
                    width: 360,
                    height: 156
                };
            }

            this.___positionToTarget();

            _NodeSetStyle(this.___colorPicker, 'display', '');

            this.___colorPicker.classList.toggle('n2_color_picker--has-opacity', this.___hasOpacity);

            if (value.length === 8) {
                this.___currentOpacity = ColorConvert.HexToDec(value.substr(6, 2)) / 255;
                value = value.substr(0, 6);
            } else {
                this.___currentOpacity = 1;
            }

            if (value.length === 6) {
                var hsl = ColorConvert.Hex6toHSV(value);
                this.___currentHue = hsl[0];
                this.___currentSaturation = hsl[1];
                this.___currentValue = hsl[2];
            }
            this.___hue.sync();
            this.___canvas.sync();
            this.___opacity.sync();

            this.updateUI();

            this.___listeners.push(_addEventListenerWithRemover(document, 'scroll', this.detach.bind(this)));
            this.___listeners.push(_addEventListenerWithRemover(document.body, 'pointerdown', (function (e) {
                if (this.___colorPicker !== e.target && !this.___colorPicker.contains(e.target)) {

                    this.detach();
                }
            }).bind(this), {
                capture: true
            }));
        }

        detach() {

            var value = this.___hasOpacity ? this.hexa : this.hex;

            if (this.___isChanged && this.___originalValue !== value) {
                this.___notifySaveCallback(value);

                this.___palette.addHistoryColor(value);
            }

            _NodeSetStyle(this.___colorPicker, 'display', 'none');


            _removeEventListeners(this.___listeners);
        }

        ___positionToTarget() {

            var rect = this.___target.getBoundingClientRect(),
                top = rect.top + rect.height;

            if (top > window.innerHeight - this.___size.height - 5) {
                top = rect.top - this.___size.height - 5;
            }

            _NodeSetStyles(this.___colorPicker, {
                left: Math.min(rect.left, window.innerWidth - this.___size.width - 5) + 'px',
                top: top + 'px'
            });
        }

        updateTargetValue() {
            this.___isChanged = true;
            this.___notifyChangeCallback(this.___hasOpacity ? this.hexa : this.hex);
        }

        setColor(hex8) {
            var hex6 = hex8;
            if (hex8.length === 8) {
                this.___currentOpacity = ColorConvert.HexToDec(hex8.substr(6, 2)) / 255;
                hex6 = hex6.substr(0, 6);
            } else {
                this.___currentOpacity = 1;
            }

            if (hex6.length === 6) {
                var hsl = ColorConvert.Hex6toHSV(hex6);
                this.___currentHue = hsl[0];
                this.___currentSaturation = hsl[1];
                this.___currentValue = hsl[2];
            }

            this.___hue.sync();
            this.___canvas.sync();
            this.___opacity.sync();

            this.updateUI();

            this.updateTargetValue();

            this.detach();
        }
    }

    class Palette {
        constructor(picker) {

            this.___picker = picker;

            this.___colors = [
                '000000', '81898d', 'ced3d5', 'ffffff',
                '55aa39', '5cba3c', '27ae60', '2ecc71',
                '357cbd', '4594e1', '01add3', '00c1c4',
                'bb4a28', 'd85935', 'e79d19', 'ecc31f',
                '8757b2', '9e74c2', 'e264af', 'ec87c0',
                '000000CC', '00000080', 'FFFFFFCC', 'FFFFFF80'
            ];

            this.___colorsHistory = [];

            var stored = localStorage.getItem('n2-colors');
            if (stored) {
                this.___colorsHistory = JSON.parse(stored);
                this.___colorsHistory.splice(6);
            }
        }

        initUI(area) {

            var palette = _CreateElementDiv();
            _NodeAddClass(palette, 'n2_color_picker__palette');
            area.appendChild(palette);

            var colors = [
                '000000', '81898d', 'ced3d5', 'ffffff',
                '55aa39', '5cba3c', '27ae60', '2ecc71',
                '357cbd', '4594e1', '01add3', '00c1c4',
                'bb4a28', 'd85935', 'e79d19', 'ecc31f',
                '8757b2', '9e74c2', 'e264af', 'ec87c0',
                '000000CC', '00000080', 'FFFFFFCC', 'FFFFFF80'
            ];

            for (var i = 0; i < colors.length; i++) {
                var paletteItem = _CreateElementDiv();
                _NodeAddClass(paletteItem, 'n2_color_picker__palette_item');
                _NodeAddClass(paletteItem, 'n2_checker_box');
                if (colors[i].length === 8) {
                    _NodeAddClass(paletteItem, 'n2_color_picker__palette_item_alpha');
                }
                _NodeSetStyle(paletteItem, '--palette-item-color', ColorConvert.Hex8toCssRGBA(colors[i]));
                palette.appendChild(paletteItem);

                var paletteItemColor = _CreateElementDiv();
                _NodeAddClass(paletteItemColor, 'n2_color_picker__palette_item_color');
                paletteItem.appendChild(paletteItemColor);

                _addEventListener(paletteItem, 'click', this.___picker.setColor.bind(this.___picker, colors[i]))
            }

            this.___initUIHistory(area);
        }

        ___initUIHistory(area) {

            this.___historyItems = [];

            var paletteHistory = _CreateElementDiv();
            _NodeAddClass(paletteHistory, 'n2_color_picker__palette_history');
            area.appendChild(paletteHistory);

            for (var i = 0; i < 6; i++) {
                var paletteItem = _CreateElementDiv();
                _NodeAddClass(paletteItem, 'n2_color_picker__palette_item');
                _NodeAddClass(paletteItem, 'n2_checker_box');
                _NodeSetData(paletteItem, 'color', 'ffffffff');
                _NodeSetStyle(paletteItem, '--palette-item-color', '#fff');
                paletteHistory.appendChild(paletteItem);

                var paletteItemColor = _CreateElementDiv();
                _NodeAddClass(paletteItemColor, 'n2_color_picker__palette_item_color');
                paletteItem.appendChild(paletteItemColor);

                this.___historyItems.push(paletteItem);

                _addEventListener(paletteItem, 'click', (function (e) {
                    this.___picker.setColor(_NodeGetData(e.currentTarget, 'color'));
                }).bind(this));
            }

            this.___updateHistoryItems();
        }

        ___updateHistoryItems() {
            for (var i = 0; i < this.___colorsHistory.length; i++) {
                _NodeSetData(this.___historyItems[i], 'color', this.___colorsHistory[i]);
                _NodeSetStyle(this.___historyItems[i], '--palette-item-color', ColorConvert.Hex8toCssRGBA(this.___colorsHistory[i]));

            }
        }

        addHistoryColor(hex8) {
            if (this.___colors.indexOf(hex8) === -1 && this.___colorsHistory.indexOf(hex8) === -1) {
                this.___colorsHistory.unshift(hex8);
                this.___colorsHistory.splice(6);

                try {
                    localStorage.setItem('n2-colors', JSON.stringify(this.___colorsHistory));
                } catch (e) {

                }

                this.___updateHistoryItems();
            }
        }
    }

    class ColorPickerCanvas {
        constructor(picker) {
            this.___picker = picker;
            this.___listeners = [];
        }

        initUI(area) {

            this.___canvasElement = _CreateElementDiv();
            _NodeAddClass(this.___canvasElement, 'n2_color_picker__picker_canvas');
            area.appendChild(this.___canvasElement);

            this.___dotElement = _CreateElementDiv();
            _NodeAddClass(this.___dotElement, 'n2_color_picker__picker_canvas_dot');
            this.___canvasElement.appendChild(this.___dotElement);

            this.___canvasElement.addEventListener('pointerdown', this.___onPointerDown.bind(this));
        }

        ___onPointerDown(e) {

            this.___listeners.push(_addEventListenerWithRemover(this.___picker.area, 'pointermove', this.___onPointerMove.bind(this)));
            this.___listeners.push(_addEventListenerWithRemover(this.___picker.area, 'pointerup', this.___onPointerUp.bind(this)));
            this.___listeners.push(_addEventListenerWithRemover(this.___picker.area, 'pointerleave', this.___onPointerLeave.bind(this)));

            this.___updateByEvent(e);
        }

        ___onPointerMove(e) {

            this.___updateByEvent(e);
        }

        ___onPointerUp(e) {

            this.___updateByEvent(e);
            this.___endInteraction();
        }

        ___onPointerLeave(e) {

            this.___endInteraction();
        }

        ___endInteraction() {
            _removeEventListeners(this.___listeners);
        }

        ___updateByEvent(e) {

            var rect = this.___canvasElement.getBoundingClientRect(),
                x = Math.max(0, Math.min(rect.width, e.clientX - rect.left)),
                y = Math.max(0, Math.min(rect.height, e.clientY - rect.top));

            this.___dotElement.style.setProperty('transform', 'translate(' + x + 'px, ' + y + 'px)');

            this.___picker.setSaturationValue(x / rect.width, 1 - y / rect.height);
        }

        updateHue(hue) {
            this.___canvasElement.style.setProperty('background-color', '#' + ColorConvert.HSVtoHex6(hue, 1, 1));
        }

        sync() {
            var saturation = this.___picker.currentSaturation,
                value = this.___picker.currentValue,
                rect = this.___canvasElement.getBoundingClientRect();

            this.___dotElement.style.setProperty('transform', 'translate(' + rect.width * saturation + 'px, ' + rect.height * (1 - value) + 'px)');
        }
    }

    class ColorPickerHue {
        constructor(picker) {
            this.___picker = picker;
            this.___listeners = [];
        }

        initUI(area) {

            this.___hueElement = _CreateElementDiv();
            _NodeAddClass(this.___hueElement, 'n2_color_picker__picker_hue');
            area.appendChild(this.___hueElement);

            this.___dotElement = _CreateElementDiv();
            _NodeAddClass(this.___dotElement, 'n2_color_picker__picker_hue_dot');
            this.___hueElement.appendChild(this.___dotElement);

            this.___hueElement.addEventListener('pointerdown', this.___onPointerDown.bind(this));
        }

        ___onPointerDown(e) {

            this.___listeners.push(_addEventListenerWithRemover(this.___picker.area, 'pointermove', this.___onPointerMove.bind(this)));
            this.___listeners.push(_addEventListenerWithRemover(this.___picker.area, 'pointerup', this.___onPointerUp.bind(this)));
            this.___listeners.push(_addEventListenerWithRemover(this.___picker.area, 'pointerleave', this.___onPointerLeave.bind(this)));

            this.___updateByEvent(e);
        }

        ___onPointerMove(e) {

            this.___updateByEvent(e);
        }

        ___onPointerUp(e) {

            this.___updateByEvent(e);
            this.___endInteraction();
        }

        ___onPointerLeave(e) {

            this.___endInteraction();
        }

        ___endInteraction() {
            _removeEventListeners(this.___listeners);
        }

        ___updateByEvent(e) {

            var rect = this.___hueElement.getBoundingClientRect(),
                y = Math.max(0, Math.min(rect.height, e.clientY - rect.top));

            this.___dotElement.style.setProperty('transform', 'translateY(' + y + 'px)');

            this.___picker.setHue(y / rect.height);
        }

        sync() {

            var hue = this.___picker.currentHue,
                rect = this.___hueElement.getBoundingClientRect();

            this.___dotElement.style.setProperty('transform', 'translateY(' + rect.height * hue + 'px)');
        }
    }

    class ColorPickerOpacity {
        constructor(picker) {
            this.___picker = picker;
            this.___listeners = [];
        }

        initUI(area) {

            var opacityElement = _CreateElementDiv();
            _NodeAddClass(opacityElement, 'n2_color_picker__picker_opacity');
            _NodeAddClass(opacityElement, 'n2_checker_box');
            area.appendChild(opacityElement);

            this.___opacityElement = _CreateElementDiv();
            _NodeAddClass(this.___opacityElement, 'n2_color_picker__picker_opacity_inner');
            opacityElement.appendChild(this.___opacityElement);

            this.___dotElement = _CreateElementDiv();
            _NodeAddClass(this.___dotElement, 'n2_color_picker__picker_opacity_dot');
            this.___opacityElement.appendChild(this.___dotElement);

            this.___opacityElement.addEventListener('pointerdown', this.___onPointerDown.bind(this));
        }

        ___onPointerDown(e) {

            this.___listeners.push(_addEventListenerWithRemover(this.___picker.area, 'pointermove', this.___onPointerMove.bind(this)));
            this.___listeners.push(_addEventListenerWithRemover(this.___picker.area, 'pointerup', this.___onPointerUp.bind(this)));
            this.___listeners.push(_addEventListenerWithRemover(this.___picker.area, 'pointerleave', this.___onPointerLeave.bind(this)));

            this.___updateByEvent(e);
        }

        ___onPointerMove(e) {

            this.___updateByEvent(e);
        }

        ___onPointerUp(e) {

            this.___updateByEvent(e);
            this.___endInteraction();
        }

        ___onPointerLeave(e) {

            this.___endInteraction();
        }

        ___endInteraction() {
            _removeEventListeners(this.___listeners);
        }

        ___updateByEvent(e) {

            var rect = this.___opacityElement.getBoundingClientRect(),
                y = Math.max(0, Math.min(rect.height, e.clientY - rect.top));

            this.___dotElement.style.setProperty('transform', 'translateY(' + y + 'px)');

            this.___picker.setOpacity(1 - y / rect.height);
        }

        update() {
            var rgb = this.___picker.rgb.join(',');
            this.___opacityElement.style.setProperty('background-image', 'linear-gradient(180deg, RGBA(' + rgb + ', 1) 0%, RGBA(' + rgb + ', 0) 100%)');
        }

        sync() {

            var opacity = this.___picker.currentOpacity,
                rect = this.___opacityElement.getBoundingClientRect();

            this.___dotElement.style.setProperty('transform', 'translateY(' + rect.height * (1 - opacity) + 'px)');
        }
    }

    return new ColorPicker();
});_N2.d('Notification', ['$'], function () {
    var $ = _N2.$;

    /**
     * @alias _N2.notification
     * @constructor
     */
    function Notification() {

        this.deferred = $.Deferred();

        _N2.r('documentReady', this.ready.bind(this));
    }

    Notification.prototype.ready = function () {

        this.$container = $('<div class="n2_notification_center n2_admin_ui"></div>')
            .appendTo('body');

        this.deferred.resolve();
    };

    Notification.prototype.error = function (message, parameters) {

        parameters = _Assign({
            wide: false,
            heading: n2_('Oops, Something Went Wrong')
        }, parameters);

        var modal = new _N2.ModalSimple('notification error', {
            width: parameters.wide ? 1000 : 440
        });

        modal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--error', 'red'));
        modal.addContent(_N2.UI.modalHeading(parameters.heading));
        modal.addContent(_N2.UI.modalParagraphHTML(message));

        var $createButton = _N2.UI.modalButton(n2_('Got it'), 'red');
        modal.addButton($createButton);

        $createButton.on('click', function (e) {
            e.preventDefault();

            modal.hide(e);
        });

        modal.show();
    };

    Notification.prototype.success = function (message) {
        this.message('success', n2_('Success'), message, {
            timeout: 3
        });
    };

    Notification.prototype.notice = function (message) {
        this.message('notice', n2_('Notice'), message);
    };

    Notification.prototype.message = function (type, label, message, parameters) {

        parameters = _Assign({
            timeout: 0
        }, parameters);

        var $message = $('<div class="n2_notification_center__message n2_notification_center__message--' + type + '"></div>')
            .scrollTop(0)
            .prependTo(this.$container);

        $('<div class="n2_notification_center__message_icon"><i class="ssi_24"></i></div>')
            .appendTo($message);

        var $messageContent = $('<div class="n2_notification_center__message_content"></div>')
            .appendTo($message);

        $('<div class="n2_notification_center__message_content_label">' + label + '</div>')
            .appendTo($messageContent);

        $('<div class="n2_notification_center__message_content_description">' + message + '</div>')
            .appendTo($messageContent);

        if (parameters.timeout) {
            $message.one('mouseenter', function () {
                $message.remove();
            });
            setTimeout(function () {
                $message.remove();
            }, parameters.timeout * 1000);
        } else {

            $('<div class="n2_notification_center__message_action"></div>')
                .text(n2_('Got it'))
                .on('click', function () {
                    $message.remove();
                })
                .appendTo($message);
        }

        setTimeout((function ($message) {
            $message.addClass('n2_notification_center__message--animate');
        }).bind(this, $message), 100);
    };

    return new Notification();
});_N2.d('Form', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @abstract
     * @constructor
     */
    function Form() {

        this.skipChangeConfirm = false;

        this.onChangeCallback = this.onChange.bind(this);
    }

    Form.prototype.setID = function (id) {
        this.$form = $(id)
            .data('form', this);
    };

    Form.prototype.onReady = function () {

        this.window = $('#n2-admin')[0];

        this.initButtons();

        this.resetChangeTracker();

        this.registerBeforeUnload();

        document.addEventListener('keydown', this.onKeyDown.bind(this), {
            capture: true
        });
    };

    Form.prototype.initButtons = function () {

    };

    Form.prototype.resetChangeTracker = function () {
        this.hasChange = false;

        this.window.addEventListener('nextendChange', this.onChangeCallback, {
            passive: true,
            capture: true
        });

        this.window.addEventListener('change', this.onChangeCallback, {
            passive: true,
            capture: true
        });

        this.$saveButton.addClass('n2_button--inactive');
    };

    Form.prototype.onChange = function () {
        this.hasChange = true;

        this.$saveButton.removeClass('n2_button--inactive');

        this.window.removeEventListener('nextendChange', this.onChangeCallback, {
            passive: true,
            capture: true
        });

        this.window.removeEventListener('change', this.onChangeCallback, {
            passive: true,
            capture: true
        });
    };

    Form.prototype.onKeyDown = function (e) {
        if (!e.defaultPrevented) {
            var handled = false;
            if (e.ctrlKey || e.metaKey) {
                if (e.code === 'KeyS') { // ctrl + s
                    this.actionSave();
                    handled = true;
                }
            } else if (e.code === 'Escape') { // esc
                if (e.target.tagName === 'INPUT' || e.target.tagName === 'textarea') {
                    e.target.blur();
                    handled = true;
                }
            }

            if (handled) {
                e.preventDefault();
            }
        }
    };

    Form.prototype.isChanged = function () {
        return this.hasChange;
    };

    Form.prototype.registerBeforeUnload = function () {
        window.addEventListener('beforeunload', this.actionUnload.bind(this));
    };

    Form.prototype.actionUnload = function (e) {

        if (!this.skipChangeConfirm && this.isChanged()) {
            var confirmationMessage = n2_('The changes you made will be lost if you navigate away from this page.');
            e.returnValue = confirmationMessage;
            return confirmationMessage;
        }
    };

    Form.prototype.actionSave = function () {

    };

    Form.prototype.afterSave = function () {
        this.resetChangeTracker();
    };

    return Form;
});_N2.d('FormElement', function () {

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function FormElement() {
        this.connectedField = null;
        this.element.data('field', this);
    }

    FormElement.prototype.triggerOutsideChange = function () {
        this.element[0].dispatchEvent(new CustomEvent('outsideChange', {
            cancelable: false,
            bubbles: true,
            detail: {
                field: this
            }
        }));
        this.element[0].dispatchEvent(new CustomEvent('nextendChange', {
            cancelable: false,
            bubbles: true,
            detail: {
                field: this
            }
        }));
    };

    FormElement.prototype.triggerInsideChange = function () {
        this.element[0].dispatchEvent(new CustomEvent('insideChange', {
            cancelable: false,
            bubbles: true,
            detail: {
                field: this
            }
        }));
        this.element[0].dispatchEvent(new CustomEvent('nextendChange', {
            cancelable: false,
            bubbles: true,
            detail: {
                field: this
            }
        }));
    };

    FormElement.prototype.focus = function (shouldOpen) {
        if (this.connectedField) {
            this.connectedField.focus(shouldOpen);
        }
    };

    return FormElement;
});_N2.d('FormElementAutocomplete', ['$', 'FormElementText'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param tags
     * @constructor
     * @augments {_N2.FormElementText}
     */
    function FormElementAutocomplete(id, tags) {

        this.tags = tags;

        _N2.FormElementText.prototype.constructor.call(this, id);

        this.parent.find('.n2_field_text__clear')
            .on('click', this.clear.bind(this));

        new _N2.UIAutocomplete(this.element, {
            positionTo: '.n2_field_autocomplete',
            y: -2,
            appendTo: (function () {
                return this.element.closest('.n2_container_scrollable, body')
            }).bind(this),
            source: (function () {
                return this.tags;
            }).bind(this),
            select: function (event, ui) {
                var terms = this.value.split(/,/);
                terms.pop();
                terms.push(ui.value);
                terms.push("");
                this.value = terms.join(",");
                $(this).trigger('change');
            }
        });
    }

    FormElementAutocomplete.prototype = Object.create(_N2.FormElementText.prototype);
    FormElementAutocomplete.prototype.constructor = FormElementAutocomplete;

    FormElementAutocomplete.prototype.clear = function (e) {
        e.preventDefault();
        this.element.val('');

        this.change();
    };

    return FormElementAutocomplete;

});_N2.d('BasicCSSFont', ['$', 'BasicCSSSkeleton'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments {_N2.BasicCSSSkeleton}
     */
    function BasicCSSFont() {
        this._singular = 'font';
        this._prular = 'fonts';
        _N2.BasicCSSSkeleton.prototype.constructor.apply(this, arguments);

        this.form = {
            afont: $('#layer-font-family'),
            color: $('#layer-font-color'),
            size: $('#layer-font-size'),
            weight: $('#layer-font-weight'),
            lineheight: $('#layer-font-lineheight'),
            align: $('#layer-font-textalign'),
            underline: $('#layer-font-decoration'),
            italic: $('#layer-font-decoration'),
            letterspacing: $('#layer-font-letterspacing'),
            wordspacing: $('#layer-font-wordspacing'),
            texttransform: $('#layer-font-texttransform'),
            tshadow: $('#layer-font-tshadow'),
            extra: $('#layer-font-extracss')
        };

        this.loaded();
    }

    BasicCSSFont.prototype = Object.create(_N2.BasicCSSSkeleton.prototype);
    BasicCSSFont.prototype.constructor = BasicCSSFont;

    BasicCSSFont.prototype.activateVisual = function (index) {
        this.activeVisual = index;

        this.setValue(_N2.CSSRendererFont.transformData(this.visuals[index].value, this.visuals[index].mode));
        this.setStates(_N2.CSSRendererFont.rendererModes[this.visuals[index].mode].tabs);

        _N2.BasicCSSSkeleton.prototype.activateVisual.call(this, index);
    };

    BasicCSSFont.prototype.setValue = function (value) {

        this.value = _N2.CSSRendererFont.fixBold(value);
    };

    BasicCSSFont.prototype._transformsize = function (value) {
        return value.split('||').join('|*|');
    };

    BasicCSSFont.prototype._setsize = function (tab, value) {
        tab.size = value.replace('|*|', '||');
    };

    BasicCSSFont.prototype._transformweight = function (value) {
        return parseInt(value);
    };

    BasicCSSFont.prototype._setweight = function (tab, value) {
        tab.weight = parseInt(value);
    };

    BasicCSSFont.prototype._transformunderline = function (value) {
        return [
            this.value[this.activeState].italic == 1 ? 'italic' : '',
            value == 1 ? 'underline' : ''
        ].join('||');
    };

    BasicCSSFont.prototype._setunderline = function (tab, value) {
        var values = value.split('||');
        tab.underline = (values[1] === 'underline' ? 1 : 0);
    };

    BasicCSSFont.prototype._transformitalic = function (value) {
        return [
            value == 1 ? 'italic' : '',
            this.value[this.activeState].underline == 1 ? 'underline' : ''
        ].join('||');
    };

    BasicCSSFont.prototype._setitalic = function (tab, value) {
        var values = value.split('||');
        tab.italic = (values[0] === 'italic' ? 1 : 0);
    };

    return BasicCSSFont;
});_N2.d('BasicCSSSkeleton', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param manager
     * @constructor
     * @abstract
     */
    function BasicCSSSkeleton(manager) {
        this.hasVisuals = false;
        this.isInsideChange = false;
        this.isReload = false;
        /**
         * @type {_N2.BasicCSS}
         */
        this.manager = manager;

        this.$container = manager.$container.find('.n2_fields_layer_window[data-field="fieldset-layer-window-basiccss-' + this._singular + '"]');

        this.$relatedContainers = manager.$container.find('.n2_fields_layer_window[data-parent-design="fieldset-layer-window-basiccss-' + this._singular + '"]');

        this.$containers = this.$container.add(this.$relatedContainers).attr('data-state', '0');

        /**
         * @type {_N2.FormElementList}
         */
        this.elementField = $('#layerbasiccss-' + this._singular + '-element').data('field');
        this.elementField.element.on('nextendChange', (function () {
            this.activateVisual(this.elementField.element.val());
            this.activateState(0);
        }).bind(this));

        /**
         * @type {_N2.FormElementList}
         */
        this.stateField = $('#layerbasiccss-' + this._singular + '-state').data('field');
        this.stateField.element.on('nextendChange', (function () {
            this.activateState(this.stateField.element.val());
        }).bind(this));

        /**
         * @type {_N2.FormElementList[]}
         */
        this.relatedElementFields = [];

        /**
         * @type {_N2.FormElementList[]}
         */
        this.relatedStateFields = [];

        this.$relatedContainers.each((function (i, el) {
            var $el = $(el),
                /**
                 * @type {_N2.FormElementList}
                 */
                elementField = $el.find('#layerbasiccss-' + $el.data('singular') + '-element').data('field'),
                /**
                 * @type {_N2.FormElementList}
                 */
                stateField = $el.find('#layerbasiccss-' + $el.data('singular') + '-state').data('field');

            elementField.element.on('outsideChange', (function (element) {
                this.elementField.insideChange(element.val());
            }).bind(this, elementField.element));

            stateField.element.on('outsideChange', (function (element) {
                this.stateField.insideChange(element.val());
            }).bind(this, stateField.element));

            this.relatedElementFields.push(elementField);
            this.relatedStateFields.push(stateField);
        }).bind(this));

        this.$containers.find('.n2_form__table_label_field[data-design-feature="reset-to-normal"] a')
            .on('click', (function (e) {
                e.preventDefault();
                this.value[this.activeState] = {};
                this._lazySave(e);
                this.activateState(this.activeState);
            }).bind(this));

        this.activeVisual = 0;
        this.activeState = 0;
    }

    BasicCSSSkeleton.prototype.loaded = function () {
        for (var k in this.form) {
            this.form[k].on({
                nextendChange: this.changeValue.bind(this, k)
            });
        }
    };

    BasicCSSSkeleton.prototype.changeValue = function (name, e) {
        if (!this.isReload) {
            if (typeof this['_set' + name] == 'function') {
                this['_set' + name](this.value[this.activeState], this.form[name].val());
            } else {
                this.value[this.activeState][name] = this.form[name].val();
            }

            this._lazySave(e);
        }
    };

    BasicCSSSkeleton.prototype._lazySave = NextendDeBounce(function (e) {
        this.isInsideChange = true;
        var value = this.getData();
        this.visuals[this.activeVisual].field.save(e, value);
        this.visuals[this.activeVisual].value = value;
        this.isInsideChange = false;
    }, 50);

    /**
     *
     * @param {object} data
     */
    BasicCSSSkeleton.prototype.save = function (data) {
        this.isInsideChange = true;
        for (var k in data) {
            this.visualsByName[k].field.save({}, data[k]);
            this.visualsByName[k].value = data[k];
        }
        this.isInsideChange = false;
    };

    BasicCSSSkeleton.prototype.getData = function () {
        return JSON.stringify({
            data: this.value
        });
    };

    BasicCSSSkeleton.prototype.load = function (values, visuals) {
        this.hasVisuals = visuals.length > 0;
        this.$containers.attr('data-elements', visuals.length);
        if (this.hasVisuals) {
            var options = {};

            this.visuals = [];
            this.visualsByName = {};

            for (var i = 0; i < visuals.length; i++) {
                var visual = visuals[i];
                this.visualsByName[visual.name] = {
                    value: values[visual.name],
                    mode: visual.mode,
                    field: visual.field
                };

                visual.field.element
                    .off('.basiccss')
                    .on('outsideChange.basiccss', this.loadSingleValue.bind(this, i, visual.name));
                this.visuals.push(this.visualsByName[visual.name]);

                options[i] = visual.field.getLabel();
            }

            this.elementField.setOptions(options);
            for (var i = 0; i < this.relatedElementFields.length; i++) {
                this.relatedElementFields[i].setOptions(options);
            }

            this.activateVisual(0);
            this.activateState(0);
        }
    };

    BasicCSSSkeleton.prototype.loadSingleValue = function (i) {
        if (!this.isInsideChange) {
            this.visuals[i].value = this.visuals[i].field.element.val();
            if (this.activeVisual == i) {
                this.activateVisual(i);
                this.activateState(this.activeState);
            }
        }
    };

    /**
     * @param index
     */
    BasicCSSSkeleton.prototype.activateVisual = function (index) {

        for (var i = 0; i < this.relatedElementFields.length; i++) {
            if (parseInt(this.relatedElementFields[i].element.val()) !== index) {
                this.relatedElementFields[i].insideChange(index);
            }
        }
    };

    BasicCSSSkeleton.prototype.setValue = function (value) {
        this.value = value;
    };

    BasicCSSSkeleton.prototype.setStates = function (states) {

        var options = {};

        for (var i = 0; i < states.length; i++) {
            options[i] = states[i];
        }

        this.stateField.setOptions(options);
        for (var i = 0; i < this.relatedStateFields.length; i++) {
            this.relatedStateFields[i].setOptions(options);
        }
    };

    BasicCSSSkeleton.prototype.activateState = function (index) {

        index = Math.max(0, parseInt(index));

        this.isReload = true;

        this.activeState = index;
        this.$containers.attr('data-state', index);

        var value;
        if (index === 0) {
            value = this.value[0];
        } else {
            value = _Assign({}, this.value[0], this.value[index]);
        }

        for (var k in value) {
            if (this.form[k] !== undefined) {
                if (typeof this['_transform' + k] == 'function') {
                    this.form[k].data('field').insideChange(this['_transform' + k](value[k]));
                } else {
                    this.form[k].data('field').insideChange(value[k]);
                }
            }
        }

        for (var i = 0; i < this.relatedStateFields.length; i++) {
            if (parseInt(this.relatedStateFields[i].element.val()) !== index) {
                this.relatedStateFields[i].insideChange(index);
            }
        }

        this.isReload = false;
    };

    BasicCSSSkeleton.prototype.serialize = function () {
        if (this.hasVisuals) {
            var serialized = {};
            for (var k in this.visualsByName) {
                serialized[k] = this.visualsByName[k].value;
            }
            return serialized;
        }
        return {};
    };

    BasicCSSSkeleton.prototype.unSerialize = function (serialized) {
        for (var k in serialized) {
            if (serialized.hasOwnProperty(k)) {
                this.visualsByName[k].field.save({}, serialized[k]);
                this.visualsByName[k].value = serialized[k];
            }
        }
    };

    return BasicCSSSkeleton;
});_N2.d('BasicCSSStyle', ['$', 'BasicCSSSkeleton'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments {_N2.BasicCSSSkeleton}
     */
    function BasicCSSStyle() {
        this._singular = 'style';
        this._prular = 'styles';
        _N2.BasicCSSSkeleton.prototype.constructor.apply(this, arguments);


        this.form = {
            backgroundcolor: $('#layer-style-backgroundcolor'),
            opacity: $('#layer-style-opacity'),
            padding: $('#layer-style-padding'),
            border: $('#layer-style-border'),
            borderradius: $('#layer-style-borderradius'),
            boxshadow: $('#layer-style-boxshadow'),
            extra: $('#layer-style-extracss')
        };

        this.loaded();
    }

    BasicCSSStyle.prototype = Object.create(_N2.BasicCSSSkeleton.prototype);
    BasicCSSStyle.prototype.constructor = BasicCSSStyle;

    BasicCSSStyle.prototype.activateVisual = function (index) {
        this.activeVisual = index;

        this.setValue(_N2.CSSRendererStyle.transformData(this.visuals[index].value, this.visuals[index].mode));
        this.setStates(_N2.CSSRendererStyle.rendererModes[this.visuals[index].mode].tabs);

        _N2.BasicCSSSkeleton.prototype.activateVisual.call(this, index);
    };

    return BasicCSSStyle;
});_N2.d('BasicCSS', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function BasicCSS(id, options) {
        this.inPresetList = false;
        this.$container = $('#' + id);

        this.options = _Assign({
            ajaxUrl: ''
        }, options);

        this.throttleSetTimeout = null;
        this.throttleExitTimeout = null;

        this.storage = {};

        /**
         * @type {{style: _N2.BasicCSSStyle, font: _N2.BasicCSSFont}}
         */
        this.assets = {
            font: new _N2.BasicCSSFont(this),
            style: new _N2.BasicCSSStyle(this)
        };


        this.$preset = $('<div class="n2_layer_window_design_preset"><div class="n2_layer_window_design_preset__label_container"><div class="n2_layer_window_design_preset__label">' + n2_('Preset') + '</div><i class="ssi_16 ssi_16--info" data-tip-description="' + n2_('You can use presets to save style settings for later use. Clicking on any preset will load its styling to your current layer, and the previous style settings will be lost.') + '" data-tip-label="' + n2_('Preset') + '"></i></div></div>').prependTo(this.$container);

        this.$notice = $('<div class="n2_layer_window_notice n2_layer_window_notice__device_icon"><div class="n2_field" data-field="layerslide-background-notice-image"><div class="n2_field__label"><label for="layerslide-background-notice-image">Note</label></div><div class="n2_field__element"><div class="n2_field_message n2_field_message--warning">' + n2_('Layer design options affect every device. If you need to make responsive adjustments, look for the options with the device icon.') + '</div></div></div></div>').prependTo(this.$container);

        var $presetFields = $('<div class="n2_layer_window_design_preset__fields"></div>').appendTo(this.$preset);


        var $presetFieldsSecondary = $('<div class="n2_layer_window_design_preset__fields_secondary"></div>').appendTo($presetFields);

        $('<a class="n2_layer_window_design_preset__button_secondary" href="#" data-n2tip="' + n2_('Reset style to default') + '"><i class="ssi_16 ssi_16--reset"></i></a>')
            .on('click', (function (e) {
                e.preventDefault();
                this.exitPresetList(this.defs, e);
            }).bind(this)).appendTo($presetFieldsSecondary);

        $('<a class="n2_layer_window_design_preset__button_secondary" href="#" data-n2tip="' + n2_('Save style as new preset') + '"><i class="ssi_16 ssi_16--save"></i></a>')
            .on('click', (function (e) {
                e.preventDefault();

                this.saveAsNew();
            }).bind(this)).appendTo($presetFieldsSecondary);


        this.$presets = $('<div class="n2_layer_window_design_preset__presets"></div>').insertAfter(this.$preset);


        $('<a class="n2_layer_window_design_preset__button_choose" data-n2tip="' + n2_('Load style') + '" href="#"><i class="ssi_16 ssi_16--plus"></i></a>')
            .on('click', (function (e) {
                e.preventDefault();

                this.showList();
            }).bind(this))
            .appendTo($presetFields);

        $('<a class="n2_layer_window_design_preset__button_back" href="#"><i class="ssi_16 ssi_16--remove"></i></a>')
            .on('click', (function (e) {
                e.preventDefault();
                this.exitPresetList(false, e);
            }).bind(this))
            .appendTo($presetFields);

        _N2._basicCSS = this;
    }

    BasicCSS.prototype.showList = function () {
        this.inPresetList = true;
        this.lastState = this.serialize();

        this.$presets.on('mouseleave', (function () {
            this.throttledUnSerialize(this.lastState);
        }).bind(this));

        $.when(this.loadType())
            .done((function (data) {
                if (this.storage[this.type].data('presets') === 0) {
                    _N2.Notification.notice(n2_('You have not created any presets for this layer yet.'));

                    this.deActivate();
                } else {
                    this.$presets.append(this.storage[this.type]);

                    $('.n2_ss_layer_window').addClass('n2_ss_layer_window--show-presets');
                    $('.n2_ss_layer_window__tab_container').scrollTop(0);

                }
            }).bind(this));
    };

    BasicCSS.prototype.activate = function (type, values, structure) {

        if (this.inPresetList) {
            this.exitPresetList(false);
        }

        if (this.type && this.type !== type && this.storage[this.type] !== undefined) {
            this.storage[this.type].detach();
        }

        var hasVisuals = false;
        this.defs = {
            font: [],
            style: []
        };
        this.type = type;
        for (var k in this.assets) {
            for (var i = 0; i < structure[k].length; i++) {
                this.defs[k][structure[k][i].name] = structure[k][i].def;
            }
            this.assets[k].load(values, structure[k]);
            hasVisuals = hasVisuals || this.assets[k].hasVisuals;
        }

        this.$container.toggleClass('n2_ss_design_layer_window_design--visible', hasVisuals);
    };

    BasicCSS.prototype.deActivate = function () {

        if (this.inPresetList) {
            this.exitPresetList(false);
        }
    };

    BasicCSS.prototype.serialize = function () {
        var serialized = {};
        for (var k in this.assets) {
            serialized[k] = this.assets[k].serialize();
        }
        return serialized;
    };

    BasicCSS.prototype.unSerialize = function (serialized) {

        this.assets.font.unSerialize(serialized.font);
        this.assets.style.unSerialize(serialized.style);
    };

    BasicCSS.prototype.throttledUnSerialize = function (serialized) {
        this._addThrottledRenderTimeout(this.unSerialize.bind(this, serialized));
    };

    BasicCSS.prototype.saveAsNew = function (name) {
        var modal = new _N2.ModalSimple('section-save-as', {
            destroyOnHide: false,
            width: 440
        });

        modal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--plus', 'green'));
        modal.addContent(_N2.UI.modalHeading(n2_('Save as')));

        var $row = _N2.UI.modalFormRow(true);
        modal.addContent($row);

        var $name = _N2.UI.modalFieldText($row, 'name', n2_('Name'), '');

        var $saveButton = _N2.UI.modalButton(n2_('Save as'), 'green');
        modal.addButton($saveButton);

        $saveButton.on('click', (function (e) {
            e.preventDefault();

            var name = $name.val();
            if (name === '') {
                _N2.Notification.error(n2_('Please fill the name field!'));
            } else {
                _N2.AjaxHelper.ajax({
                    type: "POST",
                    url: _N2.AjaxHelper.makeAjaxUrl(this.options.ajaxUrl, {
                        nextendaction: 'addVisual'
                    }),
                    data: {
                        type: this.type,
                        value: _N2.Base64.encode(JSON.stringify({
                            name: name,
                            data: this.serialize()
                        }))
                    },
                    dataType: 'json'
                })
                    .done((function (response) {

                        $.when(this.loadType())
                            .done((function () {
                                this.addVisual(response.data.visual).prependTo(this.storage[this.type]);
                            }).bind(this));

                        modal.hide(e);

                        _N2.Notification.success(n2_('Preset saved.'));
                    }).bind(this));
            }
        }).bind(this));

        modal.show();
    };

    BasicCSS.prototype.loadType = function () {
        if (this.storage[this.type] === undefined) {
            var deferred = $.Deferred(),
                parseVisuals = (function (visuals) {
                    this.storage[this.type] = $('<div class="n2_layer_window_design_preset__presets_list"></div>')
                        .data('presets', 0);
                    for (var i = 0; i < visuals.length; i++) {
                        this.addVisual(visuals[i]);
                    }
                    deferred.resolve();
                }).bind(this);
            if (window[this.type] === undefined) {
                this.storage[this.type] = deferred;
                _N2.AjaxHelper.ajax({
                    type: "POST",
                    url: _N2.AjaxHelper.makeAjaxUrl(this.options.ajaxUrl, {
                        nextendaction: 'loadVisuals'
                    }),
                    data: {
                        type: this.type
                    },
                    dataType: 'json'
                }).done((function (response) {
                    parseVisuals(response.data.visuals);
                }).bind(this));
            } else {
                parseVisuals(window[this.type]);
            }
        }
        return this.storage[this.type];
    };

    /**
     * loadType must be called for the actual type to be able to add visual!!!
     * @param visual
     * @returns {*}
     */
    BasicCSS.prototype.addVisual = function (visual) {

        var decoded = visual.value;
        if (decoded[0] !== '{') {
            decoded = _N2.Base64.decode(decoded)
        }

        var value = JSON.parse(decoded),
            row = $('<div class="n2_layer_window_design_preset__preset_list_preset"><span>' + value.name + '</span></div>').on({
                mouseenter: (function (value, e) {
                    this.throttledUnSerialize(value.data);
                }).bind(this, value),
                click: (function (data, e) {
                    e.preventDefault();
                    this.exitPresetList(data, e);
                }).bind(this, value.data)
            })
                .appendTo(this.storage[this.type]);

        this.storage[this.type].data('presets', this.storage[this.type].data('presets') + 1);

        if (visual.id > 10000) {
            var actions = $('<div class="n2_layer_window_design_preset__preset_list_preset_buttons"></div>').appendTo(row);

            $('<div class="n2_layer_window_design_preset__preset_list_preset_button" data-n2tip="' + n2_('Overwrite preset') + '"><i class="ssi_16 ssi_16--save"></i></div>').on('click', (function (visualID, name, e) {
                e.stopPropagation();
                _N2.AjaxHelper.ajax({
                    type: "POST",
                    url: _N2.AjaxHelper.makeAjaxUrl(this.options.ajaxUrl, {
                        nextendaction: 'changeVisual'
                    }),
                    data: {
                        visualId: visualID,
                        value: _N2.Base64.encode(JSON.stringify({
                            name: name,
                            data: this.lastState
                        })),
                        type: this.type
                    },
                    dataType: 'json'
                })
                    .done((function (response) {
                        row.replaceWith(this.addVisual(response.data.visual));

                        this.storage[this.type].data('presets', this.storage[this.type].data('presets') - 1);

                        _N2.Notification.success(n2_('Preset saved.'));
                    }).bind(this));
            }).bind(this, visual.id, value.name))
                .appendTo(actions);

            $('<div class="n2_layer_window_design_preset__preset_list_preset_button"><i class="ssi_16 ssi_16--delete"></i></div>').on('click', (function (visualID, e) {
                e.preventDefault();
                e.stopPropagation();
                _N2.AjaxHelper.ajax({
                    type: "POST",
                    url: _N2.AjaxHelper.makeAjaxUrl(this.options.ajaxUrl, {
                        nextendaction: 'deleteVisual'
                    }),
                    data: {
                        visualId: visualID,
                        type: this.type
                    },
                    dataType: 'json'
                }).done((function (response) {
                    row.remove();

                    this.storage[this.type].data('presets', this.storage[this.type].data('presets') - 1);
                    _N2.Notification.success(n2_('Preset deleted.'));
                }).bind(this));
            }).bind(this, visual.id)).appendTo(actions);

            _N2._tooltip.add(actions);
        }
        return row;
    };

    BasicCSS.prototype.exitPresetList = function (data, e) {
        if (this.throttleSetTimeout) {
            clearTimeout(this.throttleSetTimeout);
        }

        this.$presets.off('mouseleave');

        if (data) {
            this.inPresetList = false;
            this.unSerialize(data);
        } else {
            this.unSerialize(this.lastState);
        }
        $('.n2_ss_layer_window').removeClass('n2_ss_layer_window--show-presets');
        $('.n2_ss_layer_window__tab_container').scrollTop(0);
        this.inPresetList = false;

    };

    BasicCSS.prototype._addThrottledRenderTimeout = function (cb) {
        if (this.throttleSetTimeout) {
            clearTimeout(this.throttleSetTimeout);
        }

        this.throttleSetTimeout = setTimeout(cb, 100);
    };

    BasicCSS.prototype._addThrottledExitTimeout = function (cb) {
        if (this.throttleExitTimeout) {
            clearTimeout(this.throttleExitTimeout);
        }

        this.throttleExitTimeout = setTimeout(cb, 100);
    };

    return BasicCSS;

});_N2.d('FormElementBreakpoint', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param {{}} options
     * @constructor
     */
    function FormElementBreakpoint(id, options) {

        this.$container = $('#' + id).find('.n2_field_breakpoint__breakpoint_container');

        this.options = _Assign({
            orientation: '',
            devices: [],
            fields: {},
            enables: false,
            global: false
        }, options);

        this.hasEnables = !!this.options.enables;
        this.useGlobal = false;

        this.$container.toggleClass('n2_field_breakpoint--with-enables', this.hasEnables);

        this.$orientationField = $('#' + this.options.orientation)
            .on('nextendChange', this.onOrientationChanged.bind(this));

        this.breakpoints = {};

        $('<div class="n2_field_breakpoint__breakpoint_start"><div class="n2_field_breakpoint__breakpoint_label_container"><div class="n2_field_breakpoint__breakpoint_label">0</div></div></div>').prependTo(this.$container);

        this.$devices = this.$container.find('.n2_field_breakpoint__device');

        if (this.options.global) {
            this.$useGlobalField = $('#' + this.options.global.field)
                .on('nextendChange', this.syncUseGlobal.bind(this));
            this.useGlobal = this.$useGlobalField.val() === '1';

            this.syncUseGlobal();
        }

        var chainPrev = false,
            insertBefore = false;
        for (var i = 0; i < this.$devices.length; i++) {
            var breakpoint = this.createBreakpoint(this.$devices.eq(i), chainPrev);
            if (breakpoint !== undefined) {
                if (insertBefore) {
                    breakpoint.$breakpoint.insertBefore(breakpoint.$device);
                } else {
                    breakpoint.$breakpoint.insertAfter(breakpoint.$device);
                }

                chainPrev = breakpoint;
            } else {
                insertBefore = true;
            }
        }

        if (this.options.global) {
            this.syncUseGlobal();
        }

        $('<div class="n2_field_breakpoint__breakpoint_end"><div class="n2_field_breakpoint__breakpoint_label_container"><div class="n2_field_breakpoint__breakpoint_label">&infin;</div></div></div>').appendTo(this.$container);
    }

    FormElementBreakpoint.prototype.syncUseGlobal = function () {
        this.useGlobal = this.$useGlobalField.val() === '1';

        for (var k in this.breakpoints) {
            this.breakpoints[k].syncUseGlobal(this.useGlobal);
        }
    };

    /**
     *
     * @param $device
     * @param {boolean|Breakpoint} chainPrev
     * @returns {undefined|Breakpoint}
     */
    FormElementBreakpoint.prototype.createBreakpoint = function ($device, chainPrev) {
        var device = $device.data('id');

        if (device !== 'desktopportrait') {
            this.breakpoints[device] = new Breakpoint(this, device, $device, chainPrev);

            return this.breakpoints[device];
        }

        return undefined;
    };

    FormElementBreakpoint.prototype.onOrientationChanged = function () {
        var orientation = this.$orientationField.val();

        for (var k in this.breakpoints) {
            this.breakpoints[k].setOrientation(orientation);
        }

        this.$container.attr('data-orientation', orientation);
    };


    /**
     *
     * @param {_N2.FormElementBreakpoint} parentField
     * @param device
     * @param $device
     * @param chainPrev
     * @constructor
     */
    function Breakpoint(parentField, device, $device, chainPrev) {

        /**
         * @type {_N2.FormElementBreakpoint}
         */
        this.parentField = parentField;

        /**
         * @type {boolean|Breakpoint}
         */
        this.prev = chainPrev;
        if (chainPrev) {
            chainPrev.setNext(this);
        }

        /**
         * @type {boolean|Breakpoint}
         */
        this.next = false;

        this.disableEdit = false;

        this.orientation = 'portrait';

        this.device = device;
        this.$device = $device;
        this.$breakpoint = $('<div class="n2_field_breakpoint__breakpoint" data-id="' + device + '"><div class="n2_field_breakpoint__breakpoint_divider"></div></div>');
        this.$fieldPortrait = $('#' + parentField.options.fields[device + '-portrait']);
        this.$fieldLandscape = $('#' + parentField.options.fields[device + '-landscape']);

        this.$labelContainer = $('<div class="n2_field_breakpoint__breakpoint_label_container"></div>')
            .appendTo(this.$breakpoint);

        $('<div class="n2_field_breakpoint__breakpoint_label_rename"><i class="ssi_16 ssi_16--rename"></i></div>')
            .on('click', this.edit.bind(this))
            .appendTo(this.$labelContainer);

        this.$labelPortrait = $('<div class="n2_field_breakpoint__breakpoint_label" data-orientation="portrait"></div>')
            .text(this.$fieldPortrait.val())
            .appendTo(this.$labelContainer);

        this.$labelLandscape = $('<div class="n2_field_breakpoint__breakpoint_label" data-orientation="landscape"></div>')
            .text(this.$fieldLandscape.val())
            .appendTo(this.$labelContainer);

        if (parentField.hasEnables && parentField.options.enables[device]) {
            this.$enableField = $('#' + parentField.options.enables[device]);
            /**
             *  @type {_N2.FormElementOnoff}
             */
            this.enableField = this.$enableField.data('field');

            this.isEnabled = parseInt(this.$enableField.val()) === 1;
            this.syncEnabledClass();

            this.$device.append('<div class="n2_field_breakpoint__device_disable"><i class="ssi_16 ssi_16--remove"></i></div>');

            this.$device.on('click', this.toggleEnable.bind(this));
        }
    }

    Breakpoint.prototype.setOrientation = function (orientation) {
        this.orientation = orientation;
    };

    /**
     * @param {Breakpoint} chainNext
     */
    Breakpoint.prototype.setNext = function (chainNext) {
        this.next = chainNext;
    };

    /**
     * @returns {number}
     */
    Breakpoint.prototype.getValue = function () {
        return parseInt(this.$fieldPortrait.val());
    };

    Breakpoint.prototype.syncUseGlobal = function (useGlobal) {
        if (useGlobal) {
            this.disableEdit = true;
            this.$labelPortrait.text(this.parentField.options.global.values[this.device + '-portrait']);
            this.$labelLandscape.text(this.parentField.options.global.values[this.device + '-landscape']);
        } else {
            this.disableEdit = false;
            this.$labelPortrait.text(this.$fieldPortrait.val());
            this.$labelLandscape.text(this.$fieldLandscape.val());
        }

        this.$labelContainer.toggleClass('n2_field_breakpoint__breakpoint_label_container--disable-edit', this.disableEdit);
    };

    Breakpoint.prototype.toggleEnable = function (e) {
        e.preventDefault();

        this.isEnabled = !this.isEnabled;

        this.enableField.insideChange(this.isEnabled ? 1 : 0);

        this.syncEnabledClass();
    };

    Breakpoint.prototype.syncEnabledClass = function () {
        this.$device.toggleClass('n2_field_breakpoint__device--enabled', this.isEnabled);
        this.$breakpoint.toggleClass('n2_field_breakpoint__breakpoint--enabled', this.isEnabled);

        $('body').attr('data-' + this.device, this.isEnabled ? 1 : 0);
    };

    Breakpoint.prototype.edit = function (e) {
        var $label = this.orientation === 'portrait' ? this.$labelPortrait : this.$labelLandscape,
            $field = this.orientation === 'portrait' ? this.$fieldPortrait : this.$fieldLandscape;
        if (!this.disableEdit && $label[0].contentEditable !== 'true') {

            e.preventDefault();

            new _N2.InlineTextEditor($label[0], {
                filter: this.onFilter.bind(this),
                onSave: this.onSave.bind(this, $field, $label),
                onCancel: this.onCancel.bind(this, $field, $label)
            });
        }
    };

    Breakpoint.prototype.onFilter = function (text) {
        return text
            .replace(/[^0-9]/gi, '')
            .replace(/^[0]+/gi, '')
            .substr(0, 4);
    };

    Breakpoint.prototype.onSave = function ($field, $label, value) {

        value = parseInt(value);
        if (!value) {
            this.onCancel($field, $label);
        } else {
            value = Math.max(100, Math.min(99999, parseInt(value)));

            $field.val(value).trigger('change');
            $label.text(value);
        }
    };

    Breakpoint.prototype.onCancel = function ($field, $label) {
        $label.text(parseInt($field.val()));
    };

    return FormElementBreakpoint;
});_N2.d('FormElementButtonMoreLess', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormElementButtonMoreLess(id, options) {

        this.$button = $('#' + id)
            .on('click', this.switchState.bind(this));

        this.options = _Assign({
            labelMore: '',
            labelLess: '',
            relatedFields: []
        }, options);

        this.shownMore = false;


        this.relatedFields = $('');

        if (options.relatedFields !== undefined && options.relatedFields.length) {
            for (var i = 0; i < options.relatedFields.length; i++) {
                this.relatedFields = this.relatedFields.add($('[data-field="' + options.relatedFields[i] + '"]'));
            }
        }

        this.syncState();
    }

    FormElementButtonMoreLess.prototype.switchState = function (e) {
        e.preventDefault();

        this.shownMore = !this.shownMore;

        if (this.shownMore) {
            this.$button.text(this.options.labelLess);
        } else {
            this.$button.text(this.options.labelMore);
        }

        this.syncState();
    };

    FormElementButtonMoreLess.prototype.syncState = function () {

        this.relatedFields.toggleClass('n2_form_element--hidden', !this.shownMore);
    };


    return FormElementButtonMoreLess;
});_N2.d('FormElementCheckboxOnOff', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormElementCheckboxOnOff(id, options) {
        this.separator = '||';

        this.element = $('#' + id);
        this.$parent = this.element.parent();

        this.options = _Assign({
            invert: false
        }, options);


        this.relatedFields = $('');

        if (options.relatedFields !== undefined && options.relatedFields.length) {
            for (var i = 0; i < options.relatedFields.length; i++) {
                this.relatedFields = this.relatedFields.add($('[data-field="' + options.relatedFields[i] + '"]'));
            }

            this.setValue(parseInt(this.element.val()) ? 1 : 0);
        }

        _N2.FormElement.prototype.constructor.call(this);

        this.$parent.on('click', this.switchCheckbox.bind(this));
    }


    FormElementCheckboxOnOff.prototype = Object.create(_N2.FormElement.prototype);
    FormElementCheckboxOnOff.prototype.constructor = FormElementCheckboxOnOff;

    FormElementCheckboxOnOff.prototype.switchCheckbox = function () {

        this.setValue(parseInt(this.element.val()) ? 0 : 1);

        this.triggerOutsideChange();
    };


    FormElementCheckboxOnOff.prototype.setValue = function (value) {
        this.element.val(value);

        var isActive = false;
        if (!this.options.invert && value === 1) {
            isActive = true;
        } else if (this.options.invert && value !== 1) {
            isActive = true;
        }
        this.$parent.toggleClass('n2_field_checkbox_onoff--active', isActive);

        this.relatedFields.toggleClass('n2_form_element--hidden', this.options.invert ? isActive : !isActive);
    };

    FormElementCheckboxOnOff.prototype.insideChange = function (value) {

        this.setValue(parseInt(value) ? 1 : 0);

        this.triggerInsideChange();
    };

    return FormElementCheckboxOnOff;

});_N2.d('FormElementColor', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param alpha
     * @constructor
     */
    function FormElementColor(id, alpha) {

        this.element = $('#' + id);

        this.elementInner = this.element[0].parentNode.querySelector('.n2-field-color-preview-inner');
        this.elementInnerSMW = _N2.MW.___getSMW(this.elementInner);

        this.alpha = alpha === 1;

        this.element.on('change', this.onChange.bind(this));

        this.text = this.element.data('field');

        _N2.FormElement.prototype.constructor.call(this);

        this.syncUI();

        _addEventListener(this.elementInner, 'click', this.showColorPicker.bind(this));
    }

    FormElementColor.prototype = Object.create(_N2.FormElement.prototype);
    FormElementColor.prototype.constructor = FormElementColor;

    FormElementColor.prototype.syncUI = function () {

        this.elementInnerSMW.background = ColorConvert.Hex8toCssRGBA(this.element.val());
    };

    FormElementColor.prototype.showColorPicker = function () {
        _N2.ColorPicker.show(this.element[0].parentNode, this.element.val(), this.alpha, this.notifySaveCallback.bind(this), this.notifyChangeCallback.bind(this));
    };

    FormElementColor.prototype.notifyChangeCallback = function (value) {
        this.element.val(value);
        this.triggerOutsideChange();
        this.syncUI();
    };

    FormElementColor.prototype.notifySaveCallback = function (value) {
        this.element.val(value);
        this.element.trigger('change');
    };

    FormElementColor.prototype.onChange = function (e) {
        var originalValue = this.element.val(),
            value = originalValue.trim();
        /*if the field contains dynamic variable*/
        let isDynamic = value.match(/^{[a-z0-9()\/-]+}$/i);

        if (value.length === 0) {
            // If the field left blank, we need to fix it
            value = (this.alpha ? "00000000" : "000000");
        } else {
            var reRGB = /rgb\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)/i,
                rgbMatch = reRGB.exec(value);
            if (rgbMatch) {
                value = ColorConvert.DecToHex(rgbMatch[1]) + ColorConvert.DecToHex(rgbMatch[2]) + ColorConvert.DecToHex(rgbMatch[3]);
            }

            if (this.alpha) {
                var reRGBA = /rgba\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3}), ?([0-9.]+)\)/i,
                    rgbaMatch = reRGBA.exec(value);
                if (rgbaMatch) {
                    var alpha = +rgbaMatch[4];
                    if (isNaN(alpha)) {
                        alpha = 1;
                    }
                    value = ColorConvert.DecToHex(rgbaMatch[1]) + ColorConvert.DecToHex(rgbaMatch[2]) + ColorConvert.DecToHex(rgbaMatch[3]) + ColorConvert.DecToHex(Math.floor(alpha * 255));
                }
            }


            if (!isDynamic && (!rgbMatch || !rgbaMatch)) {

                //check if valid color or returns empty string
                value = value.match(/^#?([A-F0-9]+)$/i)?.[1] ?? "";

                if (value.length !== 8) {
                    switch (value.length) {
                        case 3:
                            value = value[0] + value[0] + value[1] + value[1] + value[2] + value[2];
                        case 6:
                            if (this.alpha) {
                                value += 'ff';
                            }
                            break;
                        default:
                            value = (this.alpha ? "FFFFFF00" : "FFFFFF");
                            break;
                    }
                }
            }
        }

        if (originalValue !== value) {
            this.element.val(value);
        }

        this.triggerInsideChange();
        this.triggerOutsideChange();
        this.syncUI();

        if (e) {
            e.stopImmediatePropagation();
        }
    };

    FormElementColor.prototype.insideChange = function (value) {
        this.element.val(value);

        this.triggerInsideChange();
        this.syncUI();
    };

    return FormElementColor;

});_N2.d('FormElementDecoration', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param values
     * @constructor
     *
     * @augments {_N2.FormElement}
     */
    function FormElementDecoration(id, values) {
        this.separator = '||';


        this.element = $('#' + id);

        this.values = values;

        this.checkboxes = this.element.parent().find('.n2_field_decoration__option');

        this.states = this.element.val().split(this.separator);

        for (var i = 0; i < this.checkboxes.length; i++) {
            if (this.states[i] === undefined || this.states[i] !== this.values[i]) {
                this.states[i] = '';
            }

            this.checkboxes.eq(i).on('click', this.switchCheckbox.bind(this, i));
        }

        _N2.FormElement.prototype.constructor.call(this);
    }


    FormElementDecoration.prototype = Object.create(_N2.FormElement.prototype);
    FormElementDecoration.prototype.constructor = FormElementDecoration;


    FormElementDecoration.prototype.switchCheckbox = function (i) {
        if (this.states[i] === this.values[i]) {
            this.states[i] = '';
            this.setSelected(i, 0);
        } else {
            this.states[i] = this.values[i];
            this.setSelected(i, 1);
        }
        this.element.val(this.states.join(this.separator));

        this.triggerOutsideChange();
    };

    FormElementDecoration.prototype.insideChange = function (values) {

        var states = values.split(this.separator);

        for (var i = 0; i < this.checkboxes.length; i++) {
            if (states[i] === undefined || states[i] !== this.values[i]) {
                this.states[i] = '';
                this.setSelected(i, 0);
            } else {
                this.states[i] = this.values[i];
                this.setSelected(i, 1);
            }

        }

        this.element.val(this.states.join(this.separator));

        this.triggerInsideChange();
    };

    FormElementDecoration.prototype.setSelected = function (i, state) {
        this.checkboxes.eq(i).toggleClass('n2_field_decoration__option--selected', state === 1);
    };


    return FormElementDecoration;

});_N2.d('FormElementDevice', ['FormElementOnoff'], function () {

    /**
     * @memberOf _N2
     *
     * @param id
     * @constructor
     */
    function FormElementDevice(id) {
        _N2.FormElementOnoff.prototype.constructor.call(this, id, {
            values: {
                0: 1,
                1: 0
            },
        });
    }

    FormElementDevice.prototype = Object.create(_N2.FormElementOnoff.prototype);
    FormElementDevice.prototype.constructor = FormElementDevice;

    FormElementDevice.prototype.detach = function () {
        this.onoff.detach();
    };

    FormElementDevice.prototype.syncOff = function () {
        this.onoff.removeClass('n2_field_radio__option--selected');
    };

    FormElementDevice.prototype.syncOn = function () {
        this.onoff.addClass('n2_field_radio__option--selected');
    };

    return FormElementDevice;
});_N2.d('FormElementDevices', ['$', 'FormElementDevice'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param values
     * @constructor
     */
    function FormElementDevices(id, values) {

        this.$el = $('#' + id).data('field', this);
        this.fields = {};
        for (var i = 0; i < values.length; i++) {
            this.fields[values[i]] = new _N2.FormElementDevice(id + '-' + values[i]);
        }
    }

    FormElementDevices.prototype.setAvailableDevices = function (devices) {
        for (var k in devices) {
            var field = this.fields[k.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()];
            if (!devices[k] && field) {
                field.detach();
            }
        }
        this.$el.children().first().addClass('n2-first');
        this.$el.children().last().addClass('n2-last');
    };

    return FormElementDevices;
});_N2.d('FormElementFolders', ['FormElementText'], function () {

    /**
     * @memberOf _N2
     *
     * @param id
     * @param parameters
     * @constructor
     */
    function FormElementFolders(id, parameters) {

        _N2.FormElementText.prototype.constructor.call(this, id);

        this.parameters = parameters;


        this.parent.find('.n2_field_text__clear')
            .on('click', this.clear.bind(this));

        this.parent.find('.n2_field_text__choose')
            .on('click', this.show.bind(this));

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementFolders.prototype = Object.create(_N2.FormElementText.prototype);
    FormElementFolders.prototype.constructor = FormElementFolders;

    FormElementFolders.prototype.clear = function (e) {
        e.preventDefault();
        this.val('');
    };

    FormElementFolders.prototype.val = function (value) {
        this.element.val(value);
        this.change();
    };

    FormElementFolders.prototype.show = function (e) {
        e.preventDefault();

        this.modal = new _N2.ModalLightbox(n2_('Choose folder'), 'filesystem');

        this.modal.addContent(_N2._browse.getNode("folder"));

        var $selectButton = _N2.UI.modalButton(n2_('Select'), 'green')
            .on("click", (function (e) {
                e.preventDefault();
                this.modal.hide(e);
                this.val(_N2._browse.getCurrentFolder());
            }).bind(this));
        this.modal.addButton($selectButton);

        this.modal.show();
    };

    return FormElementFolders;
});_N2.d('FormElementFontHidden', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param parameters
     * @constructor
     * @augments {_N2.FormElement}
     */
    function FormElementFontHidden(id, parameters) {
        this.element = $('#' + id);

        this.parameters = _Assign({
            mode: '',
            label: ''
        }, parameters);

        _N2.FormElement.prototype.constructor.call(this);

    }

    FormElementFontHidden.prototype = Object.create(_N2.FormElement.prototype);
    FormElementFontHidden.prototype.constructor = FormElementFontHidden;

    FormElementFontHidden.prototype.getLabel = function () {
        return this.parameters.label;
    };

    FormElementFontHidden.prototype.save = function (e, value) {

        this.val(value);
    };

    FormElementFontHidden.prototype.val = function (value) {
        this.element.val(value);

        this.triggerOutsideChange();
    };

    FormElementFontHidden.prototype.insideChange = function (value) {
        this.element.val(value);

        this.triggerInsideChange();
    };

    /**
     * @returns {_N2.CssInjection}
     */
    FormElementFontHidden.prototype.renderFont = function () {

        return _N2.CSSRendererFont.addCSS('', _N2.CSSRendererFont.getClass(), this.element.val(), _N2.CSSRendererFont.rendererModes[this.parameters.mode]);
    };

    return FormElementFontHidden;
});_N2.d('FormElementFont', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param parameters
     * @constructor
     * @augments {_N2.FormElement}
     */
    function FormElementFont(id, parameters) {
        this.element = $('#' + id);

        this.parent = this.element.parent();

        this.parameters = _Assign({
            mode: '',
            label: '',
            style: '',
            style2: '',
            preview: ''
        }, parameters);

        _N2.FormElement.prototype.constructor.call(this);

        this.parent.on('click', this.show.bind(this));

    }

    FormElementFont.prototype = Object.create(_N2.FormElement.prototype);
    FormElementFont.prototype.constructor = FormElementFont;

    FormElementFont.prototype.getLabel = function () {
        return this.parameters.label;
    };

    FormElementFont.prototype.show = function (e) {
        e.preventDefault();
        if (this.parameters.style !== '') {
            _N2._fontManager.setConnectedStyle(this.parameters.style);
        }
        if (this.parameters.style2 !== '') {
            _N2._fontManager.setConnectedStyle2(this.parameters.style2);
        }

        _N2._fontManager.setFieldID(this.element.attr('id'));

        _N2._fontManager.show(this.element.val(), this.save.bind(this), {
            previewMode: this.parameters.mode,
            previewHTML: this.parameters.preview
        });
    };

    FormElementFont.prototype.save = function (e, value) {

        this.val(value);
    };

    FormElementFont.prototype.val = function (value) {
        this.element.val(value);

        this.triggerOutsideChange();
    };

    FormElementFont.prototype.insideChange = function (value) {
        this.element.val(value);

        this.triggerInsideChange();
    };

    /**
     *
     * @returns {_N2.CssInjection}
     */
    FormElementFont.prototype.renderFont = function () {

        return _N2.CSSRendererFont.addCSS('', _N2.CSSRendererFont.getClass(), this.element.val(), _N2.CSSRendererFont.rendererModes[this.parameters.mode]);
    };

    return FormElementFont;
});_N2.d('FormTabbed', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormTabbed(id, options) {
        this.options = _Assign({
            toggleMode: false,
            sessionID: ''
        }, options);
        this.id = id;

        this.$buttons = $('.n2_form__tab_button[data-related-form="' + id + '"]');
        this.$tabs = $('.n2_form__tab[data-related-form="' + id + '"]');

        this.$buttons.on('click', this.showTab.bind(this));

        if (!this.options.toggleMode) {

            var defaultTab = this.$buttons.eq(0).data('related-tab');
            if (this.options.sessionID !== '') {
                defaultTab = sessionStorage.getItem(this.options.sessionID);
            }

            var $defaultTab = this.$buttons.filter('[data-related-tab="' + defaultTab + '"]');
            if ($defaultTab.length === 0) {
                $defaultTab = this.$buttons.eq(0);
            }
            $defaultTab.triggerHandler('click');
        }
    }

    FormTabbed.prototype.showTab = function (e) {
        e.preventDefault();

        var $target = $(e.currentTarget),
            relatedTab = $target.data('related-tab');

        this.$buttons.removeClass('n2_form__tab_button--active');
        this.$tabs.removeClass('n2_form__tab--active');

        if (!this.options.toggleMode || this.currentTab !== relatedTab) {

            $target.addClass('n2_form__tab_button--active');
            this.$tabs.filter('[data-tab="' + relatedTab + '"]').addClass('n2_form__tab--active');

            this.currentTab = relatedTab;

            if (this.options.sessionID !== '') {
                sessionStorage.setItem(this.options.sessionID, relatedTab);
            }

        } else {
            delete this.currentTab;
        }

    };

    return FormTabbed;
});_N2.d('FormElementGap', ['FormElementMixed'], function () {

    /**
     * @memberOf _N2
     *
     * @param id
     * @param elements
     * @param separator
     * @constructor
     */
    function FormElementGap(id, elements, separator) {
        this.linkedValues = false;

        _N2.FormElementMixed.prototype.constructor.apply(this, arguments);

        this.$field = this.element.parent();

        this.$field.find('.n2_field_margin_padding__pre_label').on('click', (function (e) {
            e.preventDefault();
            this.linkedValues = !this.linkedValues;

            this.$field.toggleClass('n2_field_margin_padding--linked-values', this.linkedValues);

            if (this.linkedValues) {
                this.elements[0].trigger('change');
            }
        }).bind(this));

        this.refresh();
    }


    FormElementGap.prototype = Object.create(_N2.FormElementMixed.prototype);
    FormElementGap.prototype.constructor = FormElementGap;

    FormElementGap.prototype.onFieldChange = function () {
        if (this.linkedValues) {
            var value = this.elements[0].val();
            for (var i = 1; i < 2; i++) {
                this.elements[i].data('field').insideChange(value);
            }
        }

        this.element.val(this.getValue());
        this.triggerOutsideChange();
    };

    FormElementGap.prototype.insideChange = function (value) {
        _N2.FormElementMixed.prototype.insideChange.apply(this, arguments);

        this.refresh();
    };

    FormElementGap.prototype.refresh = function () {

        this.linkedValues = true;
        var value = this.elements[0].val();
        for (var i = 1; i < 2; i++) {
            if (value !== this.elements[i].val()) {
                this.linkedValues = false;
                break;
            }
        }

        this.$field.toggleClass('n2_field_margin_padding--linked-values', this.linkedValues);
    };

    FormElementGap.prototype.relatedFieldsOff = function () {
        return this.element.val() === '0|*|0';
    };

    FormElementGap.prototype.isEmpty = function (value) {

        return value === '|*|' || value === '0|*|0';
    };
    return FormElementGap;
});



_N2.d('FormElementIconTab', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormElementIconTab(id, options) {

        this.options = _Assign({
            relatedValueFields: [],
            relatedAttribute: ''
        }, options);

        this.parent = $('#' + id + '_icon_tab');

        this.parent.find('.n2_field_icon_tab__option').each((function (i, el) {
            $(el).on('click', (function (e) {
                this.onClick($(e.currentTarget));
            }).bind(this));
        }).bind(this));

        this.element = $('#' + id).on('change', this.onHiddenChange.bind(this));

        this.relatedValueFields = false;
        if (this.options.relatedValueFields.length) {
            var value = this.element.val();
            this.relatedValueFields = $('');
            for (var i = 0; i < this.options.relatedValueFields.length; i++) {
                var $field;

                for (var j = 0; j < this.options.relatedValueFields[i].field.length; j++) {
                    $field = $('[data-field="' + this.options.relatedValueFields[i].field[j] + '"]')
                        .data('show-values', this.options.relatedValueFields[i].values);

                    $field.toggleClass('n2_form_element--hidden', this.options.relatedValueFields[i].values.indexOf(value) === -1);

                    this.relatedValueFields = this.relatedValueFields.add($field);
                }

            }

        }

        if (this.options.relatedAttribute !== '') {
            var $body = $('#n2-admin')
                .attr('data-' + this.options.relatedAttribute, this.element.val());
            this.element.on('nextendChange', (function () {
                $body.attr('data-' + this.options.relatedAttribute, this.element.val());
            }).bind(this))
        }

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementIconTab.prototype = Object.create(_N2.FormElement.prototype);
    FormElementIconTab.prototype.constructor = FormElementIconTab;

    FormElementIconTab.prototype.onHiddenChange = function () {
        var value = this.element.val();
        if (value) {
            this.insideChange(value);
        }
    };

    FormElementIconTab.prototype.onClick = function ($element) {
        if (!$element.hasClass('n2_field_icon_tab__option--selected')) {
            this.parent.find('.n2_field_icon_tab__option--selected').removeClass('n2_field_icon_tab__option--selected');
            $element.addClass('n2_field_icon_tab__option--selected');
            this.setHiddenValue($element.attr('data-ssoption'));

            this.triggerOutsideChange();
        }
    };

    FormElementIconTab.prototype.insideChange = function (value) {
        this.setHiddenValue(value);

        var $selectedElement = this.parent.find('.n2_field_icon_tab__option[data-ssoption="' + value + '"]');
        this.onClick($selectedElement);

        this.triggerInsideChange();
    };

    FormElementIconTab.prototype.setHiddenValue = function (value) {
        this.element.val(value);

        if (this.relatedValueFields) {
            this.relatedValueFields.each(function () {
                var $el = $(this);
                $el.toggleClass('n2_form_element--hidden', $el.data('show-values').indexOf(value) === -1);
            });
        }
    };

    return FormElementIconTab;

});_N2.d('FormElementIcon2Manager', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @constructor
     */
    function FormElementIcon2Manager(id) {

        this.element = $('#' + id);

        this.parent = this.element.parent();

        _N2.FormElement.prototype.constructor.call(this);

        this.parent.find('.n2_field_chooser__clear')
            .on('click', this.clear.bind(this));

        this.parent.find('.n2_field_chooser__choose')
            .on('click', this.openModal.bind(this));

        this.$preview = this.parent.find('.n2_field_icon__preview')
            .on('click', this.openModal.bind(this));

        this.updateIsEmpty();

    }

    FormElementIcon2Manager.prototype = Object.create(_N2.FormElement.prototype);
    FormElementIcon2Manager.prototype.constructor = FormElementIcon2Manager;

    FormElementIcon2Manager.prototype.isEmpty = function (value) {

        return value === '';
    };

    FormElementIcon2Manager.prototype.clear = function (e) {
        e.preventDefault();
        this.val('');
    };

    FormElementIcon2Manager.prototype.insideChange = function (value) {

        this.updatePreview(value);

        this.element.val(value);

        this.updateIsEmpty();

        this.triggerInsideChange();
    };

    FormElementIcon2Manager.prototype.openModal = function (e) {
        if (e) e.preventDefault();
        _N2.Icons.showModal(this.setIcon.bind(this), this.element.val());
    };

    FormElementIcon2Manager.prototype.val = function (value) {
        this.element.val(value);
        this.updatePreview(value);
        this.updateIsEmpty();

        this.triggerOutsideChange();
    };

    FormElementIcon2Manager.prototype.setIcon = function (value) {
        this.val(value);
    };

    FormElementIcon2Manager.prototype.updateIsEmpty = function () {
        this.parent.toggleClass('n2_field_chooser--empty', this.isEmpty(this.element.val()));
    };

    FormElementIcon2Manager.prototype.updatePreview = function (value) {
        var iconData = _N2.Icons.render(value);
        if (iconData) {
            this.$preview.html('<i class="n2i ' + iconData.class + '">' + iconData.ligature + '</i>');
        } else {
            this.$preview.html('');
        }
    };

    FormElementIcon2Manager.prototype.focus = function (shouldOpen) {
        if (shouldOpen) {
            this.openModal();
        }
    };


    return FormElementIcon2Manager;
});_N2.d('FormElementImageList', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param parameters
     * @param relatedFields
     * @constructor
     */
    function FormElementImageList(id, parameters, relatedFields) {
        this.id = id;
        this.element = $('#' + id);
        this.$parent = this.element.parent();
        this.$preview = this.$parent.find('.n2_field_image_list__preview');
        this.$fields = this.$parent.find('.n2_field_image_list__fields');

        if (this.$fields.length) {
            this.field = this.$fields.find('input[type="text"]').data('field');
            this.field.element.on('outsideChange', (function () {

                var value = this.element.val();

                this.markSelected(value);
                this.renderPreview(value);
            }).bind(this))
        }

        this.$popover = false;

        this.parameters = _Assign({
            width: 120,
            column: 3,
            hasDisabled: false,
            options: []
        }, parameters);

        this.realItemWidth = this.parameters.width + 10;

        this.relatedFields = false;
        if (relatedFields !== undefined && relatedFields.length) {
            this.relatedFields = $('');
            for (var i = 0; i < relatedFields.length; i++) {
                this.relatedFields = this.relatedFields.add($('[data-field="' + relatedFields[i] + '"]'));
            }

            this.relatedFields.toggleClass('n2_form_element--hidden', this.isOff(this.element.val()));
        }

        _N2.FormElement.prototype.constructor.call(this);

        this.$parent.on('click', this.toggle.bind(this));


        this.renderPreview(this.element.val());
    }

    FormElementImageList.prototype = Object.create(_N2.FormElement.prototype);
    FormElementImageList.prototype.constructor = FormElementImageList;

    FormElementImageList.prototype.stopPropagation = function (e) {
        if (e.originalEvent['handled' + this.id] === undefined) {
            e.originalEvent['handled' + this.id] = true;

            return false;
        }

        return true;
    };

    FormElementImageList.prototype.toggle = function (e) {
        if (this.$parent.hasClass('n2_field_image_list--focus')) {
            this.blur(e);
        } else {
            this.focus(e);
        }
    };

    FormElementImageList.prototype.focus = function (e) {
        if (!this.stopPropagation(e)) {
            this.$parent.addClass('n2_field_image_list--focus');
            $('body').on('click.' + this.id, this.blur.bind(this));

            this.buildPopover();

            this.markSelected(this.element.val());
        }
    };

    FormElementImageList.prototype.blur = function (e) {
        if (!this.stopPropagation(e)) {
            this.$parent.removeClass('n2_field_image_list--focus');
            $('body').off('click.' + this.id);
        }
    };


    FormElementImageList.prototype.isOff = function (value) {
        return value === '-1';
    };

    FormElementImageList.prototype.buildPopover = function () {

        if (!this.$popover) {
            this.items = {};

            this.$popover = $('<div class="n2_field_image_list__popover"></div>')
                .on('click', this.stopPropagation.bind(this));
            var $items = $('<div class="n2_field_image_list__popover_items"></div>')
                .css('width', (this.realItemWidth + 20) * this.parameters.column + 'px')
                .appendTo(this.$popover);

            for (var k in this.parameters.options) {
                this.items[k] = $('<div class="n2_field_image_list__popover_item"></div>')
                    .css('width', this.realItemWidth + 'px')
                    .on('click', this.selectOption.bind(this, k))
                    .appendTo($items);

                var $itemImage = $('<div class="n2_field_image_list__popover_item_image"></div>').appendTo(this.items[k]);
                this.loadImage(this.parameters.options[k].url, $itemImage);

                if (this.parameters.options[k].label !== undefined) {
                    $('<div class="n2_field_image_list__popover_item_label"></div>')
                        .text(this.parameters.options[k].label)
                        .appendTo(this.items[k]);
                }
            }

            if (this.parameters.hasDisabled) {
                this.parameters.options['-1'] = {
                    url: ''
                };
                this.items['-1'] = $('<div class="n2_field_image_list__popover_item"></div>')
                    .on('click', this.selectOption.bind(this, '-1'))
                    .append($('<div class="n2_field_image_list__popover_item_disabled"></div>').text(n2_('Disabled')))
                    .appendTo($items);
            }

            this.$fields.appendTo(this.$popover);

            this.$parent.append(this.$popover);
            this.$items = this.$popover.find('.n2_field_image_list__popover_item');
        }
    };

    FormElementImageList.prototype.selectOption = function (value, e) {

        this.setValue(value);

        this.markSelected(value);
        this.renderPreview(value);

        this.triggerOutsideChange();

        this.blur(e);
    };

    FormElementImageList.prototype.setValue = function (value) {
        this.element.val(value);

        if (this.relatedFields) {
            this.relatedFields.toggleClass('n2_form_element--hidden', this.isOff(value));
        }
    };

    FormElementImageList.prototype.markSelected = function (value) {

        this.$items.removeClass('n2_field_image_list__popover_item--selected');

        if (!this.field || this.field.element.val() === '') {
            this.items[value].addClass('n2_field_image_list__popover_item--selected');
        }
    };

    FormElementImageList.prototype.renderPreview = function (value) {
        this.$preview.html('');
        var url, label;

        if (this.field && this.field.element.val() !== '') {
            url = _N2._imageHelper.fixed(this.field.element.val());
        } else {
            if (value !== '-1' && this.parameters.options[value] !== undefined) {
                url = this.parameters.options[value].url;
                label = this.parameters.options[value].label;
            }
        }

        if (url !== undefined) {

            this.loadImage(url, this.$preview);
            if (label) {
                $('<div class="n2_field_image_list__preview_label"></div>')
                    .text(label)
                    .appendTo(this.$preview);
            }
        } else {
            $('<div class="n2_field_image_list__preview_label"></div>')
                .text(n2_('Disabled'))
                .appendTo(this.$preview);
        }
    };

    FormElementImageList.prototype.insideChange = function (value) {

        if (this.parameters.options[value] !== undefined) {
            this.setValue(value);

            this.markSelected(value);
            this.renderPreview(value);

            this.triggerInsideChange();
        }
    };

    FormElementImageList.prototype.loadImage = function (url, $parent) {

        this.getImage(url, '94b2c1')
            .done(function (src) {
                $('<img alt="" src="' + src + '">')
                    .prependTo($parent);
            });
    };

    FormElementImageList.prototype.getCurrentImage = function (color) {

        var value = this.element.val(),
            url = '';
        if (this.parameters.options[value]) {
            url = this.parameters.options[value].url;
        }

        return this.getImage(url, color);
    };

    FormElementImageList.prototype.getImage = function (url, color) {
        var deferred = $.Deferred(),
            extension = url.split('?')[0].split('.').pop();

        if (extension === "svg") {
            $.ajax({
                    url: url,
                    method: "GET",
                    dataType: "text",
                    data: {},
                    success: (function (response) {
                        var coloredSvg = response
                            .replace(/fill="#FFF"/gi, 'fill="#' + color.substr(0, 6) + '"')
                            .replace('opacity="1"', 'opacity="' + N2Color.hex2alpha(color) + '"');
                        deferred.resolve('data:image/svg+xml;base64,' + btoa(coloredSvg));
                    }).bind(this)
                }
            );
        } else {
            deferred.resolve(url);
        }

        return deferred;
    };

    return FormElementImageList;
});_N2.d('FormElementImage', ['$', 'FormElementText'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param parameters
     * @constructor
     */
    function FormElementImage(id, parameters) {

        _N2.FormElementText.prototype.constructor.call(this, id);

        this.parameters = _Assign({
            alt: false
        }, parameters);

        this.preview = this.parent.find('.n2_field_text_image__preview')
            .on('click', this.open.bind(this));

        this.element.on('nextendChange', this.makePreview.bind(this));

        this.parent.find('.n2_field_text__choose')
            .on('click', this.open.bind(this));

        this.parent.find('.n2_field_text__clear')
            .on('click', this.clear.bind(this));

        this.$sizeLabel = $('<div class="n2_field_text_image__size_label"></div>').appendTo(this.parent);

        this.refreshSizeLabel();
    }


    FormElementImage.prototype = Object.create(_N2.FormElementText.prototype);
    FormElementImage.prototype.constructor = FormElementImage;

    FormElementImage.prototype.clear = function (e) {
        e.preventDefault();
        this.val('');
    };

    FormElementImage.prototype.val = function (value, meta) {

        meta = _Assign({alt: false}, meta);
        if (meta.alt && meta.alt !== '' && this.parameters.alt && this.parameters.alt !== '') {
            $('#' + this.parameters.alt).val(meta.alt).trigger('change');
        }

        this.element.val(value);

        this.change();
    };

    FormElementImage.prototype.makePreview = function () {
        var image = this.element.val();
        if (image.substr(0, 1) === '{') {
            this.preview.css('background-image', '');
        } else {
            this.preview.css('background-image', 'url(' + _N2._imageHelper.fixed(image) + ')');
        }

        this.refreshSizeLabel();
    };

    FormElementImage.prototype.open = function (e) {
        if (e) {
            e.preventDefault();
        }
        _N2._imageHelper.openLightbox(this.val.bind(this));
    };

    FormElementImage.prototype.focus = function (shouldOpen) {
        if (shouldOpen) {
            this.open();
        }
    };

    FormElementImage.prototype.refreshSizeLabel = function () {
        this.$sizeLabel.removeClass('n2_field_text_image__size_label--visible');

        var image = this.element.val();
        if (image !== '' && image.substr(0, 1) !== '{') {
            var img = new Image();
            img.addEventListener('load', (function () {
                if (img.naturalWidth && img.naturalHeight) {
                    this.$sizeLabel
                        .text(img.naturalWidth + 'x' + img.naturalHeight + '')
                        .addClass('n2_field_text_image__size_label--visible');
                }
            }).bind(this));
            img.src = _N2._imageHelper.fixed(image);
        }
    };

    return FormElementImage;
});_N2.d('FormElementLayerWindowFocus', ['$', 'UIDragTooltip'], function () {
    var $ = _N2.$;

    var tooltip = new _N2.UIDragTooltip('Focus');

    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormElementLayerWindowFocus(id, options) {

        this.$field = $('#' + id)
            .on('mousedown', this.onMouseDown.bind(this));

        this.onMouseMoveCallback = this.onMouseMove.bind(this);
        this.onMouseUpCallback = this.onMouseUp.bind(this);

        this.$image = this.$field.find('.n2_field_layer_window_focus__image')
            .on({
                error: (function () {
                    this.$image.attr('src', _N2._imageHelper.fixed('$system$/images/placeholder/image.png'));
                }).bind(this)
            });

        this.$dot = $('<div class="n2_field_layer_window_focus__dot"></div>')
            .on('mouseenter', this.onMouseEnter.bind(this))
            .appendTo(this.$field);

        this.options = _Assign({
            image: '',
            focusX: '',
            focusY: ''
        }, options);

        this.$imageField = $('#' + this.options.image)
            .on('nextendChange', this.onImageChange.bind(this));
        this.imageField = this.$imageField.data('field');

        this.$focusXField = $('#' + this.options.focusX)
            .on('nextendChange', this.onFocusXChange.bind(this));
        this.focusXField = this.$focusXField.data('field');

        this.$focusYField = $('#' + this.options.focusY)
            .on('nextendChange', this.onFocusYChange.bind(this));
        this.focusYField = this.$focusYField.data('field');
    }

    FormElementLayerWindowFocus.prototype.onImageChange = function () {

        var value = _N2._imageHelper.fixed(_N2._currentEditor.generator.fill(this.$imageField.val()));
        if (this.image !== value) {
            this.image = value;
            this.$image.attr('src', this.image);
        }
    };

    FormElementLayerWindowFocus.prototype.onFocusXChange = function () {
        var value = this.$focusXField.val();
        if (value !== this.focusX) {
            this.focusX = value;
            this.$dot.css('left', this.focusX + '%');
        }
    };

    FormElementLayerWindowFocus.prototype.onFocusYChange = function () {
        var value = this.$focusYField.val();
        if (value !== this.focusY) {
            this.focusY = value;
            this.$dot.css('top', this.focusY + '%');
        }
    };

    FormElementLayerWindowFocus.prototype.onMouseDown = function (e) {
        this.context = {
            originalFocusX: this.$focusXField.val(),
            originalFocusY: this.$focusYField.val(),
        };

        _N2.History.get().off();
        tooltip.dragShow();

        this.onMouseMove(e);
        document.addEventListener('mousemove', this.onMouseMoveCallback);
        document.addEventListener('mouseup', this.onMouseUpCallback);
    };

    FormElementLayerWindowFocus.prototype.onMouseMove = function (e) {
        e.preventDefault();
        var rect = this.$field[0].getBoundingClientRect(),
            x = Math.max(Math.min(Math.round((e.clientX - rect.left) / rect.width * 100), 100), 0),
            y = Math.max(Math.min(Math.round((e.clientY - rect.top) / rect.height * 100), 100), 0);

        this.$focusXField.val(x).trigger('change');
        this.$focusYField.val(y).trigger('change');

        tooltip.dragUpdate(e, 'X: ' + x + '%<br>Y: ' + y + '%');
    };

    FormElementLayerWindowFocus.prototype.onMouseUp = function (e) {

        this.$focusXField.val(this.context.originalFocusX).trigger('change');
        this.$focusYField.val(this.context.originalFocusY).trigger('change');

        _N2.History.get().on();
        tooltip.dragHide();

        document.removeEventListener('mousemove', this.onMouseMoveCallback);
        document.removeEventListener('mouseup', this.onMouseUpCallback);

        this.onMouseMove(e);

        delete this.context;
    };

    FormElementLayerWindowFocus.prototype.onMouseEnter = function (e) {

        tooltip.hoverShow(e, (function () {
            return 'X: ' + this.$focusXField.val() + '%<br>Y: ' + this.$focusYField.val() + '%';
        }).bind(this));

        this.$dot.on({
            'mousemove.tooltip': function (e) {
                tooltip.hoverUpdatePosition(e);
            },
            'mouseleave.tooltip': (function () {
                if (this.timeout) {
                    clearTimeout(this.timeout);
                    delete this.timeout;
                }
                this.$dot.off('.tooltip');
                tooltip.hoverHide();
            }).bind(this)
        });
    };

    return FormElementLayerWindowFocus;

});
_N2.d('FormElementList', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormElementList(id, options) {

        this.separator = '||';

        this.options = _Assign({
            relatedFields: [],
            relatedValueFields: [],
            relatedAttribute: ''
        }, options);

        this.element = $('#' + id).on('change', this.onHiddenChange.bind(this));

        this.select = $('#' + id + '_select').on('change', this.onChange.bind(this));

        this.relatedFields = false;
        if (this.options.relatedFields.length) {
            this.relatedFields = $('');
            for (var i = 0; i < this.options.relatedFields.length; i++) {
                this.relatedFields = this.relatedFields.add($('[data-field="' + this.options.relatedFields[i] + '"]'));
            }

            this.relatedFields.toggleClass('n2_form_element--hidden', this.isOff(this.element.val()));
        }

        this.relatedValueFields = false;
        if (this.options.relatedValueFields.length) {
            var value = this.element.val();
            this.relatedValueFields = $('');
            for (var i = 0; i < this.options.relatedValueFields.length; i++) {
                var $field;

                for (var j = 0; j < this.options.relatedValueFields[i].field.length; j++) {
                    $field = $('[data-field="' + this.options.relatedValueFields[i].field[j] + '"]')
                        .data('show-values', this.options.relatedValueFields[i].values);

                    $field.toggleClass('n2_form_element--hidden', this.options.relatedValueFields[i].values.indexOf(value) === -1);

                    this.relatedValueFields = this.relatedValueFields.add($field);
                }
            }

        }

        if (this.options.relatedAttribute !== '') {
            var $body = $('#n2-admin')
                .attr('data-' + this.options.relatedAttribute, this.element.val());
            this.element.on('nextendChange', (function () {
                $body.attr('data-' + this.options.relatedAttribute, this.element.val());
            }).bind(this))
        }

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementList.prototype = Object.create(_N2.FormElement.prototype);
    FormElementList.prototype.constructor = FormElementList;

    FormElementList.prototype.onHiddenChange = function () {
        var value = this.element.val();
        if (value && value !== this.select.val()) {
            this.insideChange(value);
        }
    };

    FormElementList.prototype.onChange = function () {
        var value = this.select.val();
        if (value !== null && typeof value === 'object') {
            value = value.join(this.separator);
        }
        this.setHiddenValue(value);

        this.triggerOutsideChange();
    };

    FormElementList.prototype.insideChange = function (value) {
        if (typeof value === 'object') {
            this.select.val(value);
            value = value.join('||');
        } else {
            this.select.val(value);
        }
        this.setHiddenValue(value);

        this.triggerInsideChange();
    };

    FormElementList.prototype.setHiddenValue = function (value) {
        this.element.val(value);

        if (this.relatedFields) {
            this.relatedFields.toggleClass('n2_form_element--hidden', this.isOff(value));
        }

        if (this.relatedValueFields) {
            this.relatedValueFields.each(function () {
                var $el = $(this);
                $el.toggleClass('n2_form_element--hidden', $el.data('show-values').indexOf(value) === -1);
            });
        }
    };

    FormElementList.prototype.isOff = function (value) {
        return value == '' || value == '0' || value == 'off';
    };

    FormElementList.prototype.setOptions = function (options) {
        this.select.html('');

        for (var k in options) {
            $('<option value="' + k + '"></option>')
                .text(options[k])
                .appendTo(this.select);
        }
    };

    return FormElementList;

});
_N2.d('FormElementMarginPadding', ['FormElementMixed'], function () {

    /**
     * @memberOf _N2
     *
     * @param id
     * @param elements
     * @param separator
     * @constructor
     */
    function FormElementMarginPadding(id, elements, separator) {
        this.linkedValues = false;

        _N2.FormElementMixed.prototype.constructor.apply(this, arguments);

        this.$field = this.element.parent();

        this.$field.find('.n2_field_margin_padding__pre_label').on('click', (function (e) {
            e.preventDefault();
            this.linkedValues = !this.linkedValues;

            this.$field.toggleClass('n2_field_margin_padding--linked-values', this.linkedValues);

            if (this.linkedValues) {
                this.elements[0].trigger('change');
            }
        }).bind(this));

        this.refresh();
    }


    FormElementMarginPadding.prototype = Object.create(_N2.FormElementMixed.prototype);
    FormElementMarginPadding.prototype.constructor = FormElementMarginPadding;

    FormElementMarginPadding.prototype.onFieldChange = function () {
        if (this.linkedValues) {
            var value = this.elements[0].val();
            for (var i = 1; i < 4; i++) {
                this.elements[i].data('field').insideChange(value);
            }
        }

        this.element.val(this.getValue());
        this.triggerOutsideChange();
    };

    FormElementMarginPadding.prototype.insideChange = function (value) {
        _N2.FormElementMixed.prototype.insideChange.apply(this, arguments);

        this.refresh();
    };

    FormElementMarginPadding.prototype.refresh = function () {

        this.linkedValues = true;
        var value = this.elements[0].val();
        for (var i = 1; i < 4; i++) {
            if (value !== this.elements[i].val()) {
                this.linkedValues = false;
                break;
            }
        }

        this.$field.toggleClass('n2_field_margin_padding--linked-values', this.linkedValues);
    };

    FormElementMarginPadding.prototype.relatedFieldsOff = function () {
        return this.element.val() === '0|*|0|*|0|*|0';
    };

    FormElementMarginPadding.prototype.isEmpty = function (value) {

        return value === '|*||*||*|' || value === '0|*|0|*|0|*|0';
    };
    return FormElementMarginPadding;
});



_N2.d('FormElementMixed', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param elements
     * @param separator
     * @constructor
     */
    function FormElementMixed(id, elements, separator) {

        this.element = $('#' + id);

        this.elements = [];
        for (var i = 0; i < elements.length; i++) {
            this.elements.push($('#' + elements[i])
                .on('outsideChange', this.onFieldChange.bind(this)));
        }

        this.separator = separator;

        _N2.FormElement.prototype.constructor.call(this);
    }


    FormElementMixed.prototype = Object.create(_N2.FormElement.prototype);
    FormElementMixed.prototype.constructor = FormElementMixed;


    FormElementMixed.prototype.onFieldChange = function () {
        this.element.val(this.getValue());

        this.triggerOutsideChange();
    };

    FormElementMixed.prototype.insideChange = function (value) {
        this.element.val(value);

        var values = value.split(this.separator);

        for (var i = 0; i < this.elements.length; i++) {
            this.elements[i].data('field').insideChange(values[i]);
        }

        this.triggerInsideChange();
    };

    FormElementMixed.prototype.getValue = function () {
        var values = [];
        for (var i = 0; i < this.elements.length; i++) {
            values.push(this.elements[i].val());
        }

        return values.join(this.separator);
    };

    return FormElementMixed;

});_N2.d('FormElementNumberSlider', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param properties
     * @constructor
     */
    function FormElementNumberSlider(id, properties) {
        this.localChange = false;
        this.element = $('#' + id);
        this.$parent = this.element.parent().on({
            'mouseenter.n2slider': this.startSlider.bind(this, properties)
        });
        var $units = this.$parent.siblings('.n2_field_unit').find('> input');
        if (properties.units && $units.length) {
            var units = properties.units;
            $units.on('nextendChange', (function () {
                properties.min = units[$units.val() + 'Min'];
                properties.max = units[$units.val() + 'SliderMax'];
                if (this.slider) {
                    this.uiSlider.setOption("min", properties.min);
                    this.uiSlider.setOption("max", properties.max);
                }
            }).bind(this));
        }
    }

    FormElementNumberSlider.prototype.startSlider = function (properties, e) {
        this.element.parent().off('.n2slider');
        if (!this.slider) {

            var $sliderContainer = $('<div class="n2_field_number__slider"></div>').appendTo(this.$parent),
                $sliderContainerInner = $('<div class="n2_field_number__slider_inner"></div>').appendTo($sliderContainer);

            this.slider = $('<div></div>')
                .appendTo($sliderContainerInner)
                .removeAttr('slide').prop('slide', false); //Mootools fix

            this.uiSlider = new _N2.UISlider(this.slider, _Assign({
                start: (function () {
                    this.element.parent().addClass('n2-active');
                }).bind(this),
                stop: (function () {
                    this.element.parent().removeClass('n2-active');
                }).bind(this),
                sliding: (function (e, ui) {
                    this.localChange = true;
                    this.element.val(ui.value).trigger('change');
                    this.localChange = false;
                }).bind(this)
            }, properties));

            if (this.slider[0].slide !== undefined) {
                this.slider[0].slide = null;
            }

            this.element.on('nextendChange', (function () {
                if (!this.localChange) {
                    var val = parseFloat(this.element.val());
                    if (isFinite(val)) {
                        this.uiSlider.setOption('value', val);
                    }
                }
            }).bind(this));
        }

        this.uiSlider.setOption('value', parseFloat(this.element.val()));
    };

    return FormElementNumberSlider;
});_N2.d('FormElementNumber', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param min
     * @param max
     * @param units
     * @constructor
     */
    function FormElementNumber(id, min, max, units) {
        this.min = min;
        this.max = max;

        this.element = $('#' + id).on({
            focus: this._focus.bind(this),
            blur: this.blur.bind(this),
            change: this.change.bind(this)
        });
        this.parent = this.element.parent();

        var $units = this.parent.siblings('.n2-form-element-units').find('> input');
        if (units && $units.length) {
            $units.on('nextendChange', (function () {
                this.min = units[$units.val() + 'Min'];
                this.max = units[$units.val() + 'Max'];
            }).bind(this));
        }

        _N2.FormElement.prototype.constructor.call(this);
    }


    FormElementNumber.prototype = Object.create(_N2.FormElement.prototype);
    FormElementNumber.prototype.constructor = FormElementNumber;


    FormElementNumber.prototype._focus = function () {
        this.parent.addClass('n2_field_text--focus');

        this.element.on('keypress.n2-text', (function (e) {
            if (e.code === 'Enter') {
                this.element.off('keypress.n2-text');
                this.element.trigger('blur');
            }
        }).bind(this));
    };

    FormElementNumber.prototype.blur = function () {
        this.parent.removeClass('n2_field_text--focus');
    };

    FormElementNumber.prototype.change = function () {
        var validated = this.validate(this.element.val());
        if (validated === true) {
            this.triggerOutsideChange();
        } else {
            this.element.val(validated).trigger('change');
        }
    };

    FormElementNumber.prototype.insideChange = function (value) {
        var validated = this.validate(value);
        if (validated === true) {
            this.element.val(value);
        } else {
            this.element.val(validated);
        }

        this.triggerInsideChange();
    };

    FormElementNumber.prototype.validate = function (value) {
        if (value !== undefined && value.length > 0 && value.charAt(0) === '{') {
            return true;
        }

        var validatedValue = parseFloat(value);
        if (isNaN(validatedValue)) {
            validatedValue = 0;
        }
        validatedValue = Math.max(this.min, Math.min(this.max, validatedValue));
        if (validatedValue + '' !== value) {
            return validatedValue;
        }

        return true;
    };

    FormElementNumber.prototype.relatedFieldsOff = function () {
        return parseInt(this.element.val()) === 0;
    };

    FormElementNumber.prototype.isEmpty = function (value) {

        return value === '' || value === 0;
    };

    return FormElementNumber;
});_N2.d('FormElementOnoff', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @typedef {{relatedFields: array, relatedAttribute: string}} options
     */
    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormElementOnoff(id, options) {
        this.element = $('#' + id);

        this.options = _Assign({
            values: {
                0: 0,
                1: 1
            },
            relatedFieldsOff: [],
            relatedFieldsOn: [],
            relatedAttribute: ''
        }, options);

        this.options.valuesDict = {};
        for (var k in this.options.values) {
            this.options.valuesDict[this.options.values[k]] = k;
        }

        if (this.options.relatedFieldsOff.length) {
            this.relatedFieldsOff = $('');
            for (var i = 0; i < this.options.relatedFieldsOff.length; i++) {
                this.relatedFieldsOff = this.relatedFieldsOff.add($('[data-field="' + this.options.relatedFieldsOff[i] + '"]'));
            }
        } else {
            this.relatedFieldsOff = false;
        }

        if (this.options.relatedFieldsOn.length) {
            this.relatedFieldsOn = $('');
            for (var i = 0; i < this.options.relatedFieldsOn.length; i++) {
                this.relatedFieldsOn = this.relatedFieldsOn.add($('[data-field="' + this.options.relatedFieldsOn[i] + '"]'));
            }
        } else {
            this.relatedFieldsOn = false;
        }

        if (this.options.relatedAttribute !== '') {
            var $body = $('#n2-admin');
            $body.attr('data-' + this.options.relatedAttribute, this.element.val());
            this.element.on('nextendChange', (function () {
                $body.attr('data-' + this.options.relatedAttribute, this.element.val());
            }).bind(this))
        }

        this.onoff = this.element.parent()
            .on({
                click: this.switch.bind(this),
                keydown: (function (e) {
                    if (e.code === 'Space') {
                        e.stopPropagation();
                        e.preventDefault();
                        this.onoff.trigger('click');
                    }
                }).bind(this)
            });

        if (this.options.values[this.element.val()]) {
            this.syncOn();
        } else {
            this.syncOff();
        }

        _N2.FormElement.prototype.constructor.call(this);
    }


    FormElementOnoff.prototype = Object.create(_N2.FormElement.prototype);
    FormElementOnoff.prototype.constructor = FormElementOnoff;


    FormElementOnoff.prototype.switch = function () {

        var state = this.options.values[this.element.val()] ? 0 : 1;

        this.element.val(this.options.valuesDict[state]);
        this.setSelected(state);

        this.triggerOutsideChange();
    };

    FormElementOnoff.prototype.insideChange = function (value) {

        var state = this.options.values[value];

        this.element.val(this.options.valuesDict[state]);
        this.setSelected(state);

        this.triggerInsideChange();
    };

    FormElementOnoff.prototype.setSelected = function (state) {
        if (state) {
            this.syncOn();
        } else {
            this.syncOff();
        }
    };

    FormElementOnoff.prototype.syncOff = function () {

        this.onoff.removeClass('n2_field_onoff--on')
            .attr('aria-checked', 'false');

        if (this.relatedFieldsOff) {
            this.relatedFieldsOff.removeClass('n2_form_element--hidden');
        }
        if (this.relatedFieldsOn) {
            this.relatedFieldsOn.addClass('n2_form_element--hidden');
        }
    };

    FormElementOnoff.prototype.syncOn = function () {

        this.onoff.addClass('n2_field_onoff--on')
            .attr('aria-checked', 'true');

        if (this.relatedFieldsOff) {
            this.relatedFieldsOff.addClass('n2_form_element--hidden');
        }
        if (this.relatedFieldsOn) {
            this.relatedFieldsOn.removeClass('n2_form_element--hidden');
        }
    };

    return FormElementOnoff;

});
_N2.d('FormElementRadio', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param values
     * @param relatedFields
     * @constructor
     */
    function FormElementRadio(id, values, relatedFields) {
        this.element = $('#' + id);

        this.values = values;
        this.relatedFields = false;
        if (relatedFields !== undefined && relatedFields.length) {
            this.relatedFields = $('');
            for (var i = 0; i < relatedFields.length; i++) {
                this.relatedFields = this.relatedFields.add($('[data-field="' + relatedFields[i] + '"]'));
            }

            this.relatedFields.toggleClass('n2_form_element--hidden', this.isOff(this.element.val()));
        }

        this.parent = this.element.parent();

        this.options = this.parent.find('.n2_field_radio__option');

        for (var i = 0; i < this.options.length; i++) {
            this.options.eq(i).on('click', this.click.bind(this));
        }

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementRadio.prototype = Object.create(_N2.FormElement.prototype);
    FormElementRadio.prototype.constructor = FormElementRadio;

    FormElementRadio.prototype.click = function (e) {
        this.changeSelectedIndex(this.options.index(e.currentTarget));
    };

    FormElementRadio.prototype.changeSelectedIndex = function (index) {
        var value = this.values[index];

        this.setValue(value);

        this.setSelected(index);

        this.triggerOutsideChange();
    };

    FormElementRadio.prototype.insideChange = function (value, option) {
        var index = this.values.indexOf(value);
        if (index === -1) {
            index = this.partialSearch(value);
        }

        if (index === -1 && option !== undefined) {
            index = this.addOption(value, option);
        }

        if (index !== -1) {
            this.setValue(this.values[index]);
            this.setSelected(index);

            this.triggerInsideChange();
        } else {
            // It will reset the state if the preferred value not available
            this.options.eq(0).trigger('click');
        }
    };

    FormElementRadio.prototype.setSelected = function (index) {
        this.options.removeClass('n2_field_radio__option--selected');
        this.options.eq(index).addClass('n2_field_radio__option--selected');
    };

    FormElementRadio.prototype.partialSearch = function (text) {
        text = text.replace(/^.*[\\\/]/, '');
        for (var i = 0; i < this.values.length; i++) {
            if (this.values[i].indexOf(text) !== -1) return i;
        }
        return -1;
    };

    FormElementRadio.prototype.addOption = function (value, option) {
        var i = this.values.push(value) - 1;
        option.appendTo(this.parent)
            .on('click', this.click.bind(this));
        this.options = this.options.add(option);
        return i;
    };

    FormElementRadio.prototype.addTabOption = function (value, label) {
        var i = this.values.push(value) - 1;
        var option = $('<div class="n2_field_radio__option">' + label + '</div>')
            .insertAfter(this.options.last())
            .on('click', this.click.bind(this));
        this.options = this.options.add(option);
        return i;
    };

    FormElementRadio.prototype.removeTabOption = function (value) {
        var i = this.values.indexOf(value);
        var option = this.options.eq(i);
        this.options = this.options.not(option);
        option.remove();

        this.values.splice(i, 1);
    };

    FormElementRadio.prototype.moveTab = function (originalIndex, targetIndex) {

    };

    FormElementRadio.prototype.setValue = function (value) {
        this.element.val(value);

        if (this.relatedFields) {
            this.relatedFields.toggleClass('n2_form_element--hidden', this.isOff(value));
        }
    };

    FormElementRadio.prototype.isOff = function (value) {
        return value === '' || value === '0' || value === 0 || value === 'off';
    };

    return FormElementRadio;

});_N2.d('FormRelatedFields', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormRelatedFields(id, options) {

        this.$field = $('#' + id);

        this.options = _Assign({
            relatedFieldsOn: [],
            relatedFieldsOff: []
        }, options);

        this.field = this.$field.data('field');

        if (this.field && this.field.relatedFieldsOff !== undefined) {
            this.fieldChanged = this.fieldChangedCallback;
        } else {
            this.fieldChanged = this.fieldChangedSimple;
        }

        this.$field.on('nextendChange', this.fieldChanged.bind(this));

        this.relatedFieldsOn = $('');
        for (var i = 0; i < this.options.relatedFieldsOn.length; i++) {
            this.relatedFieldsOn = this.relatedFieldsOn.add($('[data-field="' + this.options.relatedFieldsOn[i] + '"]'));
        }

        this.relatedFieldsOff = $('');
        for (var i = 0; i < this.options.relatedFieldsOff.length; i++) {
            this.relatedFieldsOff = this.relatedFieldsOff.add($('[data-field="' + this.options.relatedFieldsOff[i] + '"]'));
        }

        this.fieldChanged();
    }

    FormRelatedFields.prototype.fieldChangedSimple = function () {
        var value = this.$field.val(),
            isOff = this.field.isEmpty(value);

        this.relatedFieldsOn.toggleClass('n2_form_element--hidden', isOff);
        this.relatedFieldsOff.toggleClass('n2_form_element--hidden', !isOff);
    };

    FormRelatedFields.prototype.fieldChangedCallback = function () {
        var isOff = this.field.relatedFieldsOff();
        this.relatedFieldsOn.toggleClass('n2_form_element--hidden', isOff);
        this.relatedFieldsOff.toggleClass('n2_form_element--hidden', !isOff);
    };

    return FormRelatedFields;
});
_N2.d('FormElementRichText', ['$', 'FormElementText'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @constructor
     */
    function FormElementRichText(id) {

        _N2.FormElementText.prototype.constructor.apply(this, arguments);

        this.parent.find('.n2_field_textarea_rich__button')
            .on('click', this.onButtonClick.bind(this));
    }

    FormElementRichText.prototype = Object.create(_N2.FormElementText.prototype);
    FormElementRichText.prototype.constructor = FormElementRichText;

    FormElementRichText.prototype.onButtonClick = function (e) {
        switch ($(e.currentTarget).data('action')) {
            case 'bold':
                this.bold();
                break;
            case 'italic':
                this.italic();
                break;
            case 'link':
                this.link();
                break;
        }
    };

    FormElementRichText.prototype.bold = function () {
        this.wrapText('<b>', '</b>');
    };

    FormElementRichText.prototype.italic = function () {
        this.wrapText('<i>', '</i>');
    };

    FormElementRichText.prototype.link = function () {
        this.wrapText('<a href="#">', '</a>');
    };

    FormElementRichText.prototype.wrapText = function (openTag, closeTag) {
        var textArea = this.element;
        var len = textArea.val().length;
        var start = textArea[0].selectionStart;
        var end = textArea[0].selectionEnd;
        var selectedText = textArea.val().substring(start, end);
        var replacement = openTag + selectedText + closeTag;
        textArea.val(textArea.val().substring(0, start) + replacement + textArea.val().substring(end, len));
        this.triggerOutsideChange();
        this.element.focus();
        textArea[0].selectionEnd = end + openTag.length + closeTag.length;

    };

    return FormElementRichText;
});_N2.d('FormElementSelectIcon', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormElementSelectIcon(id, options) {

        this.options = _Assign({
            relatedFields: [],
            relatedValueFields: [],
        }, options);

        this.element = $('#' + id);

        this.$parent = this.element.parent();

        this.$options = this.$parent.find('.n2_field_select_icon__option')
            .on('click', this.onSelect.bind(this));

        this.relatedFields = false;
        if (this.options.relatedFields.length) {
            this.relatedFields = $('');
            for (var i = 0; i < this.options.relatedFields.length; i++) {
                this.relatedFields = this.relatedFields.add($('[data-field="' + this.options.relatedFields[i] + '"]'));
            }

            this.relatedFields.toggleClass('n2_form_element--hidden', this.isOff(this.element.val()));
        }

        this.relatedValueFields = false;
        if (this.options.relatedValueFields.length) {
            var value = this.element.val();
            this.relatedValueFields = $('');
            for (var i = 0; i < this.options.relatedValueFields.length; i++) {
                var $field;

                for (var j = 0; j < this.options.relatedValueFields[i].field.length; j++) {
                    $field = $('[data-field="' + this.options.relatedValueFields[i].field[j] + '"]')
                        .data('show-values', this.options.relatedValueFields[i].values);

                    $field.toggleClass('n2_form_element--hidden', this.options.relatedValueFields[i].values.indexOf(value) === -1);

                    this.relatedValueFields = this.relatedValueFields.add($field);
                }

            }

        }

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementSelectIcon.prototype = Object.create(_N2.FormElement.prototype);
    FormElementSelectIcon.prototype.constructor = FormElementSelectIcon;

    FormElementSelectIcon.prototype.onSelect = function (e) {
        var $option = $(e.currentTarget);
        if ($option.data('value') !== this.element.val()) {
            this.$options.removeClass('n2_field_select_icon__option--selected');

            $option.addClass('n2_field_select_icon__option--selected');

            this.setHiddenValue($option.data('value'));

            this.triggerOutsideChange();
        }
    };

    FormElementSelectIcon.prototype.insideChange = function (value) {
        this.$options.removeClass('n2_field_select_icon__option--selected');
        this.$options.filter('[data-value="' + value + '"]').addClass('n2_field_select_icon__option--selected');

        this.setHiddenValue(value);

        this.triggerInsideChange();
    };

    FormElementSelectIcon.prototype.setHiddenValue = function (value) {
        this.element.val(value);

        if (this.relatedFields) {
            this.relatedFields.toggleClass('n2_form_element--hidden', this.isOff(value));
        }

        if (this.relatedValueFields) {
            this.relatedValueFields.each(function () {
                var $el = $(this);
                $el.toggleClass('n2_form_element--hidden', $el.data('show-values').indexOf(value) === -1);
            });
        }
    };

    return FormElementSelectIcon;
});_N2.d('FormElementSkin', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param preId
     * @param skins
     * @param fixedMode
     * @constructor
     */
    function FormElementSkin(id, preId, skins, fixedMode) {
        this.element = $('#' + id);

        this.preId = preId;

        this.skins = skins;

        /**
         * @type {_N2.FormElementList}
         */
        this.list = this.element.data('field');

        this.fixedMode = fixedMode;

        this.firstOption = this.list.select.find('option').eq(0);

        this.originalText = this.firstOption.text();

        /**
         * Skin select should only set other values if the input was outsideChange!
         */
        this.element.on('outsideChange', this.onSkinSelect.bind(this));

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementSkin.prototype = Object.create(_N2.FormElement.prototype);
    FormElementSkin.prototype.constructor = FormElementSkin;

    FormElementSkin.prototype.onSkinSelect = function () {
        var skin = this.element.val();
        if (skin !== '0') {
            skin = this.skins[skin].settings;
            for (var k in skin) {
                if (skin.hasOwnProperty(k)) {
                    var el = $('#' + this.preId + k);
                    if (el.length) {
                        var field = el.data('field');
                        field.insideChange(skin[k]);
                    }
                }
            }

            if (!this.fixedMode) {
                this.changeFirstOptionText(n2_('Done'));
                this.list.insideChange('0');
                setTimeout(this.changeFirstOptionText.bind(this, this.originalText), 3000);
            }

        }
    };

    FormElementSkin.prototype.changeFirstOptionText = function (text) {
        this.firstOption.text(text);
    };

    FormElementSkin.prototype.insideChange = function (value) {
        this.element.val(value);
        this.list.insideChange(value);
    };

    return FormElementSkin;
});
_N2.d('FormElementStyleHidden', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param parameters
     * @constructor
     * @augments {_N2.FormElement}
     */
    function FormElementStyleHidden(id, parameters) {
        this.element = $('#' + id);

        this.parameters = _Assign({
            mode: '',
            label: ''
        }, parameters);

        _N2.FormElement.prototype.constructor.call(this);

    }

    FormElementStyleHidden.prototype = Object.create(_N2.FormElement.prototype);
    FormElementStyleHidden.prototype.constructor = FormElementStyleHidden;

    FormElementStyleHidden.prototype.getLabel = function () {
        return this.parameters.label;
    };

    FormElementStyleHidden.prototype.save = function (e, value) {

        this.val(value);
    };

    FormElementStyleHidden.prototype.val = function (value) {
        this.element.val(value);

        this.triggerOutsideChange();
    };

    FormElementStyleHidden.prototype.insideChange = function (value) {
        this.element.val(value);

        this.triggerInsideChange();
    };

    /**
     *
     * @returns {_N2.CssInjection}
     */
    FormElementStyleHidden.prototype.renderStyle = function () {

        return _N2.CSSRendererStyle.addCSS('', _N2.CSSRendererStyle.getClass(), this.element.val(), _N2.CSSRendererStyle.rendererModes[this.parameters.mode]);
    };

    return FormElementStyleHidden;
});_N2.d('FormElementStyle', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param parameters
     * @constructor
     * @augments {_N2.FormElementText}
     */
    function FormElementStyle(id, parameters) {
        this.element = $('#' + id);

        this.parent = this.element.parent();

        this.parameters = _Assign({
            mode: '',
            label: '',
            font: '',
            font2: '',
            style2: '',
            preview: ''
        }, parameters);

        _N2.FormElement.prototype.constructor.call(this);

        this.parent.on('click', this.show.bind(this));

    }

    FormElementStyle.prototype = Object.create(_N2.FormElement.prototype);
    FormElementStyle.prototype.constructor = FormElementStyle;

    FormElementStyle.prototype.getLabel = function () {
        return this.parameters.label;
    };

    FormElementStyle.prototype.show = function (e) {
        e.preventDefault();
        if (this.parameters.font !== '') {
            _N2._styleManager.setConnectedFont(this.parameters.font);
        }
        if (this.parameters.font2 !== '') {
            _N2._styleManager.setConnectedFont2(this.parameters.font2);
        }
        if (this.parameters.style2 !== '') {
            _N2._styleManager.setConnectedStyle(this.parameters.style2);
        }

        _N2._styleManager.setFieldID(this.element.attr('id'));

        _N2._styleManager.show(this.element.val(), this.save.bind(this), {
            previewMode: this.parameters.mode,
            previewHTML: this.parameters.preview
        });
    };

    FormElementStyle.prototype.save = function (e, value) {

        this.val(value);
    };

    FormElementStyle.prototype.val = function (value) {
        this.element.val(value);

        this.triggerOutsideChange();
    };

    FormElementStyle.prototype.insideChange = function (value) {
        this.element.val(value);

        this.triggerInsideChange();
    };

    /**
     *
     * @returns {_N2.CssInjection}
     */
    FormElementStyle.prototype.renderStyle = function () {

        return _N2.CSSRendererStyle.addCSS('', _N2.CSSRendererStyle.getClass(), this.element.val(), _N2.CSSRendererStyle.rendererModes[this.parameters.mode]);
    };

    return FormElementStyle;

});_N2.d('FormElementSubformIcon', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param ajaxUrl
     * @param targetSelector
     * @param originalValue
     * @constructor
     */
    function FormElementSubformIcon(id, ajaxUrl, targetSelector, originalValue) {
        this.id = id;

        this.ajaxUrl = ajaxUrl;

        this.element = $('#' + id)
            .data('field', this);

        this.$parent = this.element.parent();

        this.$targetContainer = $('#' + targetSelector);

        this.originalValue = originalValue;

        this.form = this.element.closest('form');

        this.$options = this.$parent.find('.n2_field_subform_icon__option')
            .on('click', this.selectOption.bind(this));

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementSubformIcon.prototype = Object.create(_N2.FormElement.prototype);
    FormElementSubformIcon.prototype.constructor = FormElementSubformIcon;

    FormElementSubformIcon.prototype.selectOption = function (e) {
        var $option = $(e.currentTarget),
            value = $option.data('value');

        this.$options.removeClass('n2_field_subform_icon__option--selected');
        $option.addClass('n2_field_subform_icon__option--selected');

        this.element.val(value);
        this.triggerOutsideChange();

        this.loadSubform(value);
    };

    FormElementSubformIcon.prototype.loadSubform = function (value) {
        var data = {
            values: [],
            value: value
        };

        if (value === this.originalValue) {
            data.values = this.form.data('form').values;
        }

        _N2.AjaxHelper.ajax({
            type: "POST",
            url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl),
            data: data,
            dataType: 'json'
        }).done(this.load.bind(this));
    };

    FormElementSubformIcon.prototype.load = function (response) {
        this.$targetContainer.html(response.data.html);
        eval(response.data.scripts);

        _N2.FormTipManager.add(this.$targetContainer);
    };

    return FormElementSubformIcon;
});
_N2.d('FormElementSubformImage', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormElementSubformImage(id, options) {

        this.element = $('#' + id);

        this.options = $('#' + options).find('.n2-subform-image-option');

        this.subform = this.element.data('field');

        this.active = this.getIndex(this.options.filter('.n2-active').get(0));

        for (var i = 0; i < this.options.length; i++) {
            this.options.eq(i).on('click', this.selectOption.bind(this));
        }

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementSubformImage.prototype = Object.create(_N2.FormElement.prototype);
    FormElementSubformImage.prototype.constructor = FormElementSubformImage;


    FormElementSubformImage.prototype.selectOption = function (e) {
        var index = this.getIndex(e.currentTarget);
        if (index != this.active) {

            this.options.eq(index).addClass('n2-active');
            this.options.eq(this.active).removeClass('n2-active');

            this.active = index;

            var value = this.subform.list.select.find('option').eq(index).val();
            this.subform.list.insideChange(value);
        }
    };

    FormElementSubformImage.prototype.getIndex = function (option) {
        return this.options.indexOf(option);
    };

    return FormElementSubformImage;
});_N2.d('FormElementTab', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     *
     * @param id
     * @param relatedValueFields
     * @constructor
     */
    function FormElementTab(id, relatedValueFields) {

        this.parent = $('#' + id + '_tab');

        this.parent.find('.n2_field_tab__option').each((function (i, el) {
            $(el).on('click', (function (e) {
                this.onClick($(e.currentTarget));
            }).bind(this));
        }).bind(this));

        this.element = $('#' + id).on('change', this.onHiddenChange.bind(this));

        this.relatedValueFields = false;
        if (relatedValueFields.length) {
            var value = this.element.val();
            this.relatedValueFields = $('');
            for (var i = 0; i < relatedValueFields.length; i++) {
                var $field;
                for (var j = 0; j < relatedValueFields[i].field.length; j++) {
                    $field = $('[data-field="' + relatedValueFields[i].field[j] + '"]')
                        .data('show-values', relatedValueFields[i].values);

                    $field.toggleClass('n2_form_element--hidden', relatedValueFields[i].values.indexOf(value) === -1);

                    this.relatedValueFields = this.relatedValueFields.add($field);
                }
            }

        }

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementTab.prototype = Object.create(_N2.FormElement.prototype);
    FormElementTab.prototype.constructor = FormElementTab;

    FormElementTab.prototype.onHiddenChange = function () {
        var value = this.element.val();
        if (value) {
            this.insideChange(value);
        }
    };

    FormElementTab.prototype.onClick = function ($element) {
        if (!$element.hasClass('n2_field_tab__option--selected')) {
            this.parent.find('.n2_field_tab__option--selected').removeClass('n2_field_tab__option--selected');
            $element.addClass('n2_field_tab__option--selected');

            this.setHiddenValue($element.attr('data-ssoption'));

            this.triggerOutsideChange();
        }
    };

    FormElementTab.prototype.insideChange = function (value) {
        this.setHiddenValue(value);

        this.triggerInsideChange();
    };

    FormElementTab.prototype.setHiddenValue = function (value) {
        this.element.val(value);

        if (this.relatedValueFields) {
            this.relatedValueFields.each(function () {
                var $el = $(this);
                $el.toggleClass('n2_form_element--hidden', $el.data('show-values').indexOf(value) === -1);
            });
        }
    };

    FormElementTab.prototype.setOptions = function (options, $container, defaultTab, relatedValueFields) {
        if (!relatedValueFields) {
            relatedValueFields = [];
        }

        $container.html('');
        for (var i in options) {
            var $option = $('<div class="n2_field_tab__option" data-ssoption="' + i + '">' + options[i] + '</div>');
            if (defaultTab === i) {
                $option.addClass('n2_field_tab__option--selected');
            }
            $container.append($option);
            new FormElementTab($container.attr('id').replace('_tab', ''), relatedValueFields);
        }
    };

    return FormElementTab;

});_N2.d('FormElementText', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @constructor
     */
    function FormElementText(id) {
        this.element = $('#' + id).on({
            focus: this._focus.bind(this),
            blur: this._blur.bind(this),
            change: this.change.bind(this)
        });

        this.tagName = this.element.prop('tagName');

        this.parent = this.element.parent();

        _N2.FormElement.prototype.constructor.call(this);

        this.updateIsEmpty();
    }


    FormElementText.prototype = Object.create(_N2.FormElement.prototype);
    FormElementText.prototype.constructor = FormElementText;


    FormElementText.prototype._focus = function () {
        this.parent.addClass('n2_field_text--focus');

        if (this.tagName !== 'TEXTAREA') {
            this.element.on('keypress.n2-text', (function (e) {
                if (e.code === 'Enter') {
                    this.element.off('keypress.n2-text');
                    this.element.trigger('blur');
                }
            }).bind(this));
        }
    };

    FormElementText.prototype._blur = function () {
        this.parent.removeClass('n2_field_text--focus');
    };

    FormElementText.prototype.change = function () {
        this.updateIsEmpty();

        this.triggerOutsideChange();
    };

    FormElementText.prototype.insideChange = function (value) {
        this.element.val(value);

        this.updateIsEmpty();

        this.triggerInsideChange();
    };

    FormElementText.prototype.updateIsEmpty = function () {
        this.parent.toggleClass('n2_field_text--empty', this.isEmpty(this.element.val()));
    };

    FormElementText.prototype.isEmpty = function (value) {
        return value === '';
    };

    FormElementText.prototype.focus = function (shouldOpen) {
        if (this.connectedField) {
            this.connectedField.focus(shouldOpen);
        } else if (shouldOpen) {
            this.element.focus().select();
        }
    };

    return FormElementText;
});_N2.d('FormElementUnits', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param values
     * @constructor
     */
    function FormElementUnits(id, values) {

        this.element = $('#' + id);
        this.$parent = this.element.parent();

        this.options = this.$parent.find('.n2_field_unit__unit');
        this.currentUnit = this.$parent.find('.n2_field_unit__current_unit');

        this.values = values;

        for (var i = 0; i < this.options.length; i++) {
            this.options.eq(i).on('click', this.switch.bind(this, i));
        }

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementUnits.prototype = Object.create(_N2.FormElement.prototype);
    FormElementUnits.prototype.constructor = FormElementUnits;


    FormElementUnits.prototype.switch = function (i, e) {
        this.element.val(this.values[i]);
        this.setSelected(i);

        this.triggerOutsideChange();
    };

    FormElementUnits.prototype.insideChange = function (value) {
        var i = this.values.indexOf(value);

        this.element.val(this.values[i]);
        this.setSelected(i);

        this.triggerInsideChange();
    };

    FormElementUnits.prototype.setSelected = function (i) {
        this.currentUnit.html(this.options.eq(i).html());
    };

    return FormElementUnits;
});
_N2.d('FormElementUpload', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     *
     * @param id
     * @constructor
     */
    function FormElementUpload(id) {

        this.element = $('#' + id).on('change', this.updateName.bind(this));
        this.parent = this.element.parent();
        this.$name = this.parent.find('.n2_field_chooser__label');

        this.updateName(this.element.val());

        _N2.FormElement.prototype.constructor.call(this);
    }

    FormElementUpload.prototype = Object.create(_N2.FormElement.prototype);
    FormElementUpload.prototype.constructor = FormElementUpload;


    FormElementUpload.prototype.insideChange = function (value) {
        this.updateName();

        this.triggerInsideChange();
    };

    FormElementUpload.prototype.updateName = function () {
        var value = this.element[0].value;

        this.parent.toggleClass('n2_field_chooser--empty', value === '');

        if (!this.isEmpty(value)) {
            var fileNameWithExt = value.split('\\').pop();
            this.$name.text(fileNameWithExt);

        } else {
            this.$name.text(n2_('No file selected.'));
        }
    };

    FormElementUpload.prototype.isEmpty = function (value) {

        return value === '';
    };


    return FormElementUpload;

});_N2.d('FormElementUrl', ['$', 'FormElementText'], function () {

        var modal = false,
            lastValue = '',
            tabs = [];

        /**
         * @memberOf _N2
         *
         * @param id
         * @param parameters
         * @constructor
         * @augments _N2.FormElementText
         */
        function FormElementUrl(id, parameters) {

            _N2.FormElementText.prototype.constructor.call(this, id);

            this.parameters = parameters;

            this.button = this.parent.find('.n2_field_text__choose')
                .on('click', this.open.bind(this));

            this.element.siblings('.n2_field_text__clear')
                .on('click', this.clear.bind(this));
        }

        FormElementUrl.prototype = Object.create(_N2.FormElementText.prototype);
        FormElementUrl.prototype.constructor = FormElementUrl;

        FormElementUrl.prototype.isEmpty = function (value) {

            return value === '' || value === '#';
        };

        FormElementUrl.prototype.clear = function (e) {
            e.preventDefault();
            this.val('#');
        };

        FormElementUrl.prototype.val = function (value) {
            this.element.val(value);

            this.change();
        };

        FormElementUrl.prototype.open = function (e) {
            e.preventDefault();
            lastValue = this.element.val();
            _N2.ModalUrl.show(this.element.val(), this.parameters.url, (function (value) {
                this.val(value);
            }).bind(this));
        };

        return FormElementUrl;
    }
);/**
 * jquery.unique-element-id.js
 *
 * A simple jQuery plugin to get a unique ID for
 * any HTML element
 *
 * Usage:
 *    $('some_element_selector').uid();
 *
 * by Jamie Rumbelow <jamie@jamierumbelow.net>
 * http://jamieonsoftware.com
 * Copyright (c)2011 Jamie Rumbelow
 *
 * Licensed under the MIT license (http://www.opensource.org/licenses/MIT)
 */

_N2.r('$', function () {
    var $ = _N2.$;

    /**
     * Generate a new unqiue ID
     */
    function generateUniqueId(prefix) {

        // Return a unique ID
        return prefix + Math.floor((1 + Math.random()) * 0x1000000000000)
            .toString(16);
    }

    /**
     * Get a unique ID for an element, ensuring that the
     * element has an id="" attribute
     */
    $.fn.uid = function (prefix) {
        var id = null;
        prefix = prefix || "n";
        do {
            id = generateUniqueId(prefix);
        } while ($('#' + id).length > 0);
        return id;
    };

    $.fn.generateUniqueClass = function (prefix) {
        var id = null;
        prefix = prefix || "n";
        do {
            id = generateUniqueId(prefix);
        } while ($('.' + id).length > 0);
        return id;
    };
});_N2.d('UIAutocomplete', ['$', 'UIWidgetBase'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments {_N2.UIWidgetBase}
     */
    function UIAutocomplete($element, options) {

        this.isRendered = false;

        this.element = $element;

        this.widgetName = this.widgetName || 'UIAutocomplete';
        this.widgetEventPrefix = "autocomplete";

        this.isActive = false;
        this.allowBlur = true;

        this.options = _Assign({
            appendTo: 'body',
            source: null,
            select: null,
            positionTo: 'self',
            x: 0,
            y: 0
        }, this.options, options);

        _N2.UIWidgetBase.prototype.constructor.apply(this, arguments);

        this.element.on({
            focus: this.focus.bind(this),
            blur: this.blur.bind(this)
        });

        if (this.options.positionTo === 'self') {
            this.positionTo = this.element;
        } else {
            this.positionTo = this.element.closest(this.options.positionTo);
        }
    }

    UIAutocomplete.prototype = Object.create(_N2.UIWidgetBase.prototype);
    UIAutocomplete.prototype.constructor = UIAutocomplete;

    UIAutocomplete.prototype.focus = function (e) {
        if (this.isActive === false) {
            this.showList(e);
            this.element.on('click.' + this.widgetEventPrefix, this.showList.bind(this));

            this.isActive = true;
        }
    };

    UIAutocomplete.prototype.showList = function (e) {
        if (typeof this.options.appendTo === 'function') {
            this.options.appendTo = this.options.appendTo.call(window);
        } else {
            this.options.appendTo = $(this.options.appendTo);
        }
        var $list = this.getList().appendTo(this.options.appendTo);

        var appendToOffset = {
                left: 0,
                top: 0
            },
            offset = this.positionTo.offset(),
            scrollbarWidth = 0;

        if (!this.options.appendTo.is($('body'))) {
            appendToOffset = this.options.appendTo.offset();
            appendToOffset.top -= this.options.appendTo.scrollTop();

            $list.css('height', '');
            var listHeight = $list.height();

            var paneRect = this.options.appendTo[0].getBoundingClientRect(),
                fieldRect = this.positionTo[0].getBoundingClientRect(),
                newListHeight = Math.min(paneRect.top + paneRect.height - fieldRect.top - fieldRect.height - 10, listHeight);

            if (newListHeight < 100 && newListHeight < listHeight) {
                newListHeight = Math.min(fieldRect.top - paneRect.top - 10, listHeight);
                appendToOffset.top = appendToOffset.top + fieldRect.height + newListHeight;
            }

            $list.css('height', newListHeight + 'px');

            if (n2const.rtl.isRtl) {
                scrollbarWidth = paneRect.width - this.options.appendTo[0].clientWidth;
            }
        }

        $list.css({
            left: offset.left - appendToOffset.left + this.options.x - scrollbarWidth + 'px',
            top: offset.top + this.positionTo.outerHeight() - appendToOffset.top + this.options.y + 'px',
            minWidth: this.element.parent().outerWidth() + 'px'
        });

        /**
         * If scrollbar dragged with mouse prevent the list to disappear
         */
        $list.off('.' + this.widgetEventPrefix)
            .on('mousedown.' + this.widgetEventPrefix, (function (e) {
                if ($(e.target).is($list)) {
                    this.element.parent().addClass('focus2');
                    this.allowBlur = false;
                }
            }).bind(this))
            .on('mouseup.' + this.widgetEventPrefix, (function (e) {
                if ($(e.target).is($list)) {
                    this.allowBlur = true;
                    this.element.focus();
                    this.element.parent().removeClass('focus2');
                }
            }).bind(this));
    };

    UIAutocomplete.prototype.blur = function (e) {
        if (this.allowBlur) {
            this.hide();
            this.element.off('.' + this.widgetEventPrefix);
        }
    };

    UIAutocomplete.prototype.hide = function () {
        this.$list.detach();
        this.isActive = false;
    };

    UIAutocomplete.prototype.getList = function () {
        if (!this.isRendered) {
            this.$list = $('<div class="n2_autocomplete_options"></div>')
                .attr({
                    "unselectable": "on"
                })
                .on({
                    'wheel': function (e) {
                        e.stopPropagation();
                    }
                });
            var options = this.options.source.call(this, this.ui());
            for (var i = 0; i < options.length; i++) {
                $('<div class="n2_autocomplete_option" tabindex="-1">' + options[i] + '</div>')
                    .on({
                        mousedown: function (e) {
                            e.preventDefault();
                        },
                        click: (function (value, e) {
                            this._trigger('select', e, {
                                value: value
                            });
                            this.hide();
                        }).bind(this, options[i])
                    })
                    .appendTo(this.$list);
            }
            this.isRendered = true;
        }

        return this.$list;
    };

    UIAutocomplete.prototype.ui = function () {
        return {};
    };

    return UIAutocomplete;
});_N2.d('UIDraggableBar', ['$', 'UIMouse'], function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param $element
     * @param options
     */
    function UIDraggableBar($element, options) {
        this.element = $element;

        this.widgetName = this.widgetName || 'UIDraggable';
        this.widgetEventPrefix = "drag";

        this.options = _Assign({
            // Callbacks
            drag: null,
            start: null,
            stop: null
        }, this.options, options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this._mouseInit();
    }

    UIDraggableBar.prototype = Object.create(_N2.UIMouse.prototype);
    UIDraggableBar.prototype.constructor = UIDraggableBar;

    UIDraggableBar.prototype._mouseStart = function (event) {

        this.currentData = this.originalData = {
            margin: parseInt(this.element.css('marginLeft'))
        };
        this.originalMousePosition = {left: event.pageX};

        this.element.addClass("nui-draggable-dragging");

        this._trigger("start", event, this.ui());

        this._mouseDrag(event);
        return true;
    };

    UIDraggableBar.prototype._mouseDrag = function (event) {
        var dx = (event.pageX - this.originalMousePosition.left) || 0;
        this.currentData = {};

        this.currentData.margin = Math.max(0, this.originalData.margin + dx);

        this._trigger("drag", event, this.ui());

        this.element.css('marginLeft', this.currentData.margin + 'px');

        return true;
    };


    UIDraggableBar.prototype._mouseStop = function (event) {

        this._trigger("stop", event, this.ui());

        return true;
    };

    UIDraggableBar.prototype.ui = function () {
        return {
            currentData: this.currentData
        };
    };

    return UIDraggableBar;
});_N2.d('UIDraggableDelay', ['$', 'UIMouse'], function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param $element
     * @param options
     */
    function UIDraggableDelay($element, options) {
        this.element = $element;

        this.widgetName = this.widgetName || 'UIDraggable';
        this.widgetEventPrefix = "drag";

        this.options = _Assign({
            // Callbacks
            drag: null,
            start: null,
            stop: null
        }, this.options, options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this._mouseInit();
    }

    UIDraggableDelay.prototype = Object.create(_N2.UIMouse.prototype);
    UIDraggableDelay.prototype.constructor = UIDraggableDelay;

    UIDraggableDelay.prototype._mouseStart = function (event) {

        this.currentData = this.originalData = {
            width: parseInt(this.element.width())
        };
        this.originalMousePosition = {left: event.pageX};

        this.element.addClass("nui-draggable-dragging");

        this._trigger("start", event, this.ui());

        this._mouseDrag(event);
        return true;
    };

    UIDraggableDelay.prototype._mouseDrag = function (event) {
        var dx = (event.pageX - this.originalMousePosition.left) || 0;
        this.currentData = {};

        this.currentData.width = Math.max(0, this.originalData.width + dx);

        this._trigger("drag", event, this.ui());

        this.element.width(this.currentData.width + 'px');

        return true;
    };


    UIDraggableDelay.prototype._mouseStop = function (event) {

        this._trigger("stop", event, this.ui());

        return true;
    };

    UIDraggableDelay.prototype.ui = function () {
        return {
            currentData: this.currentData
        };
    };

    return UIDraggableDelay;
});_N2.d('UIDraggable', ['$', 'UIMouse'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments {_N2.UIMouse}
     */
    function UIDraggable($element, options) {
        this.element = $element;

        this.widgetName = this.widgetName || 'UIDraggable';
        this.widgetEventPrefix = "drag";

        this.options = _AssignRecursive({
            addClasses: true,
            appendTo: "parent",
            axis: false,
            containment: false,
            cursor: "auto",
            cursorAt: false,
            handle: false,
            helper: "original",
            scroll: true,
            scrollSensitivity: 20,
            scrollSpeed: 20,
            min: {top: false, left: false},
            // Callbacks
            drag: null,
            start: null,
            stop: null
        }, this.options, options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UIDraggable.prototype = Object.create(_N2.UIMouse.prototype);
    UIDraggable.prototype.constructor = UIDraggable;

    UIDraggable.prototype.create = function () {

        if (this.options.helper === "original") {
            this._setPositionRelative();
        }
        if (this.options.addClasses) {
            this.element.addClass("nui-draggable");
        }
        this._setHandleClassName();

        this._mouseInit();
    };

    UIDraggable.prototype._setPositionRelative = function () {
        if (!(/^(?:r|a|f)/).test(this.element.css("position"))) {
            this.element[0].style.position = "relative";
        }
    };

    UIDraggable.prototype._getHandle = function (event) {
        return this.options.handle ?
            !!$(event.target).closest(this.element.find(this.options.handle)).length :
            true;
    };

    UIDraggable.prototype._setHandleClassName = function () {
        this.handleElement = this.options.handle ?
            this.element.find(this.options.handle) : this.element;
        this.handleElement.addClass("nui-draggable-handle");
    };

    UIDraggable.prototype._mouseCapture = function (event) {
        var o = this.options;
        // Among others, prevent a drag on a resizable-handle
        if (this.helper || o.disabled ||
            $(event.target).closest(".nui_resize_absolute").length > 0) {
            return false;
        }

        //Quit if we're not on a valid handle
        this.handle = this._getHandle(event);
        if (!this.handle) {
            return false;
        }

        this._blurActiveElement(event);

        return true;
    };

    UIDraggable.prototype.cancel = function () {

        if (this.helper.is(".nui-draggable-dragging")) {
            this._mouseUp(new $.Event("mouseup", {target: this.element[0]}));
        } else {
            this._clear();
        }

        return this;

    };

    $.fn.nuiScrollParent = function (includeHidden) {
        var position = this.css("position"),
            excludeStaticParent = position === "absolute",
            overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
            scrollParent = this.parents().filter(function () {
                var parent = $(this);
                if (excludeStaticParent && parent.css("position") === "static") {
                    return false;
                }
                return overflowRegex.test(parent.css("overflow") + parent.css("overflow-y") +
                    parent.css("overflow-x"));
            }).eq(0);

        return position === "fixed" || !scrollParent.length ?
            $(this[0].ownerDocument || document) :
            scrollParent;
    };

    UIDraggable.prototype._mouseStart = function (event) {
        var o = this.options;
        //Create and append the visible helper
        this.helper = this._createHelper(event);

        this.helper.addClass("nui-draggable-dragging");

        //Cache the helper size
        this._cacheHelperProportions();

        /*
         * - Position generation -
         * This block generates everything position related - it's the core of draggables.
         */

        //Cache the margins of the original element
        this._cacheMargins();

        //Store the helper's css position
        this.cssPosition = this.helper.css("position");
        this.scrollParent = this.helper.nuiScrollParent(true);
        this.offsetParent = this.helper.offsetParent();
        this.hasFixedAncestor = this.helper.parents().filter(function () {
            return $(this).css("position") === "fixed";
        }).length > 0;

        //The element's absolute position on the page minus margins
        this.positionAbs = this.element.offset();
        this._refreshOffsets(event);

        //Generate the original position
        this.originalPosition = this.position = this._generatePosition(event, false);
        this.originalPageX = event.pageX;
        this.originalPageY = event.pageY;

        //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
        (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));

        //Set a containment if given in the options
        this._setContainment();

        //Trigger event + callbacks
        if (this._trigger("start", event) === false) {
            this._clear();
            return false;
        }

        //Recache the helper size
        this._cacheHelperProportions();


        // Execute the drag once - this causes the helper not to be visible before getting its
        // correct position
        this._mouseDrag(event, true);

        return true;
    };

    UIDraggable.prototype._mouseDrag = function (event, noPropagation) {

        // reset any necessary cached properties (see #5009)
        if (this.hasFixedAncestor) {
            this.offset.parent = this._getParentOffset();
        }

        //Compute the helpers position
        this.position = this._generatePosition(event, true);
        this.positionAbs = this._convertPositionTo("absolute");

        //Call plugins and callbacks and use the resulting position if something is returned
        if (!noPropagation) {
            var ui = this._uiHash();
            if (this._trigger("drag", event, ui) === false) {
                this._mouseUp(new $.Event("mouseup", event));
                return false;
            }
            this.position = ui.position;
        }

        if (this.options.min.top !== false) {
            this.position.top = Math.max(this.position.top, this.options.min.top);
        }

        if (this.options.min.left !== false) {
            this.position.left = Math.max(this.position.left, this.options.min.left);
        }

        this.helper[0].style.left = this.position.left + "px";
        this.helper[0].style.top = this.position.top + "px";

        return false;
    };

    UIDraggable.prototype._mouseStop = function (event) {

        if (this._trigger("stop", event) !== false) {
            this._clear();
        }

        return false;
    };

    UIDraggable.prototype._mouseUp = function (event) {

        // Only need to focus if the event occurred on the draggable itself, see #10527
        if (this.handleElement.is(event.target)) {

            // The interaction is over; whether or not the click resulted in a drag,
            // focus the element
            this.element.trigger("focus");
        }

        return _N2.UIMouse.prototype._mouseUp.call(this, event);
    };

    UIDraggable.prototype._trigger = function (type, event, ui) {
        ui = ui || this._uiHash();

        return _N2.UIWidgetBase.prototype._trigger.call(this, type, event, ui);
    };

    UIDraggable.prototype._uiHash = function () {
        return {
            helper: this.helper,
            position: this.position,
            originalPosition: this.originalPosition
        };
    };

    UIDraggable.prototype._createHelper = function (event) {

        var o = this.options,
            helperIsFunction = typeof o.helper === "function",
            helper = helperIsFunction ?
                $(o.helper.apply(this.element[0], [event])) :
                (o.helper === "clone" ?
                    this.element.clone().removeAttr("id") :
                    this.element);

        if (!helper.parents("body").length) {
            helper.appendTo((o.appendTo === "parent" ?
                this.element[0].parentNode :
                o.appendTo));
        }

        // Http://bugs.jqueryui.com/ticket/9446
        // a helper function can return the original element
        // which wouldn't have been set to relative in _create
        if (helperIsFunction && helper[0] === this.element[0]) {
            this._setPositionRelative();
        }

        if (helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
            helper.css("position", "absolute");
        }

        return helper;

    };

    UIDraggable.prototype._cacheHelperProportions = function () {
        this.helperProportions = {
            width: this.helper.outerWidth(),
            height: this.helper.outerHeight()
        };
    };

    UIDraggable.prototype._cacheMargins = function () {
        this.margins = {
            left: (parseInt(this.element.css("marginLeft"), 10) || 0),
            top: (parseInt(this.element.css("marginTop"), 10) || 0),
            right: (parseInt(this.element.css("marginRight"), 10) || 0),
            bottom: (parseInt(this.element.css("marginBottom"), 10) || 0)
        };
    };

    UIDraggable.prototype._refreshOffsets = function (event) {
        this.offset = {
            top: this.positionAbs.top - this.margins.top,
            left: this.positionAbs.left - this.margins.left,
            scroll: false,
            parent: this._getParentOffset(),
            relative: this._getRelativeOffset()
        };

        this.offset.click = {
            left: event.pageX - this.offset.left,
            top: event.pageY - this.offset.top
        };
    };

    UIDraggable.prototype._getParentOffset = function () {

        //Get the offsetParent and cache its position
        var po = this.offsetParent.offset(),
            document = this.document[0];

        // This is a special case where we need to modify a offset calculated on start, since the
        // following happened:
        // 1. The position of the helper is absolute, so it's position is calculated based on the
        // next positioned parent
        // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
        // the document, which means that the scroll is included in the initial calculation of the
        // offset of the parent, and never recalculated upon drag
        if (this.cssPosition === "absolute" && this.scrollParent[0] !== document &&
            $.contains(this.scrollParent[0], this.offsetParent[0])) {
            po.left += this.scrollParent.scrollLeft();
            po.top += this.scrollParent.scrollTop();
        }

        if (this._isRootNode(this.offsetParent[0])) {
            po = {top: 0, left: 0};
        }

        return {
            top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"), 10) || 0),
            left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"), 10) || 0)
        };

    };

    UIDraggable.prototype._getRelativeOffset = function () {
        if (this.cssPosition !== "relative") {
            return {top: 0, left: 0};
        }

        var p = this.element.position(),
            scrollIsRootNode = this._isRootNode(this.scrollParent[0]);

        return {
            top: p.top - (parseInt(this.helper.css("top"), 10) || 0) +
                (!scrollIsRootNode ? this.scrollParent.scrollTop() : 0),
            left: p.left - (parseInt(this.helper.css("left"), 10) || 0) +
                (!scrollIsRootNode ? this.scrollParent.scrollLeft() : 0)
        };

    };

    UIDraggable.prototype._convertPositionTo = function (d, pos) {

        if (!pos) {
            pos = this.position;
        }

        var mod = d === "absolute" ? 1 : -1,
            scrollIsRootNode = this._isRootNode(this.scrollParent[0]);

        return {
            top: (

                // The absolute mouse position
                pos.top +

                // Only for relative positioned nodes: Relative offset from element to offset parent
                this.offset.relative.top * mod +

                // The offsetParent's offset without borders (offset + border)
                this.offset.parent.top * mod -
                ((this.cssPosition === "fixed" ?
                    -this.offset.scroll.top :
                    (scrollIsRootNode ? 0 : this.offset.scroll.top)) * mod)
            ),
            left: (

                // The absolute mouse position
                pos.left +

                // Only for relative positioned nodes: Relative offset from element to offset parent
                this.offset.relative.left * mod +

                // The offsetParent's offset without borders (offset + border)
                this.offset.parent.left * mod -
                ((this.cssPosition === "fixed" ?
                    -this.offset.scroll.left :
                    (scrollIsRootNode ? 0 : this.offset.scroll.left)) * mod)
            )
        };

    };

    UIDraggable.prototype._setContainment = function () {

        var isUserScrollable, c, ce,
            o = this.options,
            document = this.document[0];

        this.relativeContainer = null;

        if (!o.containment) {
            this.containment = null;
            return;
        }

        if (o.containment === "window") {
            this.containment = [
                _getScrollLeft() - this.offset.relative.left - this.offset.parent.left,
                _getScrollTop() - this.offset.relative.top - this.offset.parent.top,
                _getScrollLeft() + window.innerWidth -
                this.helperProportions.width - this.margins.left,
                _getScrollTop() +
                window.innerHeight -
                this.helperProportions.height - this.margins.top
            ];
            return;
        }

        if (o.containment === "document") {
            this.containment = [
                0,
                0,
                body.scrollWidth - this.helperProportions.width - this.margins.left,
                body.scrollHeight - this.helperProportions.height - this.margins.top
            ];
            return;
        }

        if (o.containment.constructor === Array) {
            this.containment = o.containment;
            return;
        }

        if (o.containment === "parent") {
            o.containment = this.helper[0].parentNode;
        }

        c = $(o.containment);
        ce = c[0];

        if (!ce) {
            return;
        }

        isUserScrollable = /(scroll|auto)/.test(c.css("overflow"));

        this.containment = [
            (parseInt(c.css("borderLeftWidth"), 10) || 0) +
            (parseInt(c.css("paddingLeft"), 10) || 0),
            (parseInt(c.css("borderTopWidth"), 10) || 0) +
            (parseInt(c.css("paddingTop"), 10) || 0),
            (isUserScrollable ? Math.max(ce.scrollWidth, ce.offsetWidth) : ce.offsetWidth) -
            (parseInt(c.css("borderRightWidth"), 10) || 0) -
            (parseInt(c.css("paddingRight"), 10) || 0) -
            this.helperProportions.width -
            this.margins.left -
            this.margins.right,
            (isUserScrollable ? Math.max(ce.scrollHeight, ce.offsetHeight) : ce.offsetHeight) -
            (parseInt(c.css("borderBottomWidth"), 10) || 0) -
            (parseInt(c.css("paddingBottom"), 10) || 0) -
            this.helperProportions.height -
            this.margins.top -
            this.margins.bottom
        ];
        this.relativeContainer = c;
    };


    UIDraggable.prototype._adjustOffsetFromHelper = function (obj) {
        if (typeof obj === "string") {
            obj = obj.split(" ");
        }
        if (Array.isArray(obj)) {
            obj = {left: +obj[0], top: +obj[1] || 0};
        }
        if ("left" in obj) {
            this.offset.click.left = obj.left + this.margins.left;
        }
        if ("right" in obj) {
            this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
        }
        if ("top" in obj) {
            this.offset.click.top = obj.top + this.margins.top;
        }
        if ("bottom" in obj) {
            this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
        }
    };

    UIDraggable.prototype._isRootNode = function (element) {
        return (/(html|body)/i).test(element.tagName) || element === this.document[0];
    };

    UIDraggable.prototype._generatePosition = function (event, constrainPosition) {

        var containment, co,
            o = this.options,
            scrollIsRootNode = this._isRootNode(this.scrollParent[0]),
            pageX = event.pageX,
            pageY = event.pageY;

        // Cache the scroll
        if (!scrollIsRootNode || !this.offset.scroll) {
            this.offset.scroll = {
                top: this.scrollParent.scrollTop(),
                left: this.scrollParent.scrollLeft()
            };
        }

        /*
         * - Position constraining -
         * Constrain the position to containment.
         */

        // If we are not dragging yet, we won't check for options
        if (constrainPosition) {
            if (this.containment) {
                if (this.relativeContainer) {
                    co = this.relativeContainer.offset();
                    containment = [
                        this.containment[0] + co.left,
                        this.containment[1] + co.top,
                        this.containment[2] + co.left,
                        this.containment[3] + co.top
                    ];
                } else {
                    containment = this.containment;
                }

                if (event.pageX - this.offset.click.left < containment[0]) {
                    pageX = containment[0] + this.offset.click.left;
                }
                if (event.pageY - this.offset.click.top < containment[1]) {
                    pageY = containment[1] + this.offset.click.top;
                }
                if (event.pageX - this.offset.click.left > containment[2]) {
                    pageX = containment[2] + this.offset.click.left;
                }
                if (event.pageY - this.offset.click.top > containment[3]) {
                    pageY = containment[3] + this.offset.click.top;
                }
            }

            if (o.axis === "y") {
                pageX = this.originalPageX;
            }

            if (o.axis === "x") {
                pageY = this.originalPageY;
            }
        }

        return {
            top: (

                // The absolute mouse position
                pageY -

                // Click offset (relative to the element)
                this.offset.click.top -

                // Only for relative positioned nodes: Relative offset from element to offset parent
                this.offset.relative.top -

                // The offsetParent's offset without borders (offset + border)
                this.offset.parent.top +
                (this.cssPosition === "fixed" ?
                    -this.offset.scroll.top :
                    (scrollIsRootNode ? 0 : this.offset.scroll.top))
            ),
            left: (

                // The absolute mouse position
                pageX -

                // Click offset (relative to the element)
                this.offset.click.left -

                // Only for relative positioned nodes: Relative offset from element to offset parent
                this.offset.relative.left -

                // The offsetParent's offset without borders (offset + border)
                this.offset.parent.left +
                (this.cssPosition === "fixed" ?
                    -this.offset.scroll.left :
                    (scrollIsRootNode ? 0 : this.offset.scroll.left))
            )
        };

    };

    UIDraggable.prototype._clear = function () {
        this.helper.removeClass("nui-draggable-dragging");
        if (this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
            this.helper.remove();
        }
        this.helper = null;
        this.cancelHelperRemoval = false;
        if (this.destroyOnClear) {
            this.destroy();
        }
    };


    var safeActiveElement = function (document) {
            var activeElement;

            // Support: IE 9 only
            // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
            try {
                activeElement = document.activeElement;
            } catch (error) {
                activeElement = body;
            }

            // Support: IE 9 - 11 only
            // IE may return null instead of an element
            // Interestingly, this only seems to occur when NOT in an iframe
            if (!activeElement) {
                activeElement = body;
            }

            // Support: IE 11 only
            // IE11 returns a seemingly empty object in some cases when accessing
            // document.activeElement from an <iframe>
            if (!activeElement.nodeName) {
                activeElement = body;
            }

            return activeElement;
        },
        safeBlur = function (element) {

            // Support: IE9 - 10 only
            // If the <body> is blurred, IE will switch windows, see #9420
            if (element && element.nodeName.toLowerCase() !== "body") {
                $(element).trigger("blur");
            }
        };

    UIDraggable.prototype._blurActiveElement = function (event) {
        var activeElement = safeActiveElement(this.document[0]),
            target = $(event.target);

        // Don't blur if the event occurred on an element that is within
        // the currently focused element
        // See #10527, #12472
        if (target.closest(activeElement).length) {
            return;
        }

        // Blur any element that currently has focus, see #4261
        safeBlur(activeElement);
    };

    return UIDraggable;
});_N2.d('UILayerWindow', ['$', 'UIMouse'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments {_N2.UIMouse}
     */
    function UILayerWindow($element, options) {
        this.element = $element;

        this.widgetName = this.widgetName || 'UILayerWindow';
        this.widgetEventPrefix = "layerWindow";

        this.options = _AssignRecursive(this.options, options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UILayerWindow.prototype = Object.create(_N2.UIMouse.prototype);
    UILayerWindow.prototype.constructor = UILayerWindow;

    UILayerWindow.prototype.create = function () {

        this.bodyRect = body.getBoundingClientRect();
        window.addEventListener('resize', (function () {
            this.bodyRect = body.getBoundingClientRect();

            this.limitHorizontal(this.left, this.width, true);
            this.limitVertical(this.top, this.height, true);
        }).bind(this), {
            passive: true
        });

        this.left = parseInt(localStorage.getItem('ssPanelLeft'));
        if (!this.left) {
            this.left = 200
        }

        this.top = parseInt(localStorage.getItem('ssPanelTop'));
        if (!this.top) {
            this.top = 100
        }

        this.width = 355;

        this.height = parseInt(localStorage.getItem('ssPanelHeight'));
        if (!this.height) {
            this.height = 400
        }

        this.limitHorizontal(this.left, this.width);
        this.limitVertical(this.top, this.height);

        this._setupHandles();

        this._mouseInit();
    };

    UILayerWindow.prototype._setupHandles = function () {
        this.$title = this.element.find('.n2_ss_layer_window__title_inner')
            .on('mousedown', this._mouseDown.bind(this));

        this.$resize = $('<div class="n2_ss_layer_window__resize"></div>')
            .on('mousedown', this._mouseDown.bind(this))
            .appendTo(this.element);
    };

    UILayerWindow.prototype._mouseInit = function () {

    };

    UILayerWindow.prototype._mouseCapture = function (e) {
        if (this.$title.is(e.target)) {
            this.currentAction = 'move';
            return true;
        }

        if (this.$resize.is(e.target)) {
            this.currentAction = 'resize';
            return true;
        }

        return false;
    };

    UILayerWindow.prototype._mouseStart = function (e) {
        var rect = this.element[0].getBoundingClientRect();
        this.context = {};

        if (this.currentAction === 'move') {
            this.context.mouseDiff = {
                x: e.clientX - rect.left,
                y: e.clientY - rect.top
            };
        } else if (this.currentAction === 'resize') {
            this.context.mouseDiff = {
                y: e.clientY - rect.top - rect.height
            };
        }
    };

    UILayerWindow.prototype._mouseDrag = function (e) {

        if (this.currentAction === 'move') {

            this.limitHorizontal(e.clientX - this.context.mouseDiff.x, this.width);
            this.limitVertical(e.clientY - this.context.mouseDiff.y, this.height);
        } else if (this.currentAction === 'resize') {
            this.limitVertical(this.top, e.clientY - this.top - this.context.mouseDiff.y);
        }
    };

    UILayerWindow.prototype._mouseStop = function (e) {

        if (this.currentAction === 'move') {

            this.limitHorizontal(e.clientX - this.context.mouseDiff.x, this.width, true);
            this.limitVertical(e.clientY - this.context.mouseDiff.y, this.height, true);
        } else if (this.currentAction === 'resize') {
            this.limitVertical(this.top, e.clientY - this.top - this.context.mouseDiff.y, true);
        }

        delete this.currentAction;
    };

    UILayerWindow.prototype.limitVertical = function (top, height, store) {
        this.height = height;
        this.top = Math.min(Math.max(top, 60), this.bodyRect.height - this.height);
        if (this.top < 60) {

            this.height = this.height + this.top - 60;
            this.top = 60;
        }

        this.height = Math.max(this.height, 200);

        this.element.css({
            top: this.top + 'px',
            height: this.height + 'px'
        });

        if (store) {
            localStorage.setItem('ssPanelTop', this.top);
            localStorage.setItem('ssPanelHeight', this.height);
        }
    };

    UILayerWindow.prototype.limitHorizontal = function (left, width, store) {
        this.left = Math.min(Math.max(left, 50), this.bodyRect.width - width);

        this.element.css({
            left: this.left + 'px'
        });

        if (store) {
            localStorage.setItem('ssPanelLeft', this.left);
        }
    };

    return UILayerWindow;
});_N2.d('UIMouse', ['$', 'UIWidgetBase'], function () {
    var $ = _N2.$;
    "use strict";

    var ie = !!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());

    var mouseHandled = false;
    $(document).on("mouseup", function () {
        mouseHandled = false;
    });

    /**
     * @memberOf _N2
     *
     * @abstract
     * @constructor
     * @augments {_N2.UIWidgetBase}
     */
    function UIMouse($element, options) {
        this.widgetName = this.widgetName || 'UIMouse';
        this.options = _Assign({
            cancel: "input, textarea, button, select, option",
            distance: 1,
            delay: 0
        }, this.options);

        _N2.UIWidgetBase.prototype.constructor.apply(this, arguments);
    }

    UIMouse.prototype = Object.create(_N2.UIWidgetBase.prototype);
    UIMouse.prototype.constructor = UIMouse;

    UIMouse.prototype._mouseInit = function () {
        var that = this;
        this.element
            .on("mousedown." + this.widgetName, function (event) {
                return that._mouseDown(event);
            })
            .on("click." + this.widgetName, function (event) {
                if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
                    $.removeData(event.target, that.widgetName + ".preventClickEvent");
                    event.stopImmediatePropagation();
                    return false;
                }
            });
    };

    UIMouse.prototype._mouseDestroy = function () {
        this.element.off("." + this.widgetName);
        if (this._mouseMoveDelegate) {
            this.document
                .off("mousemove." + this.widgetName, this._mouseMoveDelegate)
                .off("mouseup." + this.widgetName, this._mouseUpDelegate);
        }
    };

    UIMouse.prototype._mouseDown = function (event) {
        // don't let more than one widget handle mouseStart
        if (mouseHandled) {
            return;
        }

        if (document.activeElement && document.activeElement !== body) {
            document.activeElement.blur();
        }

        this._mouseMoved = false;

        // We may have missed mouseup (out of window)
        (this._mouseStarted && this._mouseUp(event));

        this._mouseDownEvent = event;

        var that = this,
            btnIsLeft = (event.which === 1),

            // event.target.nodeName works around a bug in IE 8 with
            // disabled inputs (#7620)
            elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ?
                $(event.target).closest(this.options.cancel).length : false);
        if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
            return true;
        }

        this.mouseDelayMet = !this.options.delay;
        if (!this.mouseDelayMet) {
            this._mouseDelayTimer = setTimeout(function () {
                that.mouseDelayMet = true;
            }, this.options.delay);
        }

        if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
            this._mouseStarted = (this._mouseStart(event) !== false);
            if (!this._mouseStarted) {
                event.preventDefault();
                return true;
            }
        }

        // Click event may never have fired (Gecko & Opera)
        if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
            $.removeData(event.target, this.widgetName + ".preventClickEvent");
        }

        // These delegates are required to keep context
        this._mouseMoveDelegate = function (event) {
            return that._mouseMove(event);
        };
        this._mouseUpDelegate = function (event) {
            return that._mouseUp(event);
        };

        this.document
            .on("mousemove." + this.widgetName, this._mouseMoveDelegate)
            .on("mouseup." + this.widgetName, this._mouseUpDelegate);

        event.preventDefault();

        mouseHandled = true;
        return true;
    };


    UIMouse.prototype._mouseMove = function (event) {
        // Only check for mouseups outside the document if you've moved inside the document
        // at least once. This prevents the firing of mouseup in the case of IE<9, which will
        // fire a mousemove event if content is placed under the cursor. See #7778
        // Support: IE <9
        if (this._mouseMoved) {

            // IE mouseup check - mouseup happened when mouse was out of window
            if (ie && (!document.documentMode || document.documentMode < 9) && !event.button) {
                return this._mouseUp(event);

                // Iframe mouseup check - mouseup occurred in another document
            } else if (!event.which) {

                // Support: Safari <=8 - 9
                // Safari sets which to 0 if you press any of the following keys
                // during a drag (#14461)
                if (event.originalEvent.altKey || event.originalEvent.ctrlKey ||
                    event.originalEvent.metaKey || event.originalEvent.shiftKey) {
                    this.ignoreMissingWhich = true;
                } else if (!this.ignoreMissingWhich) {
                    return this._mouseUp(event);
                }
            }
        }

        if (event.which || event.button) {
            this._mouseMoved = true;
        }

        if (this._mouseStarted) {
            this._mouseDrag(event);
            return event.preventDefault();
        }

        if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
            this._mouseStarted =
                (this._mouseStart(this._mouseDownEvent, event) !== false);
            (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
        }

        return !this._mouseStarted;
    };

    UIMouse.prototype._mouseUp = function (event) {
        this.document
            .off("mousemove." + this.widgetName, this._mouseMoveDelegate)
            .off("mouseup." + this.widgetName, this._mouseUpDelegate);

        if (this._mouseStarted) {
            this._mouseStarted = false;

            if (event.target === this._mouseDownEvent.target) {
                $.data(event.target, this.widgetName + ".preventClickEvent", true);
            }

            this._mouseStop(event);
        } else {
            this._mouseCancel(event);
        }

        if (this._mouseDelayTimer) {
            clearTimeout(this._mouseDelayTimer);
            delete this._mouseDelayTimer;
        }

        this.ignoreMissingWhich = false;
        mouseHandled = false;
        event.preventDefault();
    };

    UIMouse.prototype._mouseDistanceMet = function (event) {
        return (Math.max(
                Math.abs(this._mouseDownEvent.pageX - event.pageX),
                Math.abs(this._mouseDownEvent.pageY - event.pageY)
            ) >= this.options.distance
        );
    };

    UIMouse.prototype._mouseDelayMet = function (/* event */) {
        return this.mouseDelayMet;
    };

    // These are placeholder methods, to be overriden by extending plugin
    UIMouse.prototype._mouseStart = function (/* event */) {
    };
    UIMouse.prototype._mouseDrag = function (/* event */) {
    };
    UIMouse.prototype._mouseStop = function (/* event */) {
    };
    UIMouse.prototype._mouseCancel = function (/* event */) {
    };
    UIMouse.prototype._mouseCapture = function (/* event */) {
        return true;
    };

    return UIMouse;
});_N2.d('UIResizeBar', ['$', 'UIMouse'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param $element
     * @param options
     */
    function UIResizeBar($element, options) {
        this.element = $element;

        this.widgetName = this.widgetName || 'UIResizable';
        this.widgetEventPrefix = "resize";

        this.options = _Assign({
            // Callbacks
            resize: null,
            start: null,
            stop: null
        }, this.options, options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UIResizeBar.prototype = Object.create(_N2.UIMouse.prototype);
    UIResizeBar.prototype.constructor = UIResizeBar;

    UIResizeBar.prototype.create = function () {

        var o = this.options;
        this.element.addClass("nui_resize_bar");

        this._setupHandles();

        this._mouseInit();

        this._trigger('create', null, {});
    };

    UIResizeBar.prototype._setupHandles = function () {
        var handleNames = ["e", "w"];

        /**
         * @type {{}}
         */
        this.handles = {};
        this.$handles = $();

        for (var i = 0; i < handleNames.length; i++) {

            var handle = handleNames[i],
                $handle = $("<div>")
                    .addClass("nui_resize_bar__handle " + "nui_resize_bar__handle--" + handle)
                    .on({
                        "mousedown": (function (handleName, e) {
                            this.currentHandle = handleName;
                            this._mouseDown(e);
                        }).bind(this, handle)
                    })
                    .appendTo(this.element);

            this.handles[handle] = $handle;
            this.$handles = this.$handles.add($handle);
        }
    };

    UIResizeBar.prototype._mouseStart = function (event) {

        this.currentData = this.originalData = {
            margin: parseInt(this.element.css('marginLeft')),
            width: parseInt(this.element.width())
        };
        this.originalMousePosition = {left: event.pageX};

        var cursor = this.handles[this.currentHandle].css("cursor");
        $("body").css("cursor", cursor);

        this._trigger("start", event, this.ui());
        return true;
    };

    UIResizeBar.prototype._mouseDrag = function (event) {
        var dx = (event.pageX - this.originalMousePosition.left) || 0;
        this.currentData = {};

        if (this.currentHandle === 'e') {
            this.currentData.margin = this.originalData.margin;
            this.currentData.width = Math.max(0, this.originalData.width + dx);
        } else if (this.currentHandle === 'w') {
            this.currentData.margin = Math.max(0, this.originalData.margin + dx);
            this.currentData.width = Math.max(0, this.originalData.width - dx);
        }

        this.currentData.width = Math.max(0, this.currentData.width);

        this._trigger("resize", event, this.ui());

        this.element.css('marginLeft', this.currentData.margin + 'px');
        this.element.css('width', this.currentData.width + 'px');
    };

    UIResizeBar.prototype._mouseStop = function (event) {

        $("body").css("cursor", "auto");

        this._trigger("stop", event, this.ui());
    };

    UIResizeBar.prototype.ui = function () {
        return {
            currentData: this.currentData
        };
    };

    return UIResizeBar;
});_N2.d('UISlider', ['$', 'UIMouse'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments {_N2.UIMouse}
     */
    function UISlider(element, options) {
        this.element = $(element);

        this.widgetName = this.widgetName || 'UISlider';
        this.widgetEventPrefix = "sliding";

        this.options = _Assign({
            min: 0,
            max: 100,
            step: 1,
            value: 0,

            // Callbacks
            change: null,
            sliding: null,
            start: null,
            stop: null
        }, this.options, options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UISlider.prototype = Object.create(_N2.UIMouse.prototype);
    UISlider.prototype.constructor = UISlider;

    UISlider.prototype.create = function () {
        this._mouseSliding = false;
        this._mouseInit();
        this._calculateNewMax();

        this.element.addClass("nui-slider nui-slider-horizontal");

        this._refresh();
    };

    UISlider.prototype.doAction = function (action) {
        _N2.UIWidgetBase.prototype.doAction.apply(this, arguments);

        switch (action) {
            case 'value':
                return this._value();
        }
    };

    UISlider.prototype.setOption = function (key, value) {
        _N2.UIWidgetBase.prototype.setOption.call(this, key, value);

        switch (key) {
            case "value":
                this._refreshValue();
                this._change(null, 0);
                break;
            case "step":
            case "min":
            case "max":
                this._calculateNewMax();
                this._refreshValue();
                break;
        }
    };


    UISlider.prototype._refresh = function () {
        this._createHandle();
        this._refreshValue();
    };

    UISlider.prototype._createHandle = function () {
        var existingHandle = this.element.find(".nui-slider-handle");

        if (existingHandle.length) {
            this.handle = existingHandle;
        } else {
            this.handle = $("<span></span>");
        }

        this.handle
            .addClass("nui-slider-handle")
            .appendTo(this.element);
    };

    UISlider.prototype._mouseCapture = function (event) {
        var position, normValue, distance, handle = this.handle, allowed, offset, mouseOverHandle,
            o = this.options;

        if (o.disabled) {
            return false;
        }

        this.elementSize = {
            width: this.element.outerWidth(),
            height: this.element.outerHeight()
        };
        this.elementOffset = this.element.offset();

        position = {x: event.pageX, y: event.pageY};
        normValue = this._normValueFromMouse(position);

        allowed = this._start(event);
        if (allowed === false) {
            return false;
        }
        this._mouseSliding = true;

        handle.trigger("focus");

        this._slide(event, normValue);

        return true;
    };

    UISlider.prototype._mouseStart = function () {
        this.lastValue = Number.MAX_VALUE;
        return true;
    };

    UISlider.prototype._mouseDrag = function (event) {
        var position = {x: event.pageX, y: event.pageY},
            normValue = this._normValueFromMouse(position);

        if (this.lastValue != normValue) {
            this._slide(event, normValue);
            this.lastValue = normValue;
        }

        return false;
    };

    UISlider.prototype._mouseStop = function (event) {
        this._mouseSliding = false;

        this._stop(event);
        this._change(event);

        return false;
    };

    UISlider.prototype._mouseCancel = function (e) {
        this._trigger("cancel", e);
    };

    UISlider.prototype._normValueFromMouse = function (position) {
        var pixelTotal,
            pixelMouse,
            percentMouse,
            valueTotal,
            valueMouse;

        pixelTotal = this.elementSize.width;
        pixelMouse = position.x - this.elementOffset.left;

        percentMouse = (pixelMouse / pixelTotal);
        if (percentMouse > 1) {
            percentMouse = 1;
        }
        if (percentMouse < 0) {
            percentMouse = 0;
        }

        valueTotal = this._valueMax() - this._valueMin();
        valueMouse = this._valueMin() + percentMouse * valueTotal;

        return this._trimAlignValue(valueMouse);
    };

    UISlider.prototype._trimAlignValue = function (val) {
        if (val <= this._valueMin()) {
            return this._valueMin();
        }
        if (val >= this._valueMax()) {
            return this._valueMax();
        }
        var step = (this.options.step > 0) ? this.options.step : 1,
            valModStep = (val - this._valueMin()) % step,
            alignValue = val - valModStep;

        if (Math.abs(valModStep) * 2 >= step) {
            alignValue += (valModStep > 0) ? step : (-step);
        }

        // Since JavaScript has problems with large floats, round
        // the final value to 5 digits after the decimal point (see #4124)
        return parseFloat(alignValue.toFixed(5));
    };

    UISlider.prototype._calculateNewMax = function () {
        var max = this.options.max,
            min = this._valueMin(),
            step = this.options.step,
            aboveMin = Math.round((max - min) / step) * step;
        max = aboveMin + min;
        if (max > this.options.max) {

            //If max is not divisible by step, rounding off may increase its value
            max -= step;
        }
        this.max = parseFloat(max.toFixed(this._precision()));
    };

    UISlider.prototype._precision = function () {
        var precision = this._precisionOf(this.options.step);
        if (this.options.min !== null) {
            precision = Math.max(precision, this._precisionOf(this.options.min));
        }
        return precision;
    };

    UISlider.prototype._precisionOf = function (num) {
        var str = num.toString(),
            decimal = str.indexOf(".");
        return decimal === -1 ? 0 : str.length - decimal - 1;
    };

    UISlider.prototype._change = function (event) {
        if (!this._mouseSliding) {
            this._trigger("change", event, this._uiHash());
        }
    };

    UISlider.prototype.value = function (newValue) {
        if (arguments.length) {
            this.options.value = this._trimAlignValue(newValue);
            this._refreshValue();
            this._change(null, 0);
            return;
        }

        return this._value();
    };

    //internal value getter
    // _value() returns value trimmed by min and max, aligned by step
    UISlider.prototype._value = function () {
        var val = this.options.value;
        val = this._trimAlignValue(val);

        return val;
    };

    UISlider.prototype._valueMin = function () {
        return this.options.min;
    };

    UISlider.prototype._valueMax = function () {
        return this.max;
    };

    UISlider.prototype._refreshValue = function () {
        var value = this.value(),
            valueMin = this._valueMin(),
            valueMax = this._valueMax(),
            valPercent = (valueMax !== valueMin) ? (value - valueMin) / (valueMax - valueMin) * 100 : 0;
        this.handle.css('left', valPercent + "%");
    };


    UISlider.prototype._uiHash = function (value) {
        return {
            handle: this.handle[0],
            value: value !== undefined ? value : this.value()
        };
    };

    UISlider.prototype._start = function (event) {
        return this._trigger("start", event, this._uiHash());
    };

    UISlider.prototype._slide = function (event, newVal) {
        var allowed,
            currentValue = this.value();

        if (newVal === currentValue) {
            return;
        }

        allowed = this._trigger("sliding", event, this._uiHash(newVal));

        // A slide can be canceled by returning false from the slide callback
        if (allowed === false) {
            return;
        }

        this.value(newVal);
    };

    UISlider.prototype._stop = function (event) {
        this._trigger("stop", event, this._uiHash());
    };

    return UISlider;
});_N2.d('UISortable', ['$', 'UIMouse'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments {_N2.UIMouse}
     */
    function UISortable($element, options) {
        this.element = $element;

        this.widgetName = this.widgetName || 'UISortable';
        this.widgetEventPrefix = "sortable";

        this.options = _Assign({
            items: '> *',
            handle: '',
            placeholder: false,
            helper: 'original',
            forcePlaceholderSize: false,
            forceHelperSize: false,
            axis: false,
            droppables: false
        }, this.options, options);

        this.isOver = false;

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UISortable.prototype = Object.create(_N2.UIMouse.prototype);
    UISortable.prototype.constructor = UISortable;

    UISortable.prototype.create = function () {
        this._mouseInit();
    };

    UISortable.prototype.destroy = function () {
        this._mouseDestroy();
    }

    UISortable.prototype._mouseCapture = function (e) {
        if (this.options.disabled) {
            return false;
        }

        var currentItem = null,
            $target = $(e.target);

        if (this.options.handle !== '') {
            var handles = this.element.find(this.options.items + ' ' + this.options.handle);
            if (handles.index($target) === -1 && !handles.has($target).length) {
                //Item not dragged by the handle
                return false;
            }
        }

        var items = this.element.find(this.options.items);
        if (items.index($target) !== -1) {
            currentItem = $target;
        } else {
            currentItem = items.has($target);
        }

        if (!currentItem.length) {
            return false;
        }

        this.currentItem = currentItem;

        return true;
    };

    UISortable.prototype._mouseStart = function (e) {

        this._trigger('beforestart', e, {
            currentItem: this.currentItem
        });

        this.context = {
            e: e,
            original: {
                pageX: e.pageX,
                pageY: e.pageY,
                clientX: e.clientX,
                clientY: e.clientY,
                elementBCR: this.element[0].getBoundingClientRect(),
                currentItemBCR: this.currentItem[0].getBoundingClientRect()
            },
            offsetShift: {
                top: 0,
                left: 0
            },
            scrollCB: this._mouseScroll.bind(this)
        };

        this.element.addClass('n2-ui-sortable-in-progress');

        this._cacheItems();

        this.placeholder = $('<div></div>')
            .addClass(this.options.placeholder || this.currentItem[0].className);

        var size = {
            width: this.currentItem.width() + 'px',
            height: this.currentItem.height() + 'px'
        };

        if (this.options.helper === 'clone') {
            this.helper = this.currentItem.clone();
        } else if (this.options.helper === 'clone_hide') {
            this.helper = this.currentItem.clone();
            this.currentItem.css('display', 'none');
        } else {
            this.helper = this.currentItem
        }

        if (this.options.forceHelperSize) {
            this.helper.css(size);
        }

        if (this.options.forcePlaceholderSize) {
            this.placeholder.css(size);
        }

        this.helper.addClass('n2-ui-sortable-helper')
            .css({
                position: 'absolute',
                zIndex: '1000'
            })
            .appendTo(this.element);

        this._trigger('start', e, this.ui());

        /**
         * Trigger mousemove event on scroll to update the helper position
         */
        window.addEventListener('scroll', this.context.scrollCB, {
            capture: true,
            passive: true
        });
    };

    UISortable.prototype._mouseDrag = function (e) {
        this.context.e = e;

        var elementBDR = this.element[0].getBoundingClientRect(),
            helperPosition = {};

        if (!this.options.axis || this.options.axis === 'x') {

            var modifierX = elementBDR.left + this.context.original.clientX - e.clientX;
            this.context.offsetShift.left = e.pageX - this.context.original.pageX - this.context.original.elementBCR.left + modifierX;

            helperPosition.left = this.context.original.currentItemBCR.left - modifierX + 'px';
        }

        if (!this.options.axis || this.options.axis === 'y') {

            var modifierY = elementBDR.top + this.context.original.clientY - e.clientY;
            this.context.offsetShift.top = e.pageY - this.context.original.pageY - this.context.original.elementBCR.top + modifierY;

            helperPosition.top = this.context.original.currentItemBCR.top - modifierY + 'px';
        }

        this.helper.css(helperPosition);

        var closestData = this._findClosestItem(e),
            nearestItem = closestData[1] === 'before' ? this.items[Math.max(0, closestData[2] - 1)] : this.items[Math.min(this.items.length - 1, closestData[2])];

        if (this.options.helper === 'clone' && (closestData[0].is(this.currentItem) || $(nearestItem).is(this.currentItem))) {
            this.placeholder.detach();
        } else {

            this.positionPlaceholder(closestData);
        }

        if (this.options.droppables) {
            if (closestData[1] !== 'over') {
                if (this.isOver) {
                    this._trigger('out', e, this.ui());
                    this.isOver = false;
                }
            } else {
                if (this.isOver && !this.isOver.is(closestData[0])) {
                    this._trigger('out', e, this.ui());
                    this.isOver = false;
                }

                if (!this.isOver) {
                    this.isOver = closestData[0];
                    this._trigger('over', e, this.ui());
                }
            }
        }
    };

    UISortable.prototype.positionPlaceholder = function (closestData) {
        switch (closestData[1]) {
            case 'before':
                this.placeholder.insertBefore(closestData[0]);
                break;
            case 'after':
                this.placeholder.insertAfter(closestData[0]);
                break;
            case 'over':
                this.placeholder.detach();
                break;
        }
    };

    UISortable.prototype._mouseStop = function (e) {
        this.context.e = e;

        this._trigger('beforestop', e, this.ui());

        window.removeEventListener('scroll', this.context.scrollCB, {
            capture: true,
            passive: true
        });

        this.placeholder.remove();

        var closestData = this._findClosestItem(e);
        switch (closestData[1]) {
            case 'before':
                this.currentItem.insertBefore(closestData[0]);
                break;
            case 'after':
                this.currentItem.insertAfter(closestData[0]);
                break;
        }

        this.helper
            .removeClass('n2-ui-sortable-helper')
            .css({
                position: '',
                zIndex: '',
                left: '',
                top: ''
            });

        if (this.options.helper === 'clone' || this.options.helper === 'clone_hide') {
            this.helper.remove();
        }
        if (this.options.helper === 'clone_hide') {
            this.currentItem.css('display', '');
        }

        this.element.removeClass('n2-ui-sortable-in-progress');

        if (closestData[1] === 'over') {
            this._trigger('drop', e, this.ui());
            this._trigger('out', e, this.ui());
        } else {
            this._trigger('stop', e, this.ui());
        }

        this.currentItem = null;

    };

    UISortable.prototype._findClosestItem = function (e) {
        var distance = Number.MAX_VALUE,
            left = e.pageX - this.context.offsetShift.left,
            top = e.pageY - this.context.offsetShift.top,
            closestItem, closestItemSide, closestItemIndex;

        for (var i = 0; i < this.itemsData.length; i++) {
            var item = this.itemsData[i];
            for (var k in item.side) {
                var localDistance = Math.sqrt(Math.pow((left - item.side[k].left) / item.width, 2) + Math.pow((top - item.side[k].top) / item.height, 2));
                if (localDistance < distance) {
                    distance = localDistance;
                    closestItem = item.item;

                    closestItemSide = k;
                    if (k === 'after') {
                        closestItemIndex = i + 1;
                    } else if (k === 'before') {
                        closestItemIndex = i;
                    }
                }
            }
        }
        return [closestItem, closestItemSide, closestItemIndex];
    };

    UISortable.prototype.getItems = function () {
        return this.element.find(this.options.items);
    };

    UISortable.prototype._cacheItems = function () {
        this.items = this.getItems();

        this.itemsData = [];
        for (var i = 0; i < this.items.length; i++) {

            if (this.options.helper === 'original' && this.items[i] === this.currentItem[0]) {
                continue;
            }

            var offset = this.items.eq(i).offset(),
                width = this.items.eq(i).width(),
                height = this.items.eq(i).height(),
                side = {};

            if (this.options.droppables && !this.currentItem.hasClass(this.options.droppables) && this.items.eq(i).hasClass(this.options.droppables)) {
                side.before = {
                    left: offset.left + width / 6,
                    top: offset.top + height / 6
                };
                side.over = {
                    left: offset.left + 3 * width / 6,
                    top: offset.top + 3 * height / 6
                };
                side.after = {
                    left: offset.left + 5 * width / 6,
                    top: offset.top + 5 * height / 6
                };
            } else {
                side.before = {
                    left: offset.left + width / 4,
                    top: offset.top + height / 4
                };
                side.after = {
                    left: offset.left + 3 * width / 4,
                    top: offset.top + 3 * height / 4
                };
            }

            if (window.n2const.isRTL()) {
                var after = side.after;
                side.after = side.before;
                side.before = after;
            }

            this.itemsData.push({
                item: this.items.eq(i),
                side: side,
                width: width,
                height: height
            });
        }
    };

    UISortable.prototype._mouseScroll = function () {
        var e = this.context.e;
        document.dispatchEvent(new MouseEvent('mousemove', {
            clientX: e.clientX,
            clientY: e.clientY
        }));
    };

    UISortable.prototype.ui = function () {
        return {
            placeholder: this.placeholder,
            helper: this.helper,
            item: this.currentItem,
            droppable: this.isOver
        };
    };

    return UISortable;
});_N2.d('UIWidgetBase', ['$'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @abstract
     * @constructor
     */
    function UIWidgetBase($element, options) {

        var element = $element[0];

        this.document = $(element.style ?

            // Element within the document
            element.ownerDocument :

            // Element is window or document
            element.document || element);
        this.window = $(this.document[0].defaultView || this.document[0].parentWindow);

        this.disabled = false;

        this.plugins = [];
    }

    UIWidgetBase.prototype.setOption = function (key, value) {

        this.options[key] = value;

        return this;
    };

    /**
     * @returns {_N2.UIWidgetBase}
     */
    UIWidgetBase.prototype.destroy = function () {
        return this;
    };

    UIWidgetBase.prototype._trigger = function (type, event, data) {
        var prop, orig;
        var callback = this.options[type];

        data = data || {};
        event = $.Event(event);
        event.type = (type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type).toLowerCase();

        // The original event may come from any element
        // so we need to reset the target on the new event
        event.target = this.element[0];

        // Copy original event properties over to the new event
        orig = event.originalEvent;
        if (orig) {
            for (prop in orig) {
                if (!(prop in event)) {
                    event[prop] = orig[prop];
                }
            }
        }

        this.element.trigger(event, data);
        return !(typeof callback === "function" &&
            callback.apply(this.element[0], [event].concat(data)) === false ||
            event.isDefaultPrevented());
    };

    UIWidgetBase.prototype._triggerInteraction = function () {

        this.element.trigger('interaction');
    };

    UIWidgetBase.addPlugin = function (_class, option, set) {
        for (var key in set) {
            _class.plugins[key] = _class.plugins[key] || [];
            _class.plugins[key].push([option, set[key]]);
        }
    };

    UIWidgetBase.prototype.callPlugin = function (name, args, allowDisconnected) {
        var set = this.constructor.plugins[name];

        if (!set) {
            return;
        }

        if (!allowDisconnected && (!this.element[0].parentNode ||
            this.element[0].parentNode.nodeType === 11)) {
            return;
        }

        for (var i = 0; i < set.length; i++) {
            if (this.options[set[i][0]]) {
                set[i][1].apply(this.element, args);
            }
        }
    };

    return UIWidgetBase;
});
_N2.d('NextendVisualCore', ['$'], function () {

    /**
     * @memberOf _N2
     *
     * @param visual
     * @param visualManager
     * @constructor
     */
    function NextendVisualCore(visual, visualManager) {
        this.id = visual.id;
        this.visualManager = visualManager;
        this.setValue(visual.value, false);
        this.visual = visual;
        this.visualManager.visuals[this.id] = this;
    }

    NextendVisualCore.prototype.compare = function (value) {

        var length = Math.max(this.value.length, value.length);
        for (var i = 0; i < length; i++) {
            if (!this._compareTab(this.value[i] === undefined ? {} : this.value[i], value[i] === undefined ? {} : value[i])) {
                return false;
            }
        }
        return true;
    };

    NextendVisualCore.prototype._compareTab = function (a, b) {
        var aProps = Object.getOwnPropertyNames(a);
        var bProps = Object.getOwnPropertyNames(b);
        if (a.length === 0 && bProps.length === 0) {
            return true;
        }

        if (aProps.length != bProps.length) {
            return false;
        }

        for (var i = 0; i < aProps.length; i++) {
            var propName = aProps[i];

            // If values of same property are not equal,
            // objects are not equivalent
            if (a[propName] !== b[propName]) {
                return false;
            }
        }

        return true;
    };

    NextendVisualCore.prototype.setValue = function (value, render) {
        var data = null;
        if (typeof value == 'string') {

            var decoded = value;
            if (decoded[0] != '{') {
                this.base64 = decoded;
                decoded = _N2.Base64.decode(decoded)
            } else {
                this.base64 = _N2.Base64.encode(decoded);
            }

            data = JSON.parse(decoded);
        } else {
            data = value;
        }
        this.name = data.name;
        this.value = data.data;

        if (render) {
            this.render();
        }
    };

    NextendVisualCore.prototype.isSystem = function () {
        return (this.visual.system == 1);
    };

    NextendVisualCore.prototype.isEditable = function () {
        return (this.visual.editable == 1);
    };

    NextendVisualCore.prototype.activate = function (e, cb) {
        if (e) {
            e.preventDefault();
        }
        this.visualManager.changeActiveVisual(this);
        if (typeof cb == 'function') {
            this.visualManager.controller.asyncVisualData(this.value, this.visualManager.showParameters, cb);
        } else {
            this.visualManager.controller.load(this.value, false, this.visualManager.showParameters);
        }
    };

    NextendVisualCore.prototype.active = function () {
    };

    NextendVisualCore.prototype.notActive = function () {
    };

    NextendVisualCore.prototype.delete = function (e) {
        if (e) {
            e.preventDefault();
        }

        this._delete();
    };
    NextendVisualCore.prototype._delete = function () {

        return _N2.AjaxHelper.ajax({
            type: "POST",
            url: _N2.AjaxHelper.makeAjaxUrl(this.visualManager.parameters.ajaxUrl, {
                nextendaction: 'deleteVisual'
            }),
            data: {
                visualId: this.id
            },
            dataType: 'json'
        })
            .done((function (response) {
                var visual = response.data.visual;

                if (this.visualManager.activeVisual && this.id == this.visualManager.activeVisual.id) {
                    this.visualManager.changeActiveVisual(null);
                }
                delete this.visualManager.visuals[this.id];
                delete this.set.visuals[this.id];
                this.row.remove();
                this.visualManager.$.trigger('visualDelete', [this.id]);
                _N2.Notification.success(n2_('Deleted.'));
            }).bind(this));
    };

    NextendVisualCore.prototype.render = function () {

    };

    NextendVisualCore.prototype.isUsed = function () {
        return false;
    };

    return NextendVisualCore;
});_N2.d('NextendFragmentEditorController', ['$', 'NextendFragmentEditorControllerWithEditor'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendFragmentEditorController() {
        _N2.NextendFragmentEditorControllerWithEditor.prototype.constructor.apply(this, arguments);

        this.clearTabButton = $('#n2-' + this.type + '-editor' + this.type + '-clear-tab')
            .on('click', this.clearCurrentTab.bind(this));


        this.$tabField = $('#n2-' + this.type + '-editor' + this.type + '-state')
            .on('nextendChange.n2-editor', this.tabChanged.bind(this));

        this.$tabContainer = $('#n2-' + this.type + '-editor' + this.type + '-state_tab');
    }

    NextendFragmentEditorController.prototype = Object.create(_N2.NextendFragmentEditorControllerWithEditor.prototype);
    NextendFragmentEditorController.prototype.constructor = NextendFragmentEditorController;

    NextendFragmentEditorController.prototype.loadDefaults = function () {
        _N2.NextendFragmentEditorControllerWithEditor.prototype.loadDefaults.call(this);

        this.currentTabIndex = 0;
        this._renderTimeout = false;
        this._delayStart = 0;
    };

    NextendFragmentEditorController.prototype._load = function (visual, tabs, parameters) {
        this.currentVisual = [];
        for (var i = 0; i < visual.length; i++) {
            this.currentVisual[i] = _AssignRecursive(this.getCleanVisual(), visual[i]);
        }

        this.localModePreview = {};

        this.mode = parameters.previewMode;
        if (tabs === false) {
            tabs = this.getTabs();
        }
        for (var i = this.currentVisual.length; i < tabs.length; i++) {
            this.currentVisual[i] = this.getCleanVisual();
        }
        if (parameters.previewHTML !== false && parameters.previewHTML !== '') {
            this.localModePreview[parameters.previewMode] = parameters.previewHTML;
        }

        this.setPreview();

        if (tabs === false) {
            tabs = [];
            for (var i = 0; i < this.currentVisual.length; i++) {
                tabs.push('#' + i);
            }
        }

        this.setTabs(tabs);
    };

    NextendFragmentEditorController.prototype.getCleanVisual = function () {
        return {};
    };

    NextendFragmentEditorController.prototype.getTabs = function () {
        return [];
    };

    NextendFragmentEditorController.prototype.setTabs = function (labels) {

        var field = this.$tabField.data('field');

        var options = {};
        for (var i = 0; i < labels.length; i++) {
            options[i] = labels[i];
        }

        field.setOptions(options, this.$tabContainer, '0');
        field.insideChange('0');
    };

    NextendFragmentEditorController.prototype.tabChanged = function () {
        if (document.activeElement) {
            document.activeElement.blur();
        }

        var tab = this.$tabField.val();

        this.currentTabIndex = tab;
        if (this.currentVisual[tab] === undefined) {
            this.currentVisual[tab] = {};
        }
        var values = _Assign({}, this.currentVisual[0]);
        if (tab !== '0') {
            _Assign(values, this.currentVisual[tab]);
            this.clearTabButton.css('display', '');
        } else {
            this.clearTabButton.css('display', 'none');
        }

        this.editor.load(values);
        this._tabChanged();
    };

    NextendFragmentEditorController.prototype._tabChanged = function () {
        this.renderCSS();
    };

    NextendFragmentEditorController.prototype.clearCurrentTab = function (e) {
        if (e) {
            e.preventDefault();
        }
        this.currentVisual[this.currentTabIndex] = {};
        this.tabChanged();
        this.renderCSS();
    };

    /**
     * @abstract
     */
    NextendFragmentEditorController.prototype.setPreview = function () {
    };

    NextendFragmentEditorController.prototype.propertyChanged = function (e, property, value) {
        this.isChanged = true;
        this.currentVisual[this.currentTabIndex][property] = value;
        this.renderPreview();
    };

    NextendFragmentEditorController.prototype.renderPreview = function () {
        var now = $.now();
        if (this._renderTimeout) {
            clearTimeout(this._renderTimeout);
            if (now - this._delayStart > 100) {
                this.renderCSS();
                this._delayStart = now;
            }
        } else {
            this._delayStart = now;
        }
        this._renderTimeout = setTimeout(this.renderCSS.bind(this), 33);
    };

    NextendFragmentEditorController.prototype.renderCSS = function () {
        this._renderTimeout = false;
    };

    return NextendFragmentEditorController;
});_N2.d('NextendFragmentEditorControllerWithEditor', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendFragmentEditorControllerWithEditor() {

        this.loadDefaults();
        this.lightbox = $('#n2-lightbox-' + this.type);

        this.editor = this.initEditor();
        this.editor.$.on('change', this.propertyChanged.bind(this));
    }

    NextendFragmentEditorControllerWithEditor.prototype.loadDefaults = function () {
        this.type = '';
        this.isChanged = false;
        this.visible = false;
    };

    NextendFragmentEditorControllerWithEditor.prototype.pause = function () {

    };

    NextendFragmentEditorControllerWithEditor.prototype.getEmptyVisual = function () {
        return [];
    };

    NextendFragmentEditorControllerWithEditor.prototype.get = function () {
        return this.currentVisual;
    };


    NextendFragmentEditorControllerWithEditor.prototype.initEditor = function () {
        return new _N2.NextendFragmentEditor();
    };

    NextendFragmentEditorControllerWithEditor.prototype.propertyChanged = function (e, property, value) {
        this.isChanged = true;
        this.currentVisual[property] = value;
    };

    NextendFragmentEditorControllerWithEditor.prototype.load = function (visual, tabs, parameters) {
        this.isChanged = false;

        this._load(visual, tabs, parameters);
    };

    NextendFragmentEditorControllerWithEditor.prototype._load = function (visual, tabs, parameters) {

        this.currentVisual = _AssignRecursive({}, visual);

        this.loadToEditor();
    };

    NextendFragmentEditorControllerWithEditor.prototype.loadToEditor = function () {
        this.editor.load(this.currentVisual);
    };

    NextendFragmentEditorControllerWithEditor.prototype.show = function () {
        this.visible = true;
        _N2.WindowManager.addWindow("visual");
    };

    NextendFragmentEditorControllerWithEditor.prototype.close = function () {
        this.visible = false;
        _N2.WindowManager.removeWindow();
    };

    return NextendFragmentEditorControllerWithEditor;
});_N2.d('CSSRenderer', '$', function () {

    /**
     * @memberOf _N2
     *
     * @abstract
     * @constructor
     */
    function CSSRenderer() {
    }

    CSSRenderer.prototype.decodeData = function (data) {
        if (typeof data === 'object') {
            return data;
        } else if (data === '') {
            return false;
        } else if (data[0] !== '{') {
            data = _N2.Base64.decode(data);
        }

        var decoded = JSON.parse(data);
        if (decoded.data !== undefined) {
            return decoded.data;
        }

        return false;
    };

    CSSRenderer.prototype.getCSS = function (pre, selector, visualTabs, cssStructure, parameters) {

        parameters = _Assign({
            deleteRule: false
        }, parameters);

        var css = '',
            rePre = new RegExp('@pre', "g"),
            reSelector = new RegExp('@selector', "g");

        for (var k in cssStructure.selectors) {
            var rule = k
                .replace(rePre, pre)
                .replace(reSelector, selector);

            css += rule + "{\n" + cssStructure.selectors[k] + "}\n";
            if (parameters.deleteRule) {
                _N2._css.deleteRule(rule);
            }
        }

        if (cssStructure.renderOptions.combined) {
            for (var i = 0; i < visualTabs.length; i++) {
                css = css.replace(new RegExp('@tab' + i, "g"), this.render(visualTabs[i]));
            }
        } else {
            for (var i = 0; i < visualTabs.length; i++) {
                visualTabs[i] = _Assign({}, visualTabs[i]);
                css = css.replace(new RegExp('@tab' + i, "g"), this.render(visualTabs[i]));
            }
        }
        return css;
    };

    CSSRenderer.prototype.render = function (visualData) {
        var visual = this.makeVisualData(visualData);
        var css = '',
            raw = '';
        if (visual.raw !== undefined) {
            raw = visual.raw;
            delete visual.raw;
        }
        for (var k in visual) {

            css += this.deCase(k) + ": " + visual[k] + ";\n";
        }
        css += raw;
        return css;
    };

    CSSRenderer.prototype.makeVisualData = function (visualData) {
        var visual = {};
        for (var property in visualData) {
            if (visualData.hasOwnProperty(property) && typeof visualData[property] !== 'function') {
                this['makeStyle' + property](visualData[property], visual);
            }
        }
        return visual;
    };

    CSSRenderer.prototype.deCase = function (s) {
        return s.replace(/[A-Z]/g, function (a) {
            return '-' + a.toLowerCase()
        });
    };

    return CSSRenderer;
});_N2.d('NextendFragmentEditor', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendFragmentEditor() {
        this.fields = {};
        this.$ = $(this);
    }

    NextendFragmentEditor.prototype.load = function (values) {
        this._off();
        this._on();
    };

    NextendFragmentEditor.prototype._on = function () {
        for (var id in this.fields) {
            this.fields[id].element.on(this.fields[id].events);
        }
    };

    NextendFragmentEditor.prototype._off = function () {
        for (var id in this.fields) {
            this.fields[id].element.off('.n2-editor');
        }
    };

    NextendFragmentEditor.prototype.trigger = function (property, value) {
        this.$.trigger('change', [property, value]);
    };

    return NextendFragmentEditor;
});_N2.d('NextendVisualWithSet', ['$', 'NextendVisualCore'], function () {

    /**
     * @memberOf _N2
     *
     * @param visual
     * @param set
     * @param visualManager
     * @constructor
     */
    function NextendVisualWithSet(visual, set, visualManager) {
        this.set = set;
        _N2.NextendVisualCore.prototype.constructor.call(this, visual, visualManager);
    }

    NextendVisualWithSet.prototype = Object.create(_N2.NextendVisualCore.prototype);
    NextendVisualWithSet.prototype.constructor = NextendVisualWithSet;

    NextendVisualWithSet.prototype.active = function () {
        var setId = this.set.set.id;
        this.visualManager.changeSet(setId);

        _N2.NextendVisualCore.prototype.active.call(this);
    };

    return NextendVisualWithSet;
});_N2.d('NextendVisualWithSetRow', ['$', 'NextendVisualWithSet'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendVisualWithSetRow() {
        _N2.NextendVisualWithSet.prototype.constructor.apply(this, arguments);
    }

    NextendVisualWithSetRow.prototype = Object.create(_N2.NextendVisualWithSet.prototype);
    NextendVisualWithSetRow.prototype.constructor = NextendVisualWithSetRow;


    NextendVisualWithSetRow.prototype.createRow = function () {
        this.row = $('<div class="n2_fullscreen_editor__visual"></div>')
            .append($('<div class="n2_fullscreen_editor__visual_label"></div>').text(this.name).on('click', this.activate.bind(this)));
        if (!this.isSystem()) {
            this.row.append($('<div class="n2_fullscreen_editor__visual_actions"></div>')
                .append($('<div class="n2_fullscreen_editor__visual_action"><i class="ssi_16 ssi_16--delete"></i></div>')
                    .on('click', this.delete.bind(this))));
        }
        return this.row;
    };

    NextendVisualWithSetRow.prototype.setValue = function (value, render) {
        _N2.NextendVisualWithSet.prototype.setValue.call(this, value, render);

        if (this.row) {
            this.row.find('.n2_fullscreen_editor__visual_label').text(this.name);
        }
    };

    NextendVisualWithSetRow.prototype.active = function () {
        this.row.addClass('n2_fullscreen_editor__visual--active');
        _N2.NextendVisualWithSet.prototype.active.call(this);
    };

    NextendVisualWithSetRow.prototype.notActive = function () {
        this.row.removeClass('n2_fullscreen_editor__visual--active');
        _N2.NextendVisualWithSet.prototype.notActive.call(this);
    };

    return NextendVisualWithSetRow;
});_N2.d('NextendVisualWithSetRowMultipleSelection', ['$', 'NextendVisualWithSetRow'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param visual
     * @param set
     * @param visualManager
     * @constructor
     */
    function NextendVisualWithSetRowMultipleSelection(visual, set, visualManager) {
        this.checked = false;
        visual.system = 1;
        visual.editable = 0;
        _N2.NextendVisualWithSetRow.prototype.constructor.apply(this, arguments);
    }

    NextendVisualWithSetRowMultipleSelection.prototype = Object.create(_N2.NextendVisualWithSetRow.prototype);
    NextendVisualWithSetRowMultipleSelection.prototype.constructor = NextendVisualWithSetRowMultipleSelection;


    NextendVisualWithSetRowMultipleSelection.prototype.createRow = function () {
        var row = _N2.NextendVisualWithSetRow.prototype.createRow.call(this);
        this.checkbox = $('<div class="n2_fullscreen_editor__visual_select_tick"><i class="ssi_16 ssi_16--check"></i></div>')
            .on('click', this.checkOrUnCheck.bind(this))
            .prependTo(row);

        return row;
    };

    NextendVisualWithSetRowMultipleSelection.prototype.setValue = function (data, render) {
        this.name = data.name;
        this.value = data.data;
        if (this.row) {
            this.row.find('.n2_fullscreen_editor__visual_label').text(this.name);
        }

        if (render) {
            this.render();
        }
    };

    NextendVisualWithSetRowMultipleSelection.prototype.activate = function (e, cb) {
        if (e) {
            e.preventDefault();
        }
        this.visualManager.changeActiveVisual(this);
        this.visualManager.controller.setAnimationProperties(this.value);
    };

    NextendVisualWithSetRowMultipleSelection.prototype.checkOrUnCheck = function (e) {
        e.preventDefault();
        e.stopPropagation();
        if (this.checked) {
            this.unCheck();
        } else {
            this.check();
        }
    };

    NextendVisualWithSetRowMultipleSelection.prototype.check = function () {
        this.checked = true;
        this.row.addClass('n2_fullscreen_editor__visual--selected');
        this.activate();
    };

    NextendVisualWithSetRowMultipleSelection.prototype.unCheck = function () {
        this.checked = false;
        this.row.removeClass('n2_fullscreen_editor__visual--selected');
        this.activate();
    };

    return NextendVisualWithSetRowMultipleSelection;
});_N2.d('NextendVisualSetsManager', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param visualManager
     * @constructor
     */
    function NextendVisualSetsManager(visualManager) {
        this.visualManager = visualManager;
        this.$ = $(this);
    }

    return NextendVisualSetsManager;
});_N2.d('NextendVisualSet', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param set
     * @param visualManager
     * @constructor
     */
    function NextendVisualSet(set, visualManager) {
        this.set = set;
        this.visualManager = visualManager;

        this.visualList = $('<div class="n2_fullscreen_editor__visual_list"></div>');


        this.visualManager.sets[set.id] = this;
        if (set.referencekey !== '') {
            this.visualManager.setsByReference[set.referencekey] = set;
        }

        if (this.visualManager.setsSelector) {
            this.option = $('<option value="' + set.id + '"></option>').text(set.value)
                .appendTo(this.visualManager.setsSelector);
        }
    }

    NextendVisualSet.prototype.active = function () {
        $.when(this._loadVisuals())
            .done((function () {
                this.visualList.appendTo(this.visualManager.visualListContainer);
            }).bind(this));
    };

    NextendVisualSet.prototype.notActive = function () {
        this.visualList.detach();
    };

    NextendVisualSet.prototype.loadVisuals = function (visuals) {
        if (this.visuals === undefined) {
            this.visuals = {};
            for (var i = 0; i < visuals.length; i++) {
                this.addVisual(visuals[i]);
            }
        }
    };

    NextendVisualSet.prototype._loadVisuals = function () {
        if (this.visuals == null) {
            return _N2.AjaxHelper.ajax({
                type: "POST",
                url: _N2.AjaxHelper.makeAjaxUrl(this.visualManager.parameters.ajaxUrl, {
                    nextendaction: 'loadVisualsForSet'
                }),
                data: {
                    setId: this.set.id
                },
                dataType: 'json'
            })
                .done((function (response) {
                    this.loadVisuals(response.data.visuals);
                }).bind(this));
        }
        return true;
    };

    NextendVisualSet.prototype.addVisual = function (visual) {
        if (this.visuals[visual.id] === undefined) {
            this.visuals[visual.id] = this.visualManager.createVisual(visual, this);
            this.visualList.append(this.visuals[visual.id].createRow());
        }
        return this.visuals[visual.id];
    };

    NextendVisualSet.prototype.rename = function (name) {
        this.set.value = name;
        this.option.text(name);
    };

    NextendVisualSet.prototype.delete = function () {
        this.option.remove();
        delete this.visualManager.sets[this.set.id];
    };

    return NextendVisualSet;
});_N2.d('NextendVisualManagerCore', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param parameters
     * @constructor
     */
    function NextendVisualManagerCore(parameters) {
        this.loadDefaults();

        this.$ = $(this);

        _N2['_' + this.type + 'Manager'] = this;

        this.lightbox = $('#n2-lightbox-' + this.type);

        this.lightbox.find('.n2_fullscreen_editor__overlay')
            .on('click', this.hide.bind(this));

        this.visualListContainer = $('<div class="n2_fullscreen_editor__visual_list_container"></div>')
            .appendTo(this.lightbox.find('.n2_fullscreen_editor__content_sidebar'));

        this.parameters = parameters;

        this.visuals = {};

        this.controller = this.initController();
        if (this.controller) {
            this.renderer = this.controller.renderer;
        }

        this.firstLoadVisuals(parameters.visuals);

        this.lightbox.find('.n2_fullscreen_editor__save_as_new')
            .on('click', this.saveAsNew.bind(this));

        this.cancelButton = this.lightbox.find('.n2_fullscreen_editor__cancel')
            .on('click', this.hide.bind(this));

        this.saveButton = this.lightbox.find('.n2_fullscreen_editor__save');
        if (this.setVisual !== undefined) {
            this.saveButton.on('click', this.setVisual.bind(this));
        }
    }

    NextendVisualManagerCore.prototype.loadDefaults = function () {
        this.mode = 'linked';
        this.labels = {
            visual: 'visual',
            visuals: 'visuals'
        };
        this.visualLoadDeferreds = {};
        this.showParameters = false;
    };

    NextendVisualManagerCore.prototype.firstLoadVisuals = function (visuals) {

        for (var k in visuals) {
            this.sets[k].loadVisuals(visuals[k]);
        }
    };

    NextendVisualManagerCore.prototype.initController = function () {

    };

    NextendVisualManagerCore.prototype.getVisual = function (id) {
        if (parseInt(id) > 0) {
            if (this.visuals[id] !== undefined) {
                return this.visuals[id];
            } else if (this.visualLoadDeferreds[id] !== undefined) {
                return this.visualLoadDeferreds[id];
            } else {
                var deferred = $.Deferred();
                this.visualLoadDeferreds[id] = deferred;
                this._loadVisualFromServer(id)
                    .done((function () {
                        deferred.resolve(this.visuals[id]);
                        delete this.visualLoadDeferreds[id];
                    }).bind(this))
                    .fail((function () {
                        // This visual is Empty!!!
                        deferred.resolve({
                            id: -1,
                            name: n2_('Empty')
                        });
                        delete this.visualLoadDeferreds[id];
                    }).bind(this));
                return deferred;
            }
        } else {
            try {
                var decoded = id;
                if (decoded[0] != '{') {
                    decoded = _N2.Base64.decode(decoded)
                }
                JSON.parse(decoded);
                return {
                    id: 0,
                    name: n2_('Static')
                };
            } catch (e) {
                // This visual is Empty!!!
                return {
                    id: -1,
                    name: n2_('Empty')
                };
            }
        }
    };

    NextendVisualManagerCore.prototype._loadVisualFromServer = function (visualId) {
        return _N2.AjaxHelper.ajax({
            type: "POST",
            url: _N2.AjaxHelper.makeAjaxUrl(this.parameters.ajaxUrl, {
                nextendaction: 'loadVisual'
            }),
            data: {
                visualId: visualId
            },
            dataType: 'json'
        });
    };

    NextendVisualManagerCore.prototype.show = function (data, saveCallback, showParameters) {
        _N2.Esc.add((function () {
            this.hide();
            return true;
        }).bind(this));

        this.showParameters = _Assign({
            previewMode: false,
            previewHTML: false
        }, showParameters);

        $('body').addClass('n2_body--fullscreen-editor-visible');
        this.lightbox.addClass('n2_fullscreen_editor--visible');

        this.loadDataToController(data);

        this.$.on('save', saveCallback);

        this._show();
    };

    NextendVisualManagerCore.prototype._show = function () {
        $(':focus').trigger("blur");

        this.controller.show();

        $(window).on({
            'keydown.visual': (function (e) {
                if (e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'INPUT' && !e.target.isContentEditable) {
                    if (e.code === 'Enter') {
                        this.saveButton.trigger('click');
                    }
                }
            }).bind(this)
        });
    };

    NextendVisualManagerCore.prototype.setAndClose = function (data) {
        this.$.trigger('save', [data]);
    };

    NextendVisualManagerCore.prototype.hide = function (e) {
        this.controller.pause();

        if (e !== undefined) {
            e.preventDefault();
            _N2.Esc.pop();
        }
        this.controller.close();
        this.$.off('save');
        $('body').removeClass('n2_body--fullscreen-editor-visible');
        this.lightbox.removeClass('n2_fullscreen_editor--visible');

        $(window).off('keydown.visual');
    };

    NextendVisualManagerCore.prototype.getDataFromController = function (data, showParameters, cb) {
        this.showParameters = _Assign({
            previewMode: false,
            previewHTML: false
        }, showParameters);
        return this.loadDataToController(data, cb);
    };

    NextendVisualManagerCore.prototype.loadDataToController = function (data) {
        if (this.isVisualData(data)) {
            $.when(this.getVisual(data)).done((function (visual) {
                if (visual.id > 0) {
                    visual.activate();
                } else {
                    console.error(data + ' visual is not found linked');
                }
            }).bind(this));
        } else {
            console.error(data + ' visual not found');
        }
    };

    NextendVisualManagerCore.prototype.isVisualData = function (data) {
        return parseInt(data) > 0;
    };

    NextendVisualManagerCore.prototype.saveAsNew = function (e) {
        e.preventDefault();

        var modal = new _N2.ModalSimple('section-save-as', {
            destroyOnHide: false,
            width: 440
        });

        modal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--plus', 'green'));
        modal.addContent(_N2.UI.modalHeading(n2_('Save as')));

        var $row = _N2.UI.modalFormRow(true);
        modal.addContent($row);

        var $name = _N2.UI.modalFieldText($row, 'name', n2_('Name'), '');

        var $saveButton = _N2.UI.modalButton(n2_('Save as New'), 'green');
        modal.addButton($saveButton);

        $saveButton.on('click', (function (e) {
            e.preventDefault();

            var name = $name.val();
            if (name === '') {
                _N2.Notification.error(n2_('Please fill the name field!'));
            } else {
                this._saveAsNew(name)
                    .done(modal.hide.bind(modal, e), _N2.Notification.success(n2_('Saved.')));
            }
        }).bind(this));

        modal.show();
    };

    NextendVisualManagerCore.prototype._saveAsNew = function (name) {
        return _N2.AjaxHelper.ajax({
            type: "POST",
            url: _N2.AjaxHelper.makeAjaxUrl(this.parameters.ajaxUrl, {
                nextendaction: 'addVisual'
            }),
            data: {
                setId: this.getSetValue(),
                value: _N2.Base64.encode(JSON.stringify({
                    name: name,
                    data: this.controller.get('saveAsNew')
                }))
            },
            dataType: 'json'
        })
            .done((function (response) {
                var visual = response.data.visual;
                this.changeActiveVisual(this.sets[visual.referencekey].addVisual(visual));
            }).bind(this));
    };

    NextendVisualManagerCore.prototype.saveActiveVisual = function (name) {

        return _N2.AjaxHelper.ajax({
            type: "POST",
            url: _N2.AjaxHelper.makeAjaxUrl(this.parameters.ajaxUrl, {
                nextendaction: 'changeVisual'
            }),
            data: {
                visualId: this.activeVisual.id,
                value: this.getBase64(name)
            },
            dataType: 'json'
        }).done((function (response) {
            this.activeVisual.setValue(response.data.visual.value, true);
        }).bind(this));
    };

    NextendVisualManagerCore.prototype.changeActiveVisual = function (visual) {
        if (this.activeVisual) {
            this.activeVisual.notActive();
            this.activeVisual = false;
        }
        if (visual /*&& (this.mode == 0 || this.mode == 'linked')*/) {
            if (this.mode == 'static') {
                this.setMode('linked');
            }
            visual.active();
            this.activeVisual = visual;
        }
    };

    NextendVisualManagerCore.prototype.getBase64 = function (name) {

        return _N2.Base64.encode(JSON.stringify({
            name: name,
            data: this.controller.get('set')
        }));
    };

    return NextendVisualManagerCore;
});
_N2.d('NextendVisualManagerMultipleSelection', ['$', 'NextendVisualManagerVisibleSets'], function () {
    var $ = _N2.$;

    /**
     * Multiple selection
     * Ex.: Background animation, Post background animation
     *
     * @memberOf _N2
     */

    function NextendVisualManagerMultipleSelection(parameters) {

        _N2['_' + this.type + 'Manager'] = this;

        // Push the constructor to the first show as an optimization.
        this._lateInit = (function (parameters) {
            _N2.NextendVisualManagerVisibleSets.prototype.constructor.call(this, parameters);
        }).bind(this, parameters);

    }

    NextendVisualManagerMultipleSelection.prototype = Object.create(_N2.NextendVisualManagerVisibleSets.prototype);
    NextendVisualManagerMultipleSelection.prototype.constructor = NextendVisualManagerMultipleSelection;


    NextendVisualManagerMultipleSelection.prototype.lateInit = function () {
        if (!this.inited) {
            this.inited = true;

            this._lateInit();
        }
    };

    NextendVisualManagerMultipleSelection.prototype.show = function (data, saveCallback, controllerParameters) {

        this.lateInit();

        _N2.Esc.add((function () {
            this.hide();
            return true;
        }).bind(this));

        $('body').addClass('n2_body--fullscreen-editor-visible');
        this.lightbox.addClass('n2_fullscreen_editor--visible');

        var i = 0;
        if (data != '') {
            var selected = data.split('||'),
                hasSelected = false;
            for (; i < selected.length; i++) {
                $.when(this.getVisual(selected[i])).done(function (visual) {
                    if (visual && visual.check) {
                        visual.check();
                        if (!hasSelected) {
                            hasSelected = true;
                            visual.activate();
                        }
                    }
                });
            }
        }

        this.$.on('save', saveCallback);

        this.controller.start(controllerParameters);

        if (i == 0) {
            $.when(this.activeSet._loadVisuals())
                .done((function () {
                    for (var k in this.activeSet.visuals) {
                        this.activeSet.visuals[k].activate();
                        break;
                    }
                }).bind(this));
        }

        this._show();
    };

    NextendVisualManagerMultipleSelection.prototype.setVisual = function (e) {
        e.preventDefault();
        this.setAndClose(this.getAsString());
        this.hide(e);
    };

    NextendVisualManagerMultipleSelection.prototype.getAsString = function () {
        var selected = [];
        for (var k in this.sets) {
            var set = this.sets[k];
            for (var i in set.visuals) {
                if (set.visuals[i].checked) {
                    selected.push(set.visuals[i].id);
                }
            }
        }
        if (selected.length == 0 && this.activeVisual) {
            selected.push(this.activeVisual.id);
        }
        return selected.join('||');
    };

    NextendVisualManagerMultipleSelection.prototype.hide = function (e) {
        _N2.NextendVisualManagerVisibleSets.prototype.hide.apply(this, arguments);

        for (var k in this.sets) {
            var set = this.sets[k];
            for (var i in set.visuals) {
                set.visuals[i].unCheck();
            }
        }
    };

    return NextendVisualManagerMultipleSelection;
});
_N2.d('NextendVisualManagerSetsAndMore', ['$', 'NextendVisualManagerVisibleSets'], function () {
    var $ = _N2.$;

    /**
     * Static and linked mode
     * Ex.: Style, Fonts, Animation
     *
     * @memberOf _N2
     */

    function NextendVisualManagerSetsAndMore() {
        _N2.NextendVisualManagerVisibleSets.prototype.constructor.apply(this, arguments);

        this.setMode(0);
    }

    NextendVisualManagerSetsAndMore.prototype = Object.create(_N2.NextendVisualManagerVisibleSets.prototype);
    NextendVisualManagerSetsAndMore.prototype.constructor = NextendVisualManagerSetsAndMore;


    NextendVisualManagerSetsAndMore.prototype.setMode = function (newMode) {
        if (newMode == 'static') {
            this.changeActiveVisual(null);
        }
        if (this.mode != newMode) {
            switch (newMode) {
                case 0:
                    this.cancelButton.css('display', 'none');
                    this.saveButton
                        .off('click');
                    break;

                case 'static':
                default:
                    this.cancelButton.css('display', 'inline-block');
                    this.saveButton
                        .off('click')
                        .on('click', this.setVisualAsStatic.bind(this));
                    break;
            }
            this.mode = newMode;
        }
    };

    NextendVisualManagerSetsAndMore.prototype.loadDataToController = function (data, cb) {
        if (parseInt(data) > 0) {
            $.when(this.getVisual(data)).done((function (visual) {
                if (visual.id > 0) {
                    this.setMode('linked');

                    visual.activate(undefined, cb);
                } else {
                    this.setMode('static');
                    if (typeof cb == 'function') {
                        this.controller.asyncVisualData('', this.showParameters, cb);
                    } else {
                        this.controller.load('', false, this.showParameters);
                    }
                }
            }).bind(this));
        } else {
            var visualData = '';
            this.setMode('static');
            try {
                visualData = this.getStaticData(data);
            } catch (e) {
                // This visual is Empty!!!
            }
            if (typeof cb == 'function') {
                this.controller.asyncVisualData(visualData, this.showParameters, cb);
            } else {
                this.controller.load(visualData, false, this.showParameters);
            }
        }
    };

    NextendVisualManagerSetsAndMore.prototype.getStaticData = function (data) {

        var decoded = data;
        if (decoded[0] !== '{') {
            decoded = _N2.Base64.decode(decoded)
        }

        var d = JSON.parse(decoded).data;
        if (d === undefined) {
            return '';
        }
        return d;
    };

    NextendVisualManagerSetsAndMore.prototype.setVisualAsStatic = function (e) {
        e.preventDefault();
        this.setAndClose(this.getBase64(n2_('Static')));
        this.hide(e);
    };

    return NextendVisualManagerSetsAndMore;
});_N2.d('NextendVisualManagerVisibleSets', ['$', 'NextendVisualManagerCore'], function () {
    var $ = _N2.$;

    /**
     * Sets are visible
     *
     * @memberOf _N2
     */
    function NextendVisualManagerVisibleSets() {
        _N2.NextendVisualManagerCore.prototype.constructor.apply(this, arguments);
    }

    NextendVisualManagerVisibleSets.prototype = Object.create(_N2.NextendVisualManagerCore.prototype);
    NextendVisualManagerVisibleSets.prototype.constructor = NextendVisualManagerVisibleSets;

    NextendVisualManagerVisibleSets.prototype.firstLoadVisuals = function (visuals) {
        this.sets = {};
        this.setsByReference = {};

        this.setsSelector = $('#' + this.parameters.setsIdentifier + 'sets_select');
        for (var i = 0; i < this.parameters.sets.length; i++) {
            this.newVisualSet(this.parameters.sets[i]);
        }
        this.initSetsManager();
        for (var k in visuals) {
            this.sets[k].loadVisuals(visuals[k])
        }

        this.activeSet = this.sets[this.getSetValue()];
        this.activeSet.active();

        if (this.setsSelector.length) {
            this.setsSelector.on('change', (function () {
                this.activeSet.notActive();
                this.activeSet = this.sets[this.getSetValue()];
                this.activeSet.active();
            }).bind(this));
        }
    };


    NextendVisualManagerVisibleSets.prototype.initSetsManager = function () {
        new _N2.NextendVisualSetsManager(this);
    };

    NextendVisualManagerVisibleSets.prototype._loadVisualFromServer = function (visualId) {
        return _N2.AjaxHelper.ajax({
            type: "POST",
            url: _N2.AjaxHelper.makeAjaxUrl(this.parameters.ajaxUrl, {
                nextendaction: 'loadSetByVisualId'
            }),
            data: {
                visualId: visualId
            },
            dataType: 'json'
        })
            .done((function (response) {
                this.sets[response.data.set.setId].loadVisuals(response.data.set.visuals);

            }).bind(this));
    };

    NextendVisualManagerVisibleSets.prototype.changeSet = function (setId) {
        if (this.getSetValue() != setId) {
            this.setsSelector.val(setId)
                .trigger('change');
        }
    };

    NextendVisualManagerVisibleSets.prototype.changeSetById = function (id) {
        if (this.sets[id] !== undefined) {
            this.changeSet(id);
        }
    };

    NextendVisualManagerVisibleSets.prototype.newVisualSet = function (set) {
        return new _N2.NextendVisualSet(set, this);
    };

    NextendVisualManagerVisibleSets.prototype.getSetValue = function () {
        if (this.setsSelector.length) {
            return this.setsSelector.val();
        }

        return this.parameters.fixedSet;
    };

    return NextendVisualManagerVisibleSets;
});
_N2.d('NextendBrowse', ['$'], function () {
    var $ = _N2.$;

    var cache = {};

    /**
     *
     * @returns {jQuery}
     */
    function createBoxUpload() {
        return $('<div class="n2_modal_filesystem_box n2_modal_filesystem_box--upload" id="n2-browse-upload"></div>')
            .append('<div class="n2_modal_filesystem_box__icon"><i class="ssi_48 ssi_48--drop"></i></div>')
            .append('<div class="n2_modal_filesystem_box__label">' + n2_('Drop files here') + '</div>')
    }

    /**
     * @returns {jQuery}
     */
    function createBoxDirectoryUp() {
        return $('<div class="n2_modal_filesystem_box n2_modal_filesystem_box--directory-up"><div class="n2_modal_filesystem_box__icon"><i class="ssi_64 ssi_64--folderup"></i></div><div class="n2_modal_filesystem_box__label">' + n2_('Parent directory') + '</div></div>');
    }

    /**
     *
     * @param {string} directoryName
     * @returns {jQuery}
     */
    function createBoxDirectory(directoryName) {
        return $('<div class="n2_modal_filesystem_box n2_modal_filesystem_box--directory"><div class="n2_modal_filesystem_box__icon"><i class="ssi_64 ssi_64--folder"></i></div><div class="n2_modal_filesystem_box__label">' + directoryName + '</div></div>');
    }

    /**
     *
     * @param {string} fileName
     * @param {string} dynamicUrl
     * @returns {jQuery}
     */
    function createBoxFile(fileName, dynamicUrl) {
        var $box = $('<div class="n2_modal_filesystem_box n2_modal_filesystem_box--file" title="' + fileName + '"><div class="n2-button n2-button-icon n2-button-s n2-button-blue"><i class="ssi_16 ssi_16--check"></i></div><div class="n2_modal_filesystem_box__label">' + fileName + '</div></div>');
        var ext = fileName.split('.').pop();
        if (ext !== 'mp4' && ext !== 'mp3') {
            $('<div class="n2_modal_filesystem_box__image"></div>')
                .css('background-image', 'url(' + encodeURI(_N2._imageHelper.fixed(dynamicUrl)) + ')')
                .prependTo($box);
        } else {
            $('<div class="n2_modal_filesystem_box__icon"><i class="ssi_64 ssi_64--dummy"></i></div>')
                .prependTo($box);
        }

        return $box;
    }

    /**
     * @memberOf _N2
     *
     * @param url
     * @param uploadAllowed
     * @constructor
     */
    function NextendBrowse(url, uploadAllowed) {
        this.url = url;
        this.uploadAllowed = parseInt(uploadAllowed);
        this.currentPath = localStorage.getItem('browsePath') || '';

        this.listeners = [];

        var timeout = null;
        this.node = $('<div class="n2_modal_filesystem__content"></div>')
            .on('dragover', function (e) {
                if (timeout !== null) {
                    clearTimeout(timeout);
                    timeout = null;
                } else {
                    $(e.currentTarget).addClass('n2-drag-over');
                }
                timeout = setTimeout(function () {
                    $(e.currentTarget).removeClass('n2-drag-over');
                    timeout = null;
                }, 400);

            });
        _N2._browse = this;
    }

    NextendBrowse.prototype.clear = function () {
        _removeEventListeners(this.listeners);

        this.node.empty();
    };

    NextendBrowse.prototype.getNode = function (mode, callback) {
        this.clear();
        this.node.attr('data-mode', mode);
        this.mode = mode;
        if (mode === 'multiple') {
            this.selected = [];
        }
        this.callback = callback;
        this._loadPath(this.getCurrentFolder(), this._renderBoxes.bind(this));
        return this.node;
    };

    NextendBrowse.prototype._renderBoxes = function (data) {
        this.clear();

        localStorage.setItem('browsePath', this.getCurrentFolder());

        $('<div class="n2_modal_filesystem__current_path">' + n2_('Current path') + ': ' + data.fullPath + '</div>')
            .appendTo(this.node);

        if (this.uploadAllowed) {
            this.initUpload();
        }


        if (data.path !== '') {
            createBoxDirectoryUp()
                .appendTo(this.node)
                .on('click', (function (directory) {
                    this._loadPath(directory, this._renderBoxes.bind(this))
                }).bind(this, data.path + '/..'));
        }
        for (var directoryName in data.directories) {
            if (data.directories.hasOwnProperty(directoryName)) {
                createBoxDirectory(directoryName)
                    .appendTo(this.node)
                    .on('click', (function (directory) {
                        this._loadPath(directory, this._renderBoxes.bind(this))
                    }).bind(this, data.directories[directoryName]));
            }
        }
        for (var fileName in data.files) {
            if (data.files.hasOwnProperty(fileName)) {

                var box = createBoxFile(fileName, data.files[fileName])
                    .appendTo(this.node)
                    .on('click', this.clickImage.bind(this, data.files[fileName]));

                if (this.mode === 'multiple') {
                    if (this.selected.indexOf(data.files[fileName]) !== -1) {
                        box.addClass('n2_modal_filesystem_box--active');
                    }
                }
            }
        }
    };


    NextendBrowse.prototype._loadPath = function (path, callback) {
        if (cache[path] === undefined) {
            cache[path] = _N2.AjaxHelper.ajax({
                type: "POST",
                url: _N2.AjaxHelper.makeAjaxUrl(this.url),
                data: {
                    path: path
                },
                dataType: 'json'
            });
        }
        $.when(cache[path]).done((function (response) {
            this.currentPath = response.data.path;
            cache[response.data.path] = response;
            cache[path] = response;
            callback(response.data);
        }).bind(this));

    };

    NextendBrowse.prototype.initUpload = function () {
        createBoxUpload()
            .appendTo(this.node);


        var dropAreaElement = document.getElementById('n2-browse-upload');

        this.listeners.push(_addEventListenerWithRemover(dropAreaElement, 'dragover', (function (e) {
            e.preventDefault();
        }).bind(this)));
        this.listeners.push(_addEventListenerWithRemover(dropAreaElement, 'drop', (function (e) {
            e.preventDefault();
            var files = [];
            if (e.dataTransfer.items) {
                // Use DataTransferItemList interface to access the file(s)
                for (var i = 0; i < e.dataTransfer.items.length; i++) {
                    // If dropped items aren't files, reject them
                    if (e.dataTransfer.items[i].kind === 'file') {
                        files.push(e.dataTransfer.items[i].getAsFile());
                    }
                }
            } else {
                // Use DataTransfer interface to access the file(s)
                for (var i = 0; i < e.dataTransfer.files.length; i++) {
                    files.push(e.dataTransfer.files[i]);
                }
            }

            if (files.length) {
                var promises = [];
                _N2.LoadingScreen.startLoading();

                for (var i = 0; i < files.length; i++) {

                    promises.push(new Promise((function (resolve, reject) {
                        var xhr = new XMLHttpRequest(),
                            formData = new FormData();
                        xhr.open('POST', _N2.AjaxHelper.makeAjaxUrl(this.url, {
                            nextendaction: 'upload'
                        }), true);
                        xhr.onload = (function () {
                            var response = JSON.parse(xhr.response);
                            if (response.data && response.data.name) {

                                cache[response.data.path].data.files[response.data.name] = response.data.url;

                                createBoxFile(response.data.name, response.data.url)
                                    .appendTo(this.node)
                                    .on('click', this.clickImage.bind(this, response.data.url));

                            } else {
                                _N2.AjaxHelper.notification(response);
                            }

                            resolve();
                        }).bind(this);
                        xhr.onerror = function () {
                            reject();
                        };
                        formData.append('media', files[i]);
                        formData.append('path', this.currentPath);
                        xhr.send(formData)
                    }).bind(this)));

                }

                Promise.all(promises).finally((function () {

                    _N2.LoadingScreen.stopLoading();
                }).bind(this));
            }
        }).bind(this)));
    };

    NextendBrowse.prototype.clickImage = function (image, e) {
        if (this.mode === 'single') {
            this.callback(image);
        } else if (this.mode === 'multiple') {
            var i = this.selected.indexOf(image);
            if (i === -1) {
                $(e.currentTarget).addClass('n2_modal_filesystem_box--active');
                this.selected.push(image);
            } else {
                $(e.currentTarget).removeClass('n2_modal_filesystem_box--active');
                this.selected.splice(i, 1);
            }
        }
    };

    NextendBrowse.prototype.getSelected = function () {
        return this.selected;
    };

    NextendBrowse.prototype.getCurrentFolder = function () {
        return this.currentPath;
    };


    return NextendBrowse;
});_N2.d('IconPack', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function IconPack(manager, data) {
        this.currentQuery = '';

        this.manager = manager;
        this.data = data;
        this.$li = $('<div class="n2_modal_icons__list_item">' + data.label + '</div>')
            .on('click', (function (e) {
                e.preventDefault();
                this.activate();
            }).bind(this))
            .appendTo(this.manager.$list);
    }

    IconPack.prototype.load = function () {

        if (this.data.isLoaded === undefined) {
            $("head").append("<link rel='stylesheet' href='" + this.data.css + "' type='text/css' media='screen'>");
            this.data.isLoaded = true;
        }
    };

    IconPack.prototype.render = function () {
        if (this.table === undefined) {

            /**
             * @type {{$table: jQuery, $content: jQuery}}
             */
            this.table = _N2.UI.modalFormTable(this.data.label);

            for (var icon in this.data.data) {
                $('<div class="n2_modal_icons__icon" data-identifier="' + this.data.id + ':' + icon + '" data-kw="' + this.data.data[icon].kw.toLowerCase() + '">' + this._render(icon) + '</div>')
                    .on('click', (function (e) {
                        this.manager.selectIcon($(e.currentTarget).data('identifier'), e);
                    }).bind(this))
                    .appendTo(this.table.$content);
            }

            this.$icons = this.table.$content.find('.n2_modal_icons__icon');
        }
    };

    IconPack.prototype.activate = function () {
        this.manager.activate(this);

        this.$li.addClass('n2_modal_icons__list_item--active');
        this.load();
        this.render();


        this.search(this.manager.getQuery());

        this.manager.$content.append(this.table.$table);
    };

    IconPack.prototype.search = function (query) {

        if (this.currentQuery !== query) {
            if (query.length <= 1) {
                this.$icons.css('display', '');
            } else {
                var $matched = this.$icons.filter("[data-kw*='" + query + "']");
                this.$icons.not($matched).css('display', 'none');
                $matched.css('display', '');
            }
        }

        this.currentQuery = query;
    };

    IconPack.prototype.deActivate = function () {
        this.$li.removeClass('n2_modal_icons__list_item--active');
        this.table.$table.detach();
    };

    IconPack.prototype._render = function (icon) {

        if (this.data.isLigature) {
            return '<i class="n2i ' + this.data.class + '">' + icon + '</i>';
        }

        return '<i class="n2i ' + this.data.class + " " + this.data.prefix + icon + '"></i>';
    };

    IconPack.prototype.getIcon = function (icon) {
        if (this.data.isLigature) {

            return {
                "class": this.data.class,
                "ligature": icon
            };
        }

        return {
            "class": this.data.class + " " + this.data.prefix + icon,
            "ligature": ""
        };
    };

    return IconPack;
});_N2.d('Icons', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param data
     * @constructor
     */
    function Icons(data) {
        _N2.Icons = this;

        this.data = data;

        this.$list = $('<div class="n2_modal_icons__list"></div>');
        this.iconPacks = {};

        for (var k in this.data) {

            this.iconPacks[this.data[k].id] = new _N2.IconPack(this, this.data[k]);
        }

        this.defaultId = Object.keys(this.iconPacks)[0];
    }

    Icons.prototype.render = function (key) {
        var parts = key.split(':');
        if (parts.length !== 2) {
            return false;
        }
        var id = parts[0],
            icon = parts[1];
        if (this.iconPacks[id] === undefined) {
            return false;
        }

        var iconPack = this.iconPacks[id];
        if (iconPack.data.data[icon] === undefined) {
            return false;
        }

        iconPack.load();

        return iconPack.getIcon(icon);
    };

    Icons.prototype.showModal = function (cb, value) {
        this.callback = cb;

        var isCreate = false;
        if (this.modal === undefined) {
            isCreate = true;
            this.startModal();
        }

        var hasActive = false;
        var selected = value.split(':');
        if (this.iconPacks[selected[0]] !== undefined) {
            this.iconPacks[selected[0]].activate();
            hasActive = true;
        }

        if (isCreate && !hasActive) {
            this.iconPacks[this.defaultId].activate();
        }

        this.modal.show();
    };

    Icons.prototype.selectIcon = function (icon, e) {
        this.callback(icon);

        this.modal.hide(e);
    };

    Icons.prototype.startModal = function () {

        this.modal = new _N2.ModalLightbox(n2_('Icons'), 'icons');

        var $sidebar = $('<div class="n2_modal__sidebar n2_form--dark"></div>');
        this.modal.addContent($sidebar);

        this.$sidebar = $('<div class="n2_modal__sidebar_inner"></div>')
            .appendTo($sidebar);


        var $searchArea = $('<div class="n2_modal_icons__search_area"></div>')
            .appendTo(this.$sidebar);

        this.$searchField = _N2.UI.modalFieldText($searchArea, 'search-icon', false, '')
            .attr('placeholder', n2_('Search'))
            .on('keyup', (function (e) {
                this.activePack.search(this.$searchField.val());
            }).bind(this));

        this.$list.appendTo(this.$sidebar);


        var $content = $('<div class="n2_modal__content"></div>');
        this.$content = $('<div class="n2_modal__content_inner"></div>')
            .appendTo($content);

        this.modal.addContent($content);
    };

    Icons.prototype.activate = function (iconPack) {
        if (this.activePack !== undefined) {
            this.activePack.deActivate();
        }

        this.activePack = iconPack;
    };

    Icons.prototype.getQuery = function () {
        return this.$searchField.val();
    };

    return Icons;
});_N2.d('NextendFontServiceGoogle', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param style
     * @param fonts
     * @param loadedFamilies
     * @constructor
     */
    function NextendFontServiceGoogle(style, fonts, loadedFamilies) {
        this.style = style;
        this.fonts = fonts;
        this.loadedFamilies = loadedFamilies;
        $(window).on('n2Family', this.loadFamily.bind(this));
    }

    NextendFontServiceGoogle.prototype.loadFamily = function (e, family) {
        var familyLower = family.toLowerCase();
        if (this.fonts[familyLower] !== undefined && this.loadedFamilies.indexOf(this.fonts[familyLower]) === -1) {
            $('<link>').attr({
                rel: 'stylesheet',
                type: 'text/css',
                href: '//fonts.googleapis.com/css?family=' + encodeURIComponent(this.fonts[familyLower] + ':' + this.style)
            }).appendTo($('head'));

            this.loadedFamilies.push(this.fonts[familyLower]);

            return this.fonts[familyLower];
        }
        return family;
    };

    return NextendFontServiceGoogle;
});_N2.d('NextendFontEditor', ['$', 'NextendFragmentEditor'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendFontEditor() {
        _N2.NextendFragmentEditor.prototype.constructor.apply(this, arguments);

        this.fields = {
            family: {
                element: $('#n2-font-editorfamily'),
                events: {
                    'nextendChange.n2-editor': this.changeFamily.bind(this)
                }
            },
            color: {
                element: $('#n2-font-editorcolor'),
                events: {
                    'outsideChange.n2-editor': this.changeColor.bind(this)
                }
            },
            size: {
                element: $('#n2-font-editorsize'),
                events: {
                    'outsideChange.n2-editor': this.changeSize.bind(this)
                }
            },
            lineHeight: {
                element: $('#n2-font-editorlineheight'),
                events: {
                    'outsideChange.n2-editor': this.changeLineHeight.bind(this)
                }
            },
            weight: {
                element: $('#n2-font-editorweight'),
                events: {
                    'outsideChange.n2-editor': this.changeWeight.bind(this)
                }
            },
            decoration: {
                element: $('#n2-font-editordecoration'),
                events: {
                    'outsideChange.n2-editor': this.changeDecoration.bind(this)
                }
            },
            align: {
                element: $('#n2-font-editortextalign'),
                events: {
                    'outsideChange.n2-editor': this.changeAlign.bind(this)
                }
            },
            shadow: {
                element: $('#n2-font-editortshadow'),
                events: {
                    'outsideChange.n2-editor': this.changeShadow.bind(this)
                }
            },
            letterSpacing: {
                element: $('#n2-font-editorletterspacing'),
                events: {
                    'outsideChange.n2-editor': this.changeLetterSpacing.bind(this)
                }
            },
            wordSpacing: {
                element: $('#n2-font-editorwordspacing'),
                events: {
                    'outsideChange.n2-editor': this.changeWordSpacing.bind(this)
                }
            },
            textTransform: {
                element: $('#n2-font-editortexttransform'),
                events: {
                    'outsideChange.n2-editor': this.changeTextTransform.bind(this)
                }
            },
            css: {
                element: $('#n2-font-editorextracss'),
                events: {
                    'outsideChange.n2-editor': this.changeCSS.bind(this)
                }
            }
        };
    }

    NextendFontEditor.prototype = Object.create(_N2.NextendFragmentEditor.prototype);
    NextendFontEditor.prototype.constructor = NextendFontEditor;

    NextendFontEditor.prototype.load = function (values) {
        this._off();
        var family = values.afont.split('||'); // split for a while for compatibility
        this.fields.family.element.data('field').insideChange(family[0]);

        this.fields.color.element.data('field').insideChange(values.color);
        this.fields.size.element.data('field').insideChange(values.size
            .split('||')
            .join('|*|')
        );

        this.fields.lineHeight.element.data('field').insideChange(values.lineheight);
        this.fields.weight.element.data('field').insideChange(values.weight);
        this.fields.decoration.element.data('field').insideChange([
            values.italic == 1 ? 'italic' : '',
            values.underline == 1 ? 'underline' : ''
        ].join('||'));

        this.fields.align.element.data('field').insideChange(values.align);
        this.fields.shadow.element.data('field').insideChange(values.tshadow.replace(/\|\|px/g, ''));
        this.fields.letterSpacing.element.data('field').insideChange(values.letterspacing);
        this.fields.wordSpacing.element.data('field').insideChange(values.wordspacing);
        this.fields.textTransform.element.data('field').insideChange(values.texttransform);
        this.fields.css.element.data('field').insideChange(values.extra);

        this._on();
    };

    NextendFontEditor.prototype.changeFamily = function () {
        this.trigger('afont', this.fields.family.element.val());
    };

    NextendFontEditor.prototype.changeColor = function () {
        this.trigger('color', this.fields.color.element.val());
    };

    NextendFontEditor.prototype.changeSize = function () {
        this.trigger('size', this.fields.size.element.val().replace('|*|', '||'));
    };

    NextendFontEditor.prototype.changeLineHeight = function () {
        this.trigger('lineheight', this.fields.lineHeight.element.val());
    };

    NextendFontEditor.prototype.changeWeight = function () {
        this.trigger('weight', this.fields.weight.element.val());
    };

    NextendFontEditor.prototype.changeDecoration = function () {
        var value = this.fields.decoration.element.val();

        var italic = 0;
        if (value.indexOf('italic') != -1) {
            italic = 1;
        }
        this.trigger('italic', italic);

        var underline = 0;
        if (value.indexOf('underline') != -1) {
            underline = 1;
        }
        this.trigger('underline', underline);
    };

    NextendFontEditor.prototype.changeAlign = function () {
        this.trigger('align', this.fields.align.element.val());
    };

    NextendFontEditor.prototype.changeShadow = function () {
        this.trigger('tshadow', this.fields.shadow.element.val());
    };

    NextendFontEditor.prototype.changeLetterSpacing = function () {
        this.trigger('letterspacing', this.fields.letterSpacing.element.val());
    };

    NextendFontEditor.prototype.changeWordSpacing = function () {
        this.trigger('wordspacing', this.fields.wordSpacing.element.val());
    };

    NextendFontEditor.prototype.changeTextTransform = function () {
        this.trigger('texttransform', this.fields.textTransform.element.val());
    };

    NextendFontEditor.prototype.changeCSS = function () {
        this.trigger('extra', this.fields.css.element.val());
    };

    return NextendFontEditor;
});_N2.d('NextendFontEditorController', ['$', 'NextendFragmentEditorController'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments {_N2.NextendFragmentEditorController}
     */
    function NextendFontEditorController() {

        /**
         *
         * @type {_N2.CssInjection[]}
         */
        this.css = [];

        _N2.NextendFragmentEditorController.prototype.constructor.apply(this, arguments);

        this.preview = $('<div class="n2_form__table_preview"></div>')
            .css('fontSize', _N2.CSSRendererFont.fontSize + 'px')
            .appendTo('.n2_form__table[data-field="table-font-preview"]');

        this.initBackgroundColor();
    }

    NextendFontEditorController.prototype = Object.create(_N2.NextendFragmentEditorController.prototype);
    NextendFontEditorController.prototype.constructor = NextendFontEditorController;

    NextendFontEditorController.prototype.loadDefaults = function () {
        _N2.NextendFragmentEditorController.prototype.loadDefaults.call(this);
        this.type = 'font';
        this.preview = null;
    };

    NextendFontEditorController.prototype.initEditor = function () {
        return new _N2.NextendFontEditor();
    };

    NextendFontEditorController.prototype.getCleanVisual = function () {
        return _N2.CSSRendererFont.getEmpty();
    };

    NextendFontEditorController.prototype.initBackgroundColor = function () {

        this.$background = $('#n2-font-editorpreview-background').on('nextendChange', (function () {
            this.preview.css('background', '#' + this.$background.val());
        }).bind(this));
        this.preview.css('background', '#' + this.$background.val());
    };

    NextendFontEditorController.prototype.getTabs = function () {
        return _N2.CSSRendererFont.rendererModes[this.mode].tabs;
    };

    NextendFontEditorController.prototype.renderCSS = function () {
        this._renderTimeout = false;

        this.clearCSS();

        var cssInjection = _N2.CSSRendererFont.addCSS('', this.getPreviewCssClass(), this.currentVisual, _N2.CSSRendererFont.rendererModes[this.mode]);
        this.css.push(cssInjection);
    };

    NextendFontEditorController.prototype.setPreview = function () {

        var data = {
            fontClassName: this.getPreviewCssClass(),
            styleClassName: _N2._fontManager.styleClassName,
            styleClassName2: _N2._fontManager.styleClassName2
        };

        var html = '';
        if (this.localModePreview[this.mode] !== undefined) {
            this.preview.html('');

            this.preview.append(_N2[this.localModePreview[this.mode]].renderFont(_N2._fontManager.fieldID, data));
        } else {
            html = _N2.CSSRendererFont.rendererModes[this.mode].preview;

            html = html.replace(/\{([^]*?)\}/g, function (match, script) {
                return data[script];
            });

            this.preview.html(html);
        }
    };

    NextendFontEditorController.prototype.getPreviewCssClass = function () {
        return 'n2-' + this.type + '-editor-preview';
    };

    NextendFontEditorController.prototype.pause = function () {
        this.clearCSS();
    };

    NextendFontEditorController.prototype.clearCSS = function () {
        for (var i = 0; i < this.css.length; i++) {
            this.css[i].destroy();
        }
        this.css = [];
    };

    return NextendFontEditorController;
});_N2.d('NextendFontManager', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendFontManager() {

        this.$ = $(this);

        this.showParameters = false;

        this.type = 'font';

        /**
         *
         * @type {_N2.CssInjection[]}
         */
        this.css = [];

        this.styleClassName = '';
        this.styleClassName2 = '';
        /**
         *
         * @type {_N2.NextendFontManager}
         */
        _N2._fontManager = this;

        this.lightbox = $('#n2-lightbox-' + this.type);

        this.lightbox.find('.n2_fullscreen_editor__overlay')
            .on('click', this.hide.bind(this));


        this.controller = this.initController();

        this.lightbox.find('.n2_fullscreen_editor__cancel')
            .on('click', this.hide.bind(this));

        this.saveButton = this.lightbox.find('.n2_fullscreen_editor__save')
            .on('click', this.setVisualAsStatic.bind(this));
    }

    NextendFontManager.prototype.initController = function () {
        return new _N2.NextendFontEditorController();
    };

    NextendFontManager.prototype.setFieldID = function (fieldID) {
        this.fieldID = fieldID;
    };

    NextendFontManager.prototype.setConnectedStyle = function (styleId) {
        /**
         * @type {_N2.CssInjection}
         */
        var cssInjection = $('#' + styleId).data('field').renderStyle();
        this.css.push(cssInjection);
        this.styleClassName = cssInjection.getBaseClassName();
    };

    NextendFontManager.prototype.setConnectedStyle2 = function (styleId) {
        /**
         * @type {_N2.CssInjection}
         */
        var cssInjection = $('#' + styleId).data('field').renderStyle();
        this.css.push(cssInjection);
        this.styleClassName2 = cssInjection.getBaseClassName();
    };

    NextendFontManager.prototype.setAndClose = function (data) {
        this.$.trigger('save', [data]);
    };

    NextendFontManager.prototype.setVisualAsStatic = function (e) {
        e.preventDefault();
        this.setAndClose(JSON.stringify({
            data: this.controller.get('set')
        }));
        this.hide(e);
    };

    NextendFontManager.prototype.hide = function (e) {
        this.controller.pause();

        for (var i = 0; i < this.css.length; i++) {
            this.css[i].destroy();
        }
        this.css = [];

        if (e !== undefined) {
            e.preventDefault();
            _N2.Esc.pop();
        }
        this.controller.close();
        this.$.off('save');
        $('body').removeClass('n2_body--fullscreen-editor-visible');
        this.lightbox.removeClass('n2_fullscreen_editor--visible');

        $(window).off('keydown.visual');
    };

    NextendFontManager.prototype.loadDataToController = function (rawData) {

        this.controller.load(_N2.CSSRendererFont.parseRawData(rawData), false, this.showParameters);
    };

    NextendFontManager.prototype.show = function (data, saveCallback, showParameters) {
        _N2.Esc.add((function () {
            this.hide();
            return true;
        }).bind(this));

        this.showParameters = _Assign({
            previewMode: false,
            previewHTML: false
        }, showParameters);

        $('body').addClass('n2_body--fullscreen-editor-visible');
        this.lightbox.addClass('n2_fullscreen_editor--visible');

        this.loadDataToController(data);
        this.controller.show();

        this.$.on('save', saveCallback);

        this._show();
    };

    NextendFontManager.prototype._show = function () {
        $(':focus').trigger("blur");

        $(window).on({
            'keydown.visual': (function (e) {
                if (e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'INPUT' && !e.target.isContentEditable) {
                    if (e.code === 'Enter') {
                        this.saveButton.trigger('click');
                    }
                }
            }).bind(this)
        });
    };

    return NextendFontManager;

});_N2.d('CSSRendererFont', ['$', 'CSSRenderer'], function () {
    var $ = _N2.$;

    /**
     *
     * @constructor
     * @augments _N2.CSSRenderer
     */
    function RendererFont() {
    }

    RendererFont.prototype = Object.create(_N2.CSSRenderer.prototype);
    RendererFont.prototype.constructor = RendererFont;

    RendererFont.prototype.getCSS = function (pre, selector, visualTabs, cssStructure, parameters) {

        visualTabs = this.decodeData(visualTabs);

        if (visualTabs === false) {
            return '';
        }

        visualTabs = _Assign([], visualTabs);
        visualTabs[0] = _Assign(_N2.CSSRendererFont.getDefault(), visualTabs[0]);

        if (cssStructure.renderOptions.combined) {
            for (var i = 1; i < visualTabs.length; i++) {
                visualTabs[i] = _Assign({}, visualTabs[i - 1], visualTabs[i]);
                if (visualTabs[i].size === visualTabs[0].size) {
                    visualTabs[i].size = '100||%';
                } else {
                    var size1 = visualTabs[0].size.split('||'),
                        size2 = visualTabs[i].size.split('||');
                    if (size1.length === 2 && size2.length === 2 && size1[1] === 'px' && size2[1] === 'px') {
                        visualTabs[i].size = Math.round(size2[0] / size1[0] * 100) + '||%';
                    }
                }
            }
        }

        if (pre === undefined) {
            pre = _N2.CSSRendererFont.pre;
        }

        return _N2.CSSRenderer.prototype.getCSS.call(this, pre, selector, visualTabs, cssStructure, parameters);
    };

    RendererFont.prototype.makeStylecolor = function (value, target) {
        target.color = '#' + value.substr(0, 6) + ";\ncolor: " + N2Color.hex2rgbaCSS(value);
    };

    RendererFont.prototype.makeStylesize = function (value, target) {
        var fontSize = value.split('||');
        if (fontSize[1] === 'px') {
            target.fontSize = (fontSize[0] / _N2.CSSRendererFont.fontSize * 100) + '%';
        } else {
            target.fontSize = value.replace('||', '');
        }
    };

    RendererFont.prototype.makeStyletshadow = function (value, target) {
        var ts = value.split('|*|');
        if (ts[0] == '0' && ts[1] == '0' && ts[2] == '0') {
            target.textShadow = 'none';
        } else {
            target.textShadow = ts[0] + 'px ' + ts[1] + 'px ' + ts[2] + 'px ' + N2Color.hex2rgbaCSS(ts[3]);
        }
    };

    RendererFont.prototype.makeStyleafont = function (value, target) {
        var families = value.split(',');
        for (var i = 0; i < families.length; i++) {
            families[i] = this.getFamily(families[i]
                .replace(/^\s+|\s+$/gm, '')
                .replace(/"|'/gm, ''));
        }
        target.fontFamily = families.join(',');
    };

    RendererFont.prototype.getFamily = function (family) {
        var translatedFamily = $(window).triggerHandler('n2Family', [family]);
        if (translatedFamily === undefined) {
            translatedFamily = family;
        }
        return "'" + translatedFamily + "'";
    };

    RendererFont.prototype.makeStylelineheight = function (value, target) {

        target.lineHeight = value;
    };

    RendererFont.prototype.makeStyleweight =
        RendererFont.prototype.makeStylebold = function (value, target) {
            if (value == 1) {
                target.fontWeight = 'bold';
            } else if (value > 1) {
                target.fontWeight = value;
            } else {
                target.fontWeight = 'normal';
            }
        };

    RendererFont.prototype.makeStyleitalic = function (value, target) {
        if (value == 1) {
            target.fontStyle = 'italic';
        } else {
            target.fontStyle = 'normal';
        }
    };

    RendererFont.prototype.makeStyleunderline = function (value, target) {
        if (value == 1) {
            target.textDecoration = 'underline';
        } else {
            target.textDecoration = 'none';
        }
    };

    RendererFont.prototype.makeStylealign = function (value, target) {

        target.textAlign = value;
    };

    RendererFont.prototype.makeStyleletterspacing = function (value, target) {
        target.letterSpacing = value;
    };

    RendererFont.prototype.makeStylewordspacing = function (value, target) {
        target.wordSpacing = value;
    };

    RendererFont.prototype.makeStyletexttransform = function (value, target) {
        target.textTransform = value;
    };

    RendererFont.prototype.makeStyleextra = function (value, target) {

        target.raw = value;
    };

    var counter = 1,
        renderer = new RendererFont(),
        /**
         * @memberOf _N2
         *
         * @type {{getClass: (function(): string), pre: string, parseRawData: parseRawData, rendererModes: {}, transformData: (function(*=, *): []), getEmpty: (function(): {extra: string}), addCSS: (function(*=, *=, *=, *=, *=): _N2.CssInjection), defaultFamily: string, fixBold: (function(*): *), fontSize: number, getDefault: (function(): {tshadow: string, color: string, underline: number, letterspacing: string, lineheight: string, weight: number, align: string, italic: number, texttransform: string, size: string, afont: *, extra: string, wordspacing: string})}}
         */
        CSSRendererFont = {
            pre: '',
            defaultFamily: 'Roboto,Arial',
            fontSize: 16,
            rendererModes: {},
            getDefault: function () {
                return {
                    color: "000000ff",
                    size: "14||px",
                    tshadow: "0|*|0|*|0|*|000000ff",
                    afont: CSSRendererFont.defaultFamily,
                    lineheight: "1.5",
                    weight: 400,
                    italic: 0,
                    underline: 0,
                    align: "left",
                    letterspacing: "normal",
                    wordspacing: "normal",
                    texttransform: "none",
                    extra: ""
                };
            },
            getEmpty: function () {
                return {
                    extra: ''
                };
            },
            getClass: function () {

                return 'n2-font-' + (counter++);
            },
            /**
             *
             * @param pre
             * @param className
             * @param visualTabs
             * @param cssStructure
             * @param parameters
             * @returns {_N2.CssInjection}
             */
            addCSS: function (pre, className, visualTabs, cssStructure, parameters) {
                return _N2._css.add(className, renderer.getCSS.call(renderer, pre, '.' + className, visualTabs, cssStructure, parameters));
            },
            fixBold: function (visual) {
                for (var i = 0; i < visual.length; i++) {
                    if (visual[i].bold !== undefined) {
                        if (visual[i].weight !== undefined) {
                            delete visual[i].bold;
                        } else {
                            if (parseInt(visual[i].bold) === 1) {
                                visual[i].weight = 700;
                            } else if (visual[i].bold > 0) {
                                visual[i].weight = visual[i].bold;
                            }
                            delete visual[i].bold;
                        }
                    }
                }

                return visual;
            },
            parseRawData: function (rawData) {

                if (rawData.length > 0) {
                    if (rawData[0] !== '{') {
                        rawData = _N2.Base64.decode(rawData)
                    }

                    try {
                        var visual = JSON.parse(rawData);
                        if (visual.data !== undefined) {

                            visual.data = this.fixBold(visual.data);
                            visual.data[0] = _Assign({}, this.getDefault(), visual.data[0]);

                            return visual.data;
                        }
                    } catch (e) {
                        console.error('Unexpected data', rawData, e);
                    }
                }

                return [this.getDefault()];
            },
            transformData: function (rawData, mode) {

                var visual = this.parseRawData(rawData),
                    tabs = this.rendererModes[mode].tabs,
                    currentVisual = [],
                    i;
                for (i = 0; i < visual.length; i++) {
                    currentVisual[i] = _AssignRecursive(this.getEmpty(), visual[i]);
                }

                /**
                 * If there is more tabs than in the visual itself
                 */
                for (i = currentVisual.length; i < tabs.length; i++) {
                    currentVisual[i] = this.getEmpty();
                }

                return currentVisual;
            }
        };

    return CSSRendererFont;
});_N2.d('NextendStyleEditor', ['$', 'NextendFragmentEditor'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendStyleEditor() {

        _N2.NextendFragmentEditor.prototype.constructor.apply(this, arguments);

        this.fields = {
            backgroundColor: {
                element: $('#n2-style-editorbackgroundcolor'),
                events: {
                    'nextendChange.n2-editor': this.changeBackgroundColor.bind(this)
                }
            },
            opacity: {
                element: $('#n2-style-editoropacity'),
                events: {
                    'outsideChange.n2-editor': this.changeOpacity.bind(this)
                }
            },
            padding: {
                element: $('#n2-style-editorpadding'),
                events: {
                    'outsideChange.n2-editor': this.changePadding.bind(this)
                }
            },
            boxShadow: {
                element: $('#n2-style-editorboxshadow'),
                events: {
                    'outsideChange.n2-editor': this.changeBoxShadow.bind(this)
                }
            },
            border: {
                element: $('#n2-style-editorborder'),
                events: {
                    'outsideChange.n2-editor': this.changeBorder.bind(this)
                }
            },
            borderRadius: {
                element: $('#n2-style-editorborderradius'),
                events: {
                    'outsideChange.n2-editor': this.changeBorderRadius.bind(this)
                }
            },
            extracss: {
                element: $('#n2-style-editorextracss'),
                events: {
                    'outsideChange.n2-editor': this.changeExtraCSS.bind(this)
                }
            }
        };
    }

    NextendStyleEditor.prototype = Object.create(_N2.NextendFragmentEditor.prototype);
    NextendStyleEditor.prototype.constructor = NextendStyleEditor;

    NextendStyleEditor.prototype.load = function (values) {
        this._off();
        this.fields.backgroundColor.element.data('field').insideChange(values.backgroundcolor);
        this.fields.opacity.element.data('field').insideChange(values.opacity);
        this.fields.padding.element.data('field').insideChange(values.padding);
        this.fields.boxShadow.element.data('field').insideChange(values.boxshadow);
        this.fields.border.element.data('field').insideChange(values.border);
        this.fields.borderRadius.element.data('field').insideChange(values.borderradius);
        this.fields.extracss.element.data('field').insideChange(values.extra);
        this._on();
    };

    NextendStyleEditor.prototype.changeBackgroundColor = function () {
        this.trigger('backgroundcolor', this.fields.backgroundColor.element.val());

    };

    NextendStyleEditor.prototype.changeOpacity = function () {
        this.trigger('opacity', this.fields.opacity.element.val());
    };

    NextendStyleEditor.prototype.changePadding = function () {
        this.trigger('padding', this.fields.padding.element.val());
    };

    NextendStyleEditor.prototype.changeBoxShadow = function () {
        this.trigger('boxshadow', this.fields.boxShadow.element.val());
    };

    NextendStyleEditor.prototype.changeBorder = function () {
        this.trigger('border', this.fields.border.element.val());
    };

    NextendStyleEditor.prototype.changeBorderRadius = function () {
        this.trigger('borderradius', this.fields.borderRadius.element.val());
    };

    NextendStyleEditor.prototype.changeExtraCSS = function () {
        this.trigger('extra', this.fields.extracss.element.val());
    };

    return NextendStyleEditor;
});_N2.d('NextendStyleEditorController', ['$', 'NextendFragmentEditorController'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendStyleEditorController() {

        /**
         *
         * @type {_N2.CssInjection[]}
         */
        this.css = [];

        _N2.NextendFragmentEditorController.prototype.constructor.apply(this, arguments);

        this.preview = $('<div class="n2_form__table_preview"></div>')
            .css('fontSize', '16px')
            .appendTo('.n2_form__table[data-field="table-style-preview"]');

        this.initBackgroundColor();
    }

    NextendStyleEditorController.prototype = Object.create(_N2.NextendFragmentEditorController.prototype);
    NextendStyleEditorController.prototype.constructor = NextendStyleEditorController;

    NextendStyleEditorController.prototype.loadDefaults = function () {
        _N2.NextendFragmentEditorController.prototype.loadDefaults.call(this);
        this.type = 'style';
        this.preview = null;
    };

    NextendStyleEditorController.prototype.initEditor = function () {
        return new _N2.NextendStyleEditor();
    };

    NextendStyleEditorController.prototype.getCleanVisual = function () {
        return _N2.CSSRendererStyle.getEmpty();
    };

    NextendStyleEditorController.prototype.initBackgroundColor = function () {

        this.$background = $('#n2-style-editorpreview-background').on('nextendChange', (function () {
            this.preview.css('background', '#' + this.$background.val());
        }).bind(this));
        this.preview.css('background', '#' + this.$background.val());
    };

    NextendStyleEditorController.prototype.getTabs = function () {
        return _N2.CSSRendererStyle.rendererModes[this.mode].tabs;
    };

    NextendStyleEditorController.prototype.renderCSS = function () {
        this._renderTimeout = false;

        this.clearCSS();

        var cssInjection = _N2.CSSRendererStyle.addCSS('', this.getPreviewCssClass(), this.currentVisual, _N2.CSSRendererStyle.rendererModes[this.mode]);
        this.css.push(cssInjection);
    };

    NextendStyleEditorController.prototype.setPreview = function () {

        var data = {
            styleClassName: this.getPreviewCssClass(),
            fontClassName: _N2._styleManager.fontClassName,
            fontClassName2: _N2._styleManager.fontClassName2,
            styleClassName2: _N2._styleManager.styleClassName2
        };

        var html = '';
        if (this.localModePreview[this.mode] !== undefined && this.localModePreview[this.mode] !== '') {
            this.preview.html('');

            this.preview.append(_N2[this.localModePreview[this.mode]].renderStyle(_N2._styleManager.fieldID, data));
        } else {
            html = _N2.CSSRendererStyle.rendererModes[this.mode].preview;

            html = html.replace(/\{([^]*?)\}/g, function (match, script) {
                return data[script];
            });

            this.preview.html(html);
        }
    };

    NextendStyleEditorController.prototype.getPreviewCssClass = function () {
        return 'n2-' + this.type + '-editor-preview';
    };

    NextendStyleEditorController.prototype.pause = function () {
        this.clearCSS();
    };

    NextendStyleEditorController.prototype.clearCSS = function () {
        for (var i = 0; i < this.css.length; i++) {
            this.css[i].destroy();
        }
        this.css = [];
    };

    return NextendStyleEditorController;
});_N2.d('NextendStyleManager', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendStyleManager() {

        this.$ = $(this);

        this.showParameters = false;

        this.type = 'style';

        /**
         *
         * @type {_N2.CssInjection[]}
         */
        this.css = [];

        this.styleClassName2 = '';
        this.fontClassName = '';
        this.fontClassName2 = '';

        /**
         * @type {_N2.NextendStyleManager}
         */
        _N2._styleManager = this;

        this.lightbox = $('#n2-lightbox-' + this.type);

        this.lightbox.find('.n2_fullscreen_editor__overlay')
            .on('click', this.hide.bind(this));

        this.controller = this.initController();
        if (this.controller) {
            this.renderer = this.controller.renderer;
        }

        this.lightbox.find('.n2_fullscreen_editor__cancel')
            .on('click', this.hide.bind(this));

        this.saveButton = this.lightbox.find('.n2_fullscreen_editor__save')
            .on('click', this.setVisualAsStatic.bind(this));
    }

    NextendStyleManager.prototype.initController = function () {
        return new _N2.NextendStyleEditorController();
    };

    NextendStyleManager.prototype.setFieldID = function (fieldID) {
        this.fieldID = fieldID;
    };

    NextendStyleManager.prototype.setConnectedStyle = function (styleId) {
        /**
         * @type {_N2.CssInjection}
         */
        var cssInjection = $('#' + styleId).data('field').renderStyle();
        this.css.push(cssInjection);
        this.styleClassName2 = cssInjection.getBaseClassName();
    };

    NextendStyleManager.prototype.setConnectedFont = function (fontId) {
        /**
         * @type {_N2.CssInjection}
         */
        var cssInjection = $('#' + fontId).data('field').renderFont();
        this.css.push(cssInjection);
        this.fontClassName = cssInjection.getBaseClassName();
    };

    NextendStyleManager.prototype.setConnectedFont2 = function (fontId) {
        /**
         * @type {_N2.CssInjection}
         */
        var cssInjection = $('#' + fontId).data('field').renderFont();
        this.css.push(cssInjection);
        this.fontClassName2 = cssInjection.getBaseClassName();
    };

    NextendStyleManager.prototype.setAndClose = function (data) {
        this.$.trigger('save', [data]);
    };

    NextendStyleManager.prototype.setVisualAsStatic = function (e) {
        e.preventDefault();
        this.setAndClose(JSON.stringify({
            data: this.controller.get('set')
        }));
        this.hide(e);
    };

    NextendStyleManager.prototype.hide = function (e) {
        this.controller.pause();

        for (var i = 0; i < this.css.length; i++) {
            this.css[i].destroy();
        }
        this.css = [];

        if (e !== undefined) {
            e.preventDefault();
            _N2.Esc.pop();
        }
        this.controller.close();
        this.$.off('save');
        $('body').removeClass('n2_body--fullscreen-editor-visible');
        this.lightbox.removeClass('n2_fullscreen_editor--visible');

        $(window).off('keydown.visual');
    };

    NextendStyleManager.prototype.loadDataToController = function (data) {

        this.controller.load(_N2.CSSRendererStyle.parseRawData(data), false, this.showParameters);
    };

    NextendStyleManager.prototype.show = function (data, saveCallback, showParameters) {
        _N2.Esc.add((function () {
            this.hide();
            return true;
        }).bind(this));

        this.showParameters = _Assign({
            previewMode: false,
            previewHTML: false
        }, showParameters);

        $('body').addClass('n2_body--fullscreen-editor-visible');
        this.lightbox.addClass('n2_fullscreen_editor--visible');

        this.loadDataToController(data);
        this.controller.show();

        this.$.on('save', saveCallback);

        this._show();
    };

    NextendStyleManager.prototype._show = function () {
        $(':focus').trigger("blur");

        $(window).on({
            'keydown.visual': (function (e) {
                if (e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'INPUT' && !e.target.isContentEditable) {
                    if (e.code === 'Enter') {
                        this.saveButton.trigger('click');
                    }
                }
            }).bind(this)
        });
    };

    return NextendStyleManager;
});_N2.d('CSSRendererStyle', ['CSSRenderer'], function () {

    /**
     *
     * @constructor
     * @augments _N2.CSSRenderer
     */
    function RendererStyle() {
    }

    RendererStyle.prototype = Object.create(_N2.CSSRenderer.prototype);
    RendererStyle.prototype.constructor = RendererStyle;

    RendererStyle.prototype.getCSS = function (pre, selector, visualTabs, cssStructure, parameters) {

        visualTabs = this.decodeData(visualTabs);

        if (visualTabs === false) {
            return '';
        }

        visualTabs[0] = _Assign(_N2.CSSRendererStyle.getDefault(), visualTabs[0]);

        if (pre === undefined) {
            pre = _N2.CSSRendererStyle.pre;
        }

        return _N2.CSSRenderer.prototype.getCSS.call(this, pre, selector, visualTabs, cssStructure, parameters);
    };

    RendererStyle.prototype.makeStylebackgroundcolor = function (value, target) {
        target.background = '#' + value.substr(0, 6) + ";\n\tbackground: " + N2Color.hex2rgbaCSS(value);
    };

    RendererStyle.prototype.makeStyleopacity = function (value, target) {
        target.opacity = parseInt(value) / 100;
    };

    RendererStyle.prototype.makeStylepadding = function (value, target) {
        var padding = value.split('|*|'),
            unit = padding.pop();
        for (var i = 0; i < padding.length; i++) {
            padding[i] += unit;
        }
        target.padding = padding.join(' ');
    };

    RendererStyle.prototype.makeStyleboxshadow = function (value, target) {
        var s = value.split('|*|');
        if (s[0] == '0' && s[1] == '0' && s[2] == '0' && s[3] == '0') {
            target.boxShadow = 'none';
        } else {
            target.boxShadow = s[0] + 'px ' + s[1] + 'px ' + s[2] + 'px ' + s[3] + 'px ' + N2Color.hex2rgbaCSS(s[4]);
        }
    };

    RendererStyle.prototype.makeStyleborder = function (value, target) {
        var border = value.split('|*|');

        target.borderWidth = border[0] + 'px';
        target.borderStyle = border[1];
        target.borderColor = N2Color.hex2rgbaCSS(border[2]) + ";";
    };

    RendererStyle.prototype.makeStyleborderradius = function (value, target) {
        var radius = value.split('|*|');
        radius.push('');
        target.borderRadius = value + 'px';
    };

    RendererStyle.prototype.makeStyleextra = function (value, target) {

        target.raw = value;
    };

    var counter = 1,
        renderer = new RendererStyle(),
        /**
         * @memberOf _N2
         *
         * @type {{getClass: (function(): string), pre: string, parseRawData: parseRawData, rendererModes: {}, transformData: (function(*=, *): []), getEmpty: (function(): {extra: string}), addCSS: (function(*=, *, *=, *=, *=): _N2.CssInjection), getDefault: (function(): {border: string, padding: string, extra: string, borderradius: string, opacity: number, boxshadow: string, backgroundcolor: string})}}
         */
        CSSRendererStyle = {
            pre: '',
            rendererModes: {},
            getDefault: function () {
                return {
                    backgroundcolor: 'ffffff00',
                    opacity: 100,
                    padding: '0|*|0|*|0|*|0|*|px',
                    boxshadow: '0|*|0|*|0|*|0|*|000000ff',
                    border: '0|*|solid|*|000000ff',
                    borderradius: '0',
                    extra: ''
                };
            },
            getEmpty: function () {
                return {
                    extra: ''
                };
            },
            getClass: function () {

                return 'n2-style-' + (counter++);
            },
            /**
             *
             * @param pre
             * @param className
             * @param visualTabs
             * @param cssStructure
             * @param parameters
             * @returns {_N2.CssInjection}
             */
            addCSS: function (pre, className, visualTabs, cssStructure, parameters) {
                return _N2._css.add(className, renderer.getCSS.call(renderer, pre, '.' + className, visualTabs, cssStructure, parameters));
            },
            parseRawData: function (rawData) {

                if (rawData.length > 0) {
                    if (rawData[0] !== '{') {
                        rawData = _N2.Base64.decode(rawData)
                    }

                    try {
                        var visual = JSON.parse(rawData);
                        if (visual.data !== undefined) {

                            visual.data[0] = _Assign({}, this.getDefault(), visual.data[0]);

                            return visual.data;
                        }
                    } catch (e) {
                        console.error('Unexpected data', rawData, e);
                    }
                }

                return [this.getDefault()];
            },
            transformData: function (rawData, mode) {

                var visual = this.parseRawData(rawData),
                    tabs = this.rendererModes[mode].tabs,
                    currentVisual = [],
                    i;
                for (i = 0; i < visual.length; i++) {
                    currentVisual[i] = _AssignRecursive(this.getEmpty(), visual[i]);
                }

                /**
                 * If there is more tabs than in the visual itself
                 */
                for (i = currentVisual.length; i < tabs.length; i++) {
                    currentVisual[i] = this.getEmpty();
                }

                return currentVisual;
            }
        };

    return CSSRendererStyle;
});_N2.d('NextendImageEditorController', ['NextendFragmentEditorControllerWithEditor'], function () {

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendImageEditorController() {
        _N2.NextendFragmentEditorControllerWithEditor.prototype.constructor.apply(this, arguments);
    }

    NextendImageEditorController.prototype = Object.create(_N2.NextendFragmentEditorControllerWithEditor.prototype);
    NextendImageEditorController.prototype.constructor = NextendImageEditorController;

    NextendImageEditorController.prototype.loadDefaults = function () {
        _N2.NextendFragmentEditorControllerWithEditor.prototype.loadDefaults.call(this);
        this.type = 'image';
        this.currentImage = '';
    };

    NextendImageEditorController.prototype.get = function (type) {
        return this.currentVisual;
    };

    NextendImageEditorController.prototype.getEmptyVisual = function () {
        return {
            'desktop-retina': {
                image: ''
            },
            tablet: {
                image: ''
            },
            mobile: {
                image: ''
            }
        };
    };

    NextendImageEditorController.prototype._load = function (visual, tabs, parameters) {
        this.currentImage = visual.visual.image;
        _N2.NextendFragmentEditorControllerWithEditor.prototype._load.call(this, visual.value, tabs, parameters);
    };

    NextendImageEditorController.prototype.loadToEditor = function () {
        this.currentVisual = _Assign({}, this.getEmptyVisual(), this.currentVisual);
        this.editor.load(this.currentImage, this.currentVisual);
    };

    NextendImageEditorController.prototype.propertyChanged = function (e, device, property, value) {
        this.isChanged = true;
        this.currentVisual[device][property] = value;
    };

    return NextendImageEditorController;
});_N2.d('NextendImage', ['NextendVisualCore'], function () {

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendImage() {
        _N2.NextendVisualCore.prototype.constructor.apply(this, arguments);
    }

    NextendImage.prototype = Object.create(_N2.NextendVisualCore.prototype);
    NextendImage.prototype.constructor = NextendImage;

    NextendImage.prototype.setValue = function (value, render) {
        this.base64 = value;
        this.value = JSON.parse(_N2.Base64.decode(value));
    };

    NextendImage.prototype.activate = function (e) {
        if (e !== undefined) {
            e.preventDefault();
        }
        this.visualManager.changeActiveVisual(this);
        this.visualManager.controller.load(this, false, this.visualManager.showParameters);
    };

    return NextendImage;

});_N2.d('NextendImageManager', ['$', 'NextendVisualManagerCore'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function NextendImageManager() {
        this.referenceKeys = {};
        _N2.NextendVisualManagerCore.prototype.constructor.apply(this, arguments);
    }

    NextendImageManager.prototype = Object.create(_N2.NextendVisualManagerCore.prototype);
    NextendImageManager.prototype.constructor = NextendImageManager;

    NextendImageManager.prototype.loadDefaults = function () {
        _N2.NextendVisualManagerCore.prototype.loadDefaults.apply(this, arguments);
        this.type = 'image';
        this.labels = {
            visual: 'image',
            visuals: 'images'
        };

        this.fontClassName = '';
    };


    NextendImageManager.prototype.initController = function () {
        return new _N2.NextendImageEditorController();
    };

    NextendImageManager.prototype.createVisual = function (visual) {
        return new _N2.NextendImage(visual, this);
    };

    NextendImageManager.prototype.firstLoadVisuals = function (visuals) {
        for (var i = 0; i < visuals.length; i++) {
            this.referenceKeys[visuals[i].hash] = this.visuals[visuals[i].id] = this.createVisual(visuals[i]);
        }
    };

    NextendImageManager.prototype.getVisual = function (image) {
        if (image == '') {
            _N2.Notification.error(n2_('The image is empty'), {
                timeout: 3
            });
        } else {
            var referenceKey = md5(image);
            if (this.referenceKeys[referenceKey] !== undefined) {
                return this.referenceKeys[referenceKey];
            } else if (this.visualLoadDeferreds[referenceKey] !== undefined) {
                return this.visualLoadDeferreds[referenceKey];
            } else {
                var deferred = $.Deferred();
                this.visualLoadDeferreds[referenceKey] = deferred;
                this._loadVisualFromServer(image)
                    .done((function () {
                        deferred.resolve(this.referenceKeys[referenceKey]);
                        delete this.visualLoadDeferreds[referenceKey];
                    }).bind(this))
                    .fail((function () {
                        // This visual is Empty!!!
                        deferred.resolve({
                            id: -1,
                            name: n2_('Empty')
                        });
                        delete this.visualLoadDeferreds[referenceKey];
                    }).bind(this));
                return deferred;
            }
        }
    };

    NextendImageManager.prototype._loadVisualFromServer = function (image) {
        return _N2.AjaxHelper.ajax({
            type: "POST",
            url: _N2.AjaxHelper.makeAjaxUrl(this.parameters.ajaxUrl, {
                nextendaction: 'loadVisualForImage'
            }),
            data: {
                image: image
            },
            dataType: 'json'
        })
            .done((function (response) {
                var visual = response.data.visual;
                this.referenceKeys[visual.hash] = this.visuals[visual.id] = this.createVisual(visual);
            }).bind(this));
    };

    NextendImageManager.prototype.isVisualData = function (data) {
        return data !== '';
    };

    NextendImageManager.prototype.setVisual = function (e) {
        e.preventDefault();
        if (this.controller.isChanged) {
            this.saveActiveVisual(this.activeVisual.name)
                .done((function (response) {
                    $(window).trigger(response.data.visual.hash, this.activeVisual.value);
                    this.hide(e);
                }).bind(this));
        } else {
            this.hide(e);
        }
    };

    NextendImageManager.prototype.getBase64 = function () {

        return _N2.Base64.encode(JSON.stringify(this.controller.get('set')));
    };

    NextendImageManager.prototype.loadDataToController = function (data) {
        $.when(this.getVisual(data)).done((function (visual) {
            if (visual.id > 0) {
                visual.activate();
            } else {
                console.error(data + ' visual is not found linked');
            }
        }).bind(this));
    };

    return NextendImageManager;
});_N2.d('BrowserCompatibility', function () {
    var requiredVersion = {
        firefox: 52,
        edge: 18,
        opera: 55,
        safari: 10,
        chrome: 68,
        safariIOS: 604
    };

    /**
     *
     * @memberOf _N2
     */
    function BrowserCompatibility(url) {
        if (window.location.href.match(/nextendcontroller=(sliders|slider|slides|generator)/) && !isAllowedBrowser()) {
            window.location = url;
        }
    }

    function isAllowedBrowser() {
        var ua = navigator.userAgent;

        var match = ua.match(/Firefox\/([0-9]+)/);
        if (match && parseInt(match[1]) >= requiredVersion.firefox) {
            return true;
        }

        match = ua.match(/Edge\/([0-9]+)/);
        if (match && parseInt(match[1]) >= requiredVersion.edge) {
            return true;
        }

        match = ua.match(/OPR\/([0-9]+)/);
        if (match && parseInt(match[1]) >= requiredVersion.opera) {
            return true;
        }

        match = ua.match(/Version\/([0-9]+)[0-9. ]*?Safari/);
        if (match && parseInt(match[1]) >= requiredVersion.safari) {
            return true;
        }

        match = ua.match(/Chrome\/([0-9]+)/);
        if (match && parseInt(match[1]) >= requiredVersion.chrome) {
            return true;
        }

        /**
         * For IOS Chrome and Edge
         */
        match = ua.match(/Safari\/([0-9]+)/);
        if (match && parseInt(match[1]) >= requiredVersion.safariIOS) {
            return true;
        }

        return false;
    }


    return BrowserCompatibility;
});_N2.d('ContextMenu', ['$'], function () {
    var $ = _N2.$;

    var lockCounter,
        $menu,
        menuInnerRect,
        $menuInner,
        $activeItem,
        $hoveredItem,
        $subMenu,
        options,
        $hoveredSubMenuItem,
        targetElement;

    /**
     * @memberOf _N2
     *
     * @param {MouseEvent|Element} target
     * @param {[]} items
     * @param {{}} _options
     */
    function ContextMenu(target, items, _options) {

        lockCounter = 0;

        options = _Assign({
            x: 0,
            y: 0,
            horizontalAlign: 'right',
            verticalAlign: 'bottom',
            onClose: false,
            forceLtr: false
        }, _options);

        if ($menu !== undefined) {
            /**
             * There is a context menu open. Let's close it!
             */
            blur();

            if (targetElement === target) {

                /**
                 * The context menu for this target was open, so it is just a blur.
                 */
                return;
            }
        }

        $menu = $('<div class="n2_context_menu"></div>')
            .appendTo('body');

        if (options.forceLtr) {
            $menu.attr('dir', 'ltr');
        }

        $menuInner = $('<div class="n2_context_menu__inner"></div>')
            .appendTo($menu);

        items.sort(function (a, b) {
            return a.priority - b.priority
        });

        for (var i = 0; i < items.length; i++) {
            switch (items[i].type) {
                case 'raw':
                    new MenuItemRaw($menuInner, items[i]);
                    break;
                case 'onoff':
                    new MenuItemOnOff($menuInner, items[i]);
                    break;
                default:
                    new MenuItem($menuInner, items[i]);
                    break;
            }
        }

        var position = {
            top: 'auto',
            right: 'auto',
            bottom: 'auto',
            left: 'auto'
        };
        if (target instanceof Element) {
            targetElement = target;

            var rect = target.getBoundingClientRect();
            if (options.horizontalAlign === 'right') {
                position.left = Math.min(rect.left + options.x, window.innerWidth - $menuInner.width() - options.x - 10) + 'px';
            } else if (options.horizontalAlign === 'left') {
                position.left = rect.right - $menuInner.width() - options.x + 'px';
            }

            if (options.verticalAlign === 'bottom') {
                var top = rect.top + rect.height + options.y,
                    maxTop = window.innerHeight - $menuInner.height() - 10;
                if (top <= maxTop) {
                    position.top = top + 'px';
                } else {
                    position.bottom = window.innerHeight - rect.top + options.y + 'px';
                }
            } else if (options.verticalAlign === 'top') {
                if (rect.top - 10 >= $menuInner.height()) {
                    position.bottom = window.innerHeight - rect.top + options.y + 'px';
                } else {
                    position.top = rect.top + rect.height + options.y + 'px';
                }
            }
        } else {
            targetElement = false;

            position.left = Math.min(target.clientX + 1, window.innerWidth - $menuInner.width() - 10) + 'px';
            position.top = Math.min(target.clientY + 1, window.innerHeight - $menuInner.height() - 10) + 'px';
        }

        $menu.css(position);

        menuInnerRect = $menuInner[0].getBoundingClientRect();
        $menuInner[0].addEventListener('mouseover', handleMouseOver, {
            passive: true,
            capture: true
        });

        $menuInner[0].addEventListener('mouseleave', handleMouse, {
            passive: true,
            capture: true
        });


        document.addEventListener('mousedown', onPointerDown, {
            passive: true,
            capture: true
        });

        document.addEventListener('pointerdown', onPointerDown, {
            passive: true,
            capture: true
        });

        /**
         * There is a scroll event at layer activation which would trigger blur instantly.
         * @type {number}
         */
        this.frame = requestAnimationFrame(function () {
            window.addEventListener('scroll', blur, {
                passive: true,
                capture: true
            });
        });
        window.addEventListener('resize', blur);
        window.addEventListener('blur', blur);


        return {
            lock: function () {
                lockCounter++;
            },
            unlock: function () {
                lockCounter--;
                lockCounter = Math.max(lockCounter, 0);
            }
        }
    }

    function onPointerDown(e) {
        if ($menu[0] !== e.target && !$.contains($menu[0], e.target)) {
            if (!targetElement || (targetElement !== e.target && !$.contains(targetElement, e.target))) {
                blur();
            }
        }
    }

    function onItemClick(item, e) {
        if (item.action.call(window) !== false) {
            blur();
        }
    }

    function blur() {
        if (lockCounter === 0) {
            blurSubMenu();

            blurHoveredItem();

            $menuInner[0].removeEventListener('mouseover', handleMouseOver, {
                passive: true,
                capture: true
            });

            $menuInner[0].removeEventListener('mouseleave', handleMouse, {
                passive: true,
                capture: true
            });

            $menu.remove();
            $menu = undefined;
            $menuInner = undefined;
            $activeItem = undefined;
            $subMenu = undefined;

            document.removeEventListener('mousedown', onPointerDown, {
                passive: true,
                capture: true
            });
            document.removeEventListener('pointerdown', onPointerDown, {
                passive: true,
                capture: true
            });

            cancelAnimationFrame(this.frame);
            window.removeEventListener('scroll', blur, {
                passive: true,
                capture: true
            });
            window.removeEventListener('resize', blur);
            window.removeEventListener('blur', blur);

            if (options.onClose) {
                options.onClose.call(window);
            }
        }
    }

    function handleMouseOver(e) {
        var $item = $(e.target);
        if (!$item.hasClass('n2_context_menu__item')) {
            $item = $item.closest('.n2_context_menu__item');
        }

        if (!$activeItem || $item[0] !== $activeItem[0]) {

            blurSubMenu();

            $activeItem = $item;

            if ($activeItem.length) {
                var items = $activeItem.data('items');
                if (items !== undefined) {
                    $activeItem.addClass('n2_context_menu__item--active');

                    $subMenu = $activeItem.data('submenu');
                    if (!$subMenu) {
                        $subMenu = buildSubMenu(items, $activeItem[0]);
                        $activeItem.data('submenu', $subMenu);
                    }

                    $subMenu.addClass('n2_context_menu__sub_menu--active');
                }
            }
        }

        handleMouse(e);
    }

    function handleMouse(e) {
        var $item = $(e.target);
        if (!$item.hasClass('n2_context_menu__item')) {
            $item = $item.closest('.n2_context_menu__item');
        }
        if (!$hoveredItem || $item[0] !== $hoveredItem[0]) {
            blurHoveredItem();

            $hoveredItem = $item;

            if ($hoveredItem.length) {
                $hoveredItem.data('item').onFocus();
            }
        }
    }

    function blurHoveredItem() {
        if ($hoveredItem && $hoveredItem.length) {
            /**
             * @type {MenuItem}
             */
            var item = $hoveredItem.data('item');
            if (item) {
                item.onBlur();
            }
            $hoveredItem = undefined;
        }
    }

    /**
     *
     * @param {[]} items
     * @param {Element} at
     */
    function buildSubMenu(items, at) {
        var rect = at.getBoundingClientRect();

        var $_subMenu = $('<div class="n2_context_menu__sub_menu"></div>')
            .appendTo($menu);

        for (var i = 0; i < items.length; i++) {
            switch (items[i].type) {
                case 'onoff':
                    new MenuItemOnOff($_subMenu, items[i]);
                    break;
                default:
                    new MenuItem($_subMenu, items[i]);
                    break;
            }
        }

        var subMenuWidth = $_subMenu.width(),
            left,
            isOutOfWindow;

        if (n2const.rtl.isRtl) {
            isOutOfWindow = rect.left < subMenuWidth;
        } else {
            isOutOfWindow = rect.left + rect.width + subMenuWidth < window.innerWidth;
        }

        if (isOutOfWindow) {
            left = Math.round(rect.width - 3);
        } else {
            left = -Math.round(subMenuWidth - 3)
        }

        $_subMenu.css({
            left: left + 'px',
            top: Math.round(Math.min(rect.top, window.innerHeight - $_subMenu.height() - 5) - menuInnerRect.top) + 'px'
        });

        $_subMenu[0].addEventListener('mouseover', handleSubMenuMouse, {
            passive: true,
            capture: true
        });

        $_subMenu[0].addEventListener('mouseleave', handleSubMenuMouse, {
            passive: true,
            capture: true
        });

        return $_subMenu;
    }

    function handleSubMenuMouse(e) {

        var $item = $(e.target);
        if (!$item.hasClass('n2_context_menu__item')) {
            $item = $item.closest('.n2_context_menu__item');
        }
        if (!$hoveredSubMenuItem || $item[0] !== $hoveredSubMenuItem[0]) {
            blurHoveredSubMenuItem();

            $hoveredSubMenuItem = $item;

            if ($hoveredSubMenuItem.length) {
                $hoveredSubMenuItem.data('item').onFocus();
            }
        }
    }

    function blurHoveredSubMenuItem() {
        if ($hoveredSubMenuItem && $hoveredSubMenuItem.length) {
            /**
             * @type {MenuItem}
             */
            var item = $hoveredSubMenuItem.data('item');
            if (item) {
                item.onBlur();
            }
            $hoveredSubMenuItem = undefined;
        }
    }

    function blurSubMenu() {
        if ($subMenu !== undefined) {

            blurHoveredSubMenuItem();

            $subMenu.removeClass('n2_context_menu__sub_menu--active');
            $subMenu = undefined;
            $hoveredSubMenuItem = undefined;
            $activeItem.removeClass('n2_context_menu__item--active');
        }
    }

    function MenuItemRaw($parent, data) {

        this.$item = $('<div class="n2_context_menu__item"></div>')
            .data('item', this)
            .appendTo($parent);

        if (data.color !== undefined) {
            this.$item.addClass('n2_context_menu__item--color-' + data.color);
        }

        this.setData(data);
    }

    MenuItemRaw.prototype.setData = function (data) {
        this.data = data;

        this.$item.html('');

        this.render(data);

        if (data.items !== undefined) {
            this.$item.data('items', data.items);
        }
    };

    MenuItemRaw.prototype.render = function (data) {
        data.render(this.$item);
    };

    MenuItemRaw.prototype.onClick = function () {
        if (this.data.action.call(window, this) !== false) {
            blur();
        }
    };

    MenuItemRaw.prototype.onFocus = function () {
        if (this.data.focus !== undefined) {
            this.data.focus.call();
        }
    };

    MenuItemRaw.prototype.onBlur = function () {
        if (this.data.blur !== undefined) {
            this.data.blur.call();
        }
    };

    function MenuItem($parent, data) {
        MenuItemRaw.prototype.constructor.call(this, $parent, data);
    }

    MenuItem.prototype = Object.create(MenuItemRaw.prototype);
    MenuItem.prototype.constructor = MenuItem;

    MenuItem.prototype.render = function (data) {

        this.$icon = $('<i class="n2_context_menu__item_icon ' + data.icon + '"></i>').appendTo(this.$item);
        this.$label = $('<div class="n2_context_menu__item_label"></div>').appendTo(this.$item);

        if (typeof data.label === 'function') {
            data.label.call(this, this);
        } else {
            this.$label.text(data.label);
        }

        this.$item.off('click');

        if (data.action !== undefined) {
            this.$item.on('click', this.onClick.bind(this));
            this.$item.addClass('n2_context_menu__item--has-action');
        } else {
            this.$item.removeClass('n2_context_menu__item--has-action');
        }
    };

    function MenuItemOnOff($parent, data) {
        MenuItem.prototype.constructor.call(this, $parent, data);

        this.$item.addClass('n2_context_menu__item_onoff');
    }

    MenuItemOnOff.prototype = Object.create(MenuItem.prototype);
    MenuItemOnOff.prototype.constructor = MenuItemOnOff;

    MenuItemOnOff.prototype.render = function (data) {

        this.isOn = !!data.isOn.call(this);
        this.syncClass();

        $('<i class="n2_context_menu__item_icon ssi_16"></i>').appendTo(this.$item);
        this.$label = $('<div class="n2_context_menu__item_label"></div>').appendTo(this.$item);

        if (typeof data.label === 'function') {
            data.label.call(this, this.$label);
        } else {
            this.$label.text(data.label);
        }

        this.$item.off('click');

        this.$item.on('click', this.toggle.bind(this));
        this.$item.addClass('n2_context_menu__item--has-action');
    };

    MenuItemOnOff.prototype.syncClass = function () {
        this.$item.toggleClass('n2_context_menu__item_onoff--on', this.isOn);
    };

    MenuItemOnOff.prototype.toggle = function () {
        this.isOn = !this.isOn;
        this.syncClass();

        this.data.action(this.isOn);
    };

    return ContextMenu;
});
_N2.d('DashboardInfo', ['$'], function () {
    var $ = _N2.$;

    function DashboardInfo() {

        this.isVisible = false;

        this.blurInteractionCallback = this.blurInteraction.bind(this);
        this.blurCallback = this.blur.bind(this);

        _N2.r('documentReady', this.onReady.bind(this));
    }

    DashboardInfo.prototype.onReady = function () {
        this.$info = $('.n2_dashboard_info');
        this.$button = this.$info.find('> .n2_button_plain_icon')
            .on('click', this.toggleInfo.bind(this));
    };

    DashboardInfo.prototype.toggleInfo = function (e) {
        e.preventDefault();

        if (this.isVisible) {
            this.hideInfo()
        } else {
            this.showInfo();
        }
    };

    DashboardInfo.prototype.showInfo = function () {
        if (!this.isVisible) {
            this.isVisible = !this.isVisible;

            document.addEventListener('mousedown', this.blurInteractionCallback, {
                passive: true,
                capture: true
            });

            window.addEventListener('scroll', this.blurCallback, {
                passive: true,
                capture: true
            });

            this.syncClass();
        }
    };

    DashboardInfo.prototype.hideInfo = function () {
        if (this.isVisible) {
            this.isVisible = !this.isVisible;
            this.syncClass();

            document.removeEventListener('mousedown', this.blurInteractionCallback, {
                passive: true,
                capture: true
            });

            window.removeEventListener('scroll', this.blurCallback, {
                passive: true,
                capture: true
            });
        }
    };

    DashboardInfo.prototype.syncClass = function () {
        this.$info.toggleClass('n2_dashboard_info--visible', this.isVisible);
    };

    DashboardInfo.prototype.blur = function () {
        this.hideInfo();
    };

    DashboardInfo.prototype.blurInteraction = function (e) {
        if (this.$info[0] !== e.target && !$.contains(this.$info[0], e.target)) {
            this.hideInfo();
        }
    };

    return DashboardInfo;
});_N2.d('DeviceChanger', ['$'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param {_N2.SmartSliderAbstract} slider
     * @param {Element|null} [containerElement]
     * @constructor
     */
    function DeviceChanger(slider, containerElement) {
        this.slider = slider;

        this.containerElement = containerElement;

        this.activeStates = {
            hover: false,
            drag: false
        };

        this.devices = {};

        this.slider.stages.done('ResponsiveStart', this.onResponsiveStart.bind(this));
    }

    /**
     *
     * @param device
     * @param {Device} obj
     */
    DeviceChanger.prototype.registerDevice = function (device, obj) {
        this.devices[device] = obj;
    };

    DeviceChanger.prototype.setActiveState = function (state, value) {
        this.activeStates[state] = value;

        this.$deviceChanger.toggleClass('n2_device_changer--active', this.activeStates.hover || this.activeStates.drag);
    };

    DeviceChanger.prototype.onResponsiveStart = function () {

        var timeout;

        this.$deviceChanger = $('.n2_device_changer')
            .on({
                mouseenter: (function () {
                    if (timeout) {
                        clearTimeout(timeout);
                        timeout = undefined
                    }
                    this.setActiveState('hover', true);
                }).bind(this),
                mouseleave: (function () {
                    timeout = setTimeout((function () {
                        this.setActiveState('hover', false);
                    }).bind(this), 500);
                }).bind(this)
            });

        this.$icon = this.$deviceChanger.find('.n2_device_changer__button .ssi_24');

        /**
         * @type {_N2.SmartSliderResponsive}
         */
        this.responsive = this.slider.responsive;

        this.container = this.containerElement ? $(this.containerElement) : $(this.responsive.containerElement.closest('.n2_slider_preview_area__inner, .n2_slide_editor_slider__editor'));

        this.$deviceTester = this.$deviceChanger.find('.n2_device_tester');

        this.$containerDevices = $('<div class="n2_device_tester_devices"></div>')
            .appendTo(this.$deviceTester);

        /**
         * @type {Resizer}
         */
        this.resizer = new Resizer(this);

        /**
         * @type {{mobileLandscape, mobilePortrait, desktopPortrait, tabletLandscape, desktopLandscape, tabletPortrait}}
         */
        var enabledDevices = this.responsive.parameters.enabledDevices;
        if (enabledDevices.mobilePortrait) {
            new Device(this, 'mobilePortrait', this.responsive.parameters.sizes.mobilePortrait, 'ssi_24--mobileportrait');
        }
        if (enabledDevices.tabletPortrait) {
            new Device(this, 'tabletPortrait', this.responsive.parameters.sizes.tabletPortrait, 'ssi_24--tabletportrait');
        }

        var desktopPortrait = new Device(this, 'desktopPortrait', this.responsive.parameters.sizes.desktopPortrait, 'ssi_24--desktop');
        desktopPortrait.activate();

        this.responsive.identifyDeviceID = this.proxyIdentifyDeviceID.bind(this);
    };

    DeviceChanger.prototype.setDevice = function (device, width, minWidth, maxWidth) {

        if (this.device) {
            this.devices[this.device].markInActive();
            this.$icon.removeClass(this.devices[this.device].icon);
        }

        this.device = device;

        $('body').attr('data-device', device);

        this.devices[this.device].markActive();
        this.$icon.addClass(this.devices[this.device].icon);

        this.width = width;
        this.minWidth = minWidth;
        this.maxWidth = maxWidth;

        this.resizer.setLimits(minWidth, maxWidth, width);

        this.resizer.setValue(this.width);

        this.container.width(this.width + 'px');

        if (this.slider.stages.resolved('Resized')) {
            this.responsive.doResize();
        }
    };

    DeviceChanger.prototype.setWidth = function (targetWidth) {
        targetWidth = Math.max(Math.min(targetWidth, this.maxWidth), this.minWidth);

        if (targetWidth !== this.width) {

            this.width = targetWidth;

            this.resizer.setValue(targetWidth);

            this.container.width(this.width + 'px');

            this.responsive.doResize();
        }
    };

    DeviceChanger.prototype.proxyIdentifyDeviceID = function () {

        return this.slider.sliderElement.ownerDocument.defaultView._N2.SmartSliderResponsive.DeviceMode[this.device.toLowerCase()];
    };

    /**
     *
     * @param {_N2.DeviceChanger} deviceChanger
     * @param {string} device
     * @param {{}} data
     * @param {string} icon
     * @constructor
     */
    function Device(deviceChanger, device, data, icon) {
        this.deviceChanger = deviceChanger;
        this.device = device;
        this.data = data;
        this.icon = icon;

        this.$button = $('<div class="n2_device_tester_devices__device" data-device="' + device + '"><i class="ssi_24 ' + icon + '"></i></div>')
            .on('click', this.activate.bind(this))
            .appendTo(deviceChanger.$containerDevices);

        deviceChanger.registerDevice(device, this);
    }

    Device.prototype.markActive = function () {
        this.$button.addClass('n2_device_tester_devices__device--active');
    };

    Device.prototype.markInActive = function () {
        this.$button.removeClass('n2_device_tester_devices__device--active');
    };

    Device.prototype.activate = function () {
        this.deviceChanger.setDevice(this.device, this.data.width, this.data.min, this.data.max);
    };

    /**
     *
     * @param {_N2.DeviceChanger} deviceChanger
     * @constructor
     */
    function Resizer(deviceChanger) {

        this.trailSize = 160;
        /**
         * @type {_N2.DeviceChanger}
         */
        this.deviceChanger = deviceChanger;

        var $zoom = $('<div class="n2_device_tester_zoom"></div>').appendTo(deviceChanger.$deviceTester);

        this.$min = $('<div class="n2_device_tester_zoom__min_label">0</div>').appendTo($zoom);

        this.mouseMoveDelegate = this.onMouseMove.bind(this);
        this.mouseUpDelegate = this.onMouseUp.bind(this);

        this.$zoomInner = $('<div class="n2_device_tester_zoom__inner"></div>')
            .on('mousedown', this.onMouseDown.bind(this))
            .appendTo($zoom);

        this.$max = $('<div class="n2_device_tester_zoom__max_label">3000</div>').appendTo($zoom);

        $('<div class="n2_device_tester_zoom__trail"></div>').appendTo(this.$zoomInner);
        this.$editorSize = $('<div class="n2_device_tester_zoom__editor_size"></div>').appendTo(this.$zoomInner);

        this.$handle = $('<div class="n2_device_tester_zoom__handle"></div>')
            .appendTo(this.$zoomInner);

        this.$handleLabel = $('<div class="n2_device_tester_zoom__handle_label"></div>')
            .appendTo(this.$handle);
    }

    /**
     *
     * @param {number} min
     * @param {number} max
     * @param {number} editorSize
     */
    Resizer.prototype.setLimits = function (min, max, editorSize) {
        /**
         * @type {number}
         */
        this.min = min;

        this.$min.text(min);

        /**
         * @type {number}
         */
        this.max = max;

        this.$max.text(max);

        /**
         * @type {number}
         */
        this.editorSize = editorSize;

        this.onePxToReal = (max - min) / this.trailSize;

        this.$editorSize.css('left', Math.round((editorSize - min) / (max - min) * this.trailSize) + 'px');

    };


    /**
     *
     * @param {number} value
     */
    Resizer.prototype.setValue = function (value) {

        this.$handleLabel.text(value);

        this.$handle
            .css('left', Math.round((value - this.min) / (this.max - this.min) * this.trailSize) + 'px');
    };

    Resizer.prototype.onMouseDown = function (e) {
        e.preventDefault();

        $('body').addClass('n2_body--resize-editor');

        this.resizeContext = {
            clientX: this.$zoomInner[0].getBoundingClientRect().left,
            width: this.deviceChanger.width
        };

        document.addEventListener('mousemove', this.mouseMoveDelegate);
        document.addEventListener('mouseup', this.mouseUpDelegate);

        this.deviceChanger.setActiveState('drag', true);

        this.onMouseMove(e);
    };

    Resizer.prototype.onMouseMove = function (e) {
        var targetWidth = Math.round(this.min + (e.clientX - this.resizeContext.clientX) * this.onePxToReal);

        if (Math.abs(this.editorSize - targetWidth) < this.onePxToReal * 2) {
            targetWidth = this.editorSize;
        }

        this.deviceChanger.setWidth(targetWidth);
    };

    Resizer.prototype.onMouseUp = function (e) {

        document.removeEventListener('mousemove', this.mouseMoveDelegate);
        document.removeEventListener('mouseup', this.mouseUpDelegate);

        this.deviceChanger.setActiveState('drag', false);

        $('body').removeClass('n2_body--resize-editor');
    };

    return DeviceChanger;
});_N2.d('SliderPreview', ['$', 'UIDragTooltip'], function () {
    var $ = _N2.$;

    var tooltip = new _N2.UIDragTooltip('Resize'),
        minWidth = 320,
        minHeight = 320,
        deviceLabels;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function SliderPreview() {

        deviceLabels = {
            "desktopPortrait": n2_('Desktop'),
            "desktopLandscape": n2_('Large desktop'),
            "tabletPortrait": n2_('Tablet'),
            "tabletLandscape": n2_('Large tablet'),
            "mobilePortrait": n2_('Mobile'),
            "mobileLandscape": n2_('Large mobile')
        };

        this.frameScrollTop = 0;
        this.scale = 1;

        this.activeInfo = false;
        this.sliderInfos = {};

        this.isIframeContext = window.parent !== window;

        if (this.isIframeContext) {

            this.$parentDocument = window.parent._N2.$(window.parent.document);

            document.addEventListener('wheel', (function (e) {
                e.preventDefault();
            }).bind(this), {
                passive: false
            });

            window.addEventListener('message', (function (e) {
                var message = {};
                try {
                    message = JSON.parse(e.data);
                } catch (e) {
                }
                switch (message.action) {
                    case 'cancel':
                        this.$parentDocument.triggerHandler('n2Close');
                        break;
                    case 'scrollTop':
                        this.updateFrameScrollTop(message.data);
                        break;
                    case 'device_info':
                        this.updateDeviceInfo(message.data);
                        break;
                }
            }).bind(this), false);

            document.addEventListener('keydown', (function (e) {
                if (!e.defaultPrevented) {
                    if (e.code === 'Escape') {
                        e.preventDefault();
                        this.$parentDocument.triggerHandler('n2Close');
                    }
                }
            }).bind(this));

            $('.n2_preview_slider__close')
                .on('click', (function (e) {
                    e.preventDefault();
                    this.$parentDocument.triggerHandler('n2Close');
                }).bind(this))
        } else {

            window.addEventListener('message', (function (e) {
                var message = {};
                try {
                    message = JSON.parse(e.data);
                } catch (e) {
                }
                switch (message.action) {
                    case 'scrollTop':
                        this.updateFrameScrollTop(message.data);
                        break;
                    case 'device_info':
                        this.updateDeviceInfo(message.data);
                        break;
                }
            }).bind(this), false);
        }

        this.init();

        this.$frame = $('.n2_preview__device_screen_inner iframe');

        $('.n2_preview form').trigger("submit");
    }

    function filterNumber(text) {
        return text
            .replace(/[^0-9]/gi, '')
            .replace(/^[0]+/gi, '')
            .substr(0, 5);
    }

    SliderPreview.prototype.init = function () {

        this.$fieldWidth = $('.n2_preview_toolbar__width')
            .on('click', (function () {
                if (this.$fieldWidth[0].contentEditable !== 'true') {
                    new _N2.InlineTextEditor(this.$fieldWidth[0], {
                        filter: filterNumber,
                        onSave: (function (value) {
                            value = parseInt(value);
                            if (!value) {
                                this.onResize();
                            } else {
                                this.setWidth(value);
                            }
                        }).bind(this),
                        onCancel: (function () {
                            this.onResize();
                        }).bind(this)
                    });
                }
            }).bind(this));
        this.$fieldHeight = $('.n2_preview_toolbar__height')
            .on('click', (function () {
                if (this.$fieldHeight[0].contentEditable !== 'true') {
                    new _N2.InlineTextEditor(this.$fieldHeight[0], {
                        filter: filterNumber,
                        onSave: (function (value) {
                            value = parseInt(value);
                            if (!value) {
                                this.onResize();
                            } else {
                                this.setHeight(value);
                            }
                        }).bind(this),
                        onCancel: (function () {
                            this.onResize();
                        }).bind(this)
                    });
                }
            }).bind(this));

        this.$fieldScale = $('.n2_preview_toolbar__scale').on('change', (function () {

            this.setScale(this.$fieldScale.val() / 100);
        }).bind(this));

        /**
         * @type {Array}
         */
        this.devices = [];

        this.$ruler = $('.n2_preview__ruler');

        new PreviewDevice(this, n2_('Mobile') + ' S', 375, 667);
        new PreviewDevice(this, n2_('Mobile') + ' L', 425, 756);
        new PreviewDevice(this, n2_('Tablet'), 768, 1024);
        new PreviewDevice(this, n2_('Laptop'), 1024, 768);
        new PreviewDevice(this, n2_('Laptop') + ' L', 1440, 900);
        new PreviewDevice(this, 'Full HD', 1920, 1080);
        new PreviewDevice(this, '4K', 2560, 1440);

        this.$label = $('.n2_preview__ruler_label');

        this.$infoState = $('.n2_preview__device_info_state');
        this.$infoTip = $('.n2_preview__device_info .ssi_16');

        this.$screen = $('.n2_preview__device_screen');

        this.$notification = $('<div class="n2_preview__notification_scale"></div>').appendTo(this.$screen);

        this.$screenInner = $('.n2_preview__device_screen_inner');

        $('.n2_preview__resize_width').on('mousedown', (function (e) {
            e.preventDefault();

            tooltip.dragShow();

            $('body').addClass('n2_body--resize-horizontal');
            var clientX = e.clientX,
                width = Math.floor(this.$screenInner.width() / this.scale);

            tooltip.dragUpdate(e, n2_('Width') + ': ' + Math.max(minWidth, Math.ceil(width)) + 'px');

            $(document).on({
                    'mousemove.resize': (function (e) {
                        var w = Math.floor(width - (clientX - e.clientX) * 2 / this.scale);
                        this.setWidth(w);
                        this.removeCurrentDevice();

                        tooltip.dragUpdate(e, n2_('Width') + ': ' + Math.max(minWidth, Math.ceil(w)) + 'px');
                    }).bind(this),
                    'mouseup.resize': function () {
                        tooltip.dragHide();

                        $(document).off('.resize');
                        $('body').removeClass('n2_body--resize-horizontal');
                    },
                    'mouseleave.resize': function () {
                        tooltip.dragHide();

                        $(document).off('.resize');
                        $('body').removeClass('n2_body--resize-horizontal');
                    }
                }
            )
        }).bind(this));


        $('.n2_preview__resize_height').on('mousedown', (function (e) {
            e.preventDefault();

            tooltip.dragShow();

            $('body').addClass('n2_body--resize-vertical');
            var clientY = e.clientY,
                height = Math.floor(this.$screenInner.height() / this.scale);

            tooltip.dragUpdate(e, n2_('Height') + ': ' + Math.max(minHeight, height) + 'px');

            $(document).on({
                    'mousemove.resize': (function (e) {
                        var h = Math.floor(height - (clientY - e.clientY) / this.scale);
                        this.setHeight(h);
                        this.removeCurrentDevice();

                        tooltip.dragUpdate(e, n2_('Height') + ': ' + Math.max(minHeight, h) + 'px');

                    }).bind(this),
                    'mouseup.resize': function () {
                        tooltip.dragHide();

                        $(document).off('.resize');
                        $('body').removeClass('n2_body--resize-vertical');
                    }
                }
            )
        }).bind(this));

        $('.n2_button_preview_orientation').on('click', (function (e) {
            e.preventDefault();

            this.removeCurrentDevice();
            this.setDimensions(this.$screenInner.height() / this.scale, this.$screenInner.width() / this.scale);
        }).bind(this));

        $('.n2_button_preview_reload').on('click', (function (e) {
            e.preventDefault();

            this.reload();
        }).bind(this));

        $(window).on('resize', this.onResize.bind(this));
        this.onResize();

        this.$ruler.addClass('n2_preview__ruler--ready');
    };

    SliderPreview.prototype.reload = function () {

        $('.n2_preview form').trigger("submit");
    };

    SliderPreview.prototype.onResize = function () {

        this.$fieldWidth.text(Math.floor(this.$screenInner.width() / this.scale));
        this.$fieldHeight.text(Math.floor(this.$screenInner.height() / this.scale));
    };

    SliderPreview.prototype.displayNotification = function (text) {
        if (this.notificationTimeout) {
            clearTimeout(this.notificationTimeout);
        }
        this.$notification
            .text(text)
            .addClass('n2_preview__notification_scale--visible');

        this.notificationTimeout = setTimeout((function () {
            this.$notification.removeClass('n2_preview__notification_scale--visible');
            delete this.notificationTimeout;
        }).bind(this), 600);
    };

    SliderPreview.prototype.setScale = function (scale) {

        this.displayNotification((scale * 100) + '%');

        var oldScale = this.scale;

        this.scale = scale;

        $('body').addClass('n2_body--scale-preview');
        setTimeout(function () {
            $('body').removeClass('n2_body--scale-preview');
        }, 1500);


        this.$screen = $('.n2_preview__device_screen');

        var width = this.$screenInner.width(),
            height = this.$screenInner.height();

        this.$frame.css({
            transform: 'scale(' + this.scale + ')',
            width: (100 * (1 / this.scale)) + '%',
            height: (100 * (1 / this.scale)) + '%',
            marginLeft: (100 - (100 * (1 / this.scale))) / 2 + '%',
            marginRight: (100 - (100 * (1 / this.scale))) / 2 + '%',
        });

        for (var i = 0; i < this.devices.length; i++) {
            this.devices[i].refreshScale();
        }

        this._setDimensions(width / oldScale, height / oldScale);

        this.onResize();
    };

    SliderPreview.prototype.setDimensions = function (width, height) {


        var maxWidth = this.$screen.width(),
            maxHeight = this.$screen.height(),
            scale = Math.max(25, Math.floor(Math.min(1, maxWidth / width, maxHeight / height) * 100 / 25) * 25);


        if (parseInt(this.$fieldScale.val()) !== scale && scale <= 100) {
            this.$fieldScale.val(scale).trigger('change');
        }

        this._setDimensions(width, height);
    };

    SliderPreview.prototype._setDimensions = function (width, height) {

        this.setWidth(width);
        this.setHeight(height);
    };

    SliderPreview.prototype.setWidth = function (width) {

        width = Math.floor(Math.max(minWidth, width) * this.scale);

        this.$screenInner.css('maxWidth', width + 'px');

        this.$fieldWidth.text(Math.floor(this.$screenInner.width() / this.scale));
    };

    SliderPreview.prototype.setHeight = function (height) {

        height = Math.floor(Math.max(320, height) * this.scale);
        this.$screenInner.css('maxHeight', height + 'px');

        this.$fieldHeight.text(Math.floor(this.$screenInner.height() / this.scale));
    };

    /**
     *
     * @param {PreviewDevice} device
     */
    SliderPreview.prototype.setCurrentDevice = function (device) {
        if (this.currentDevice && this.currentDevice !== device) {
            this.removeCurrentDevice();
        }

        /**
         * @type {PreviewDevice}
         */
        this.currentDevice = device;

        this.resetLabel();
    };

    SliderPreview.prototype.removeCurrentDevice = function () {
        if (this.currentDevice) {
            this.currentDevice.deActivate();
            delete this.currentDevice;

            this.resetLabel();
        }
    };

    SliderPreview.prototype.setTempLabel = function (label) {
        if (this.currentDevice) {
            this.currentDevice.deActivate();
        }

        this.$label.text(label);
    };

    SliderPreview.prototype.resetLabel = function () {
        if (this.currentDevice) {
            this.$label.text(this.currentDevice.getLabel());
            this.currentDevice.activate();
        } else {
            this.$label.text('');
        }
    };

    SliderPreview.prototype.setInfo = function (state, isLandscape, min, max) {
        this.$infoState.text(state);

        var description = [];
        description.push('<b>' + n2_('Orientation') + ': </b>' + (isLandscape ? n2_('Landscape') : n2_('Portrait')));
        if (min <= 0) {
            description.push('<b>' + n2_('Size') + ': </b>' + n2_sprintf(n2_('Below %s pixels.'), max));
        } else {
            if (max >= 100000) {
                description.push('<b>' + n2_('Size') + ': </b>' + n2_sprintf(n2_('Above %s pixels.'), min));
            } else {
                description.push('<b>' + n2_('Size') + ': </b>' + n2_sprintf(n2_('Between %s and %s pixels.'), min, max));
            }
        }

        var formTip = this.$infoTip.data('formTip');
        if (formTip) {
            this.$infoTip.data('formTip').setDescription(description.join('<br>'));
        } else {
            this.$infoTip.data('tip-description', description.join('<br>'));
        }
    };

    SliderPreview.prototype.updateFrameScrollTop = function (data) {
        this.frameScrollTop = data.scrollTop;


        this.updateActive();
    };

    SliderPreview.prototype.updateDeviceInfo = function (data) {

        if (this.sliderInfos[data.id] === undefined) {
            this.sliderInfos[data.id] = new PreviewSliderInfo(this, data.id);
        }

        this.sliderInfos[data.id].update(data);

        this.updateActive();
    };

    SliderPreview.prototype.updateActive = function () {

        var active = false,
            smallest = Number.POSITIVE_INFINITY;
        for (var k in this.sliderInfos) {
            if (this.sliderInfos[k].top < smallest) {
                smallest = this.sliderInfos[k].top;
                active = this.sliderInfos[k];
            }
        }

        for (var k in this.sliderInfos) {
            if (this.sliderInfos[k].top <= this.frameScrollTop) {
                if (active.top < this.sliderInfos[k].top) {
                    active = this.sliderInfos[k];
                }
            }
        }

        if (this.activeInfo !== active) {
            this.activeInfo = active;

            this.updateInfo(active);
        }
    };

    SliderPreview.prototype.updateInfo = function (sliderInfo) {
        if (this.activeInfo === sliderInfo) {
            this.setInfo(this.activeInfo.device, this.activeInfo.isLandscape, this.activeInfo.minScreenWidth, this.activeInfo.maxScreenWidth);
        }
    };


    /**
     *
     * @param {_N2.SliderPreview} sliderPreview
     * @param {string} label
     * @param {int} width
     * @param {int} height
     * @constructor
     */
    function PreviewDevice(sliderPreview, label, width, height) {
        /**
         * @type {_N2.SliderPreview}
         */
        this.sliderPreview = sliderPreview;
        this.label = label;
        this.width = width;
        this.height = height;

        sliderPreview.devices.push(this);

        this.$bar = $('<div class="n2_preview__ruler_device"><div></div></div>')
            .on({
                mouseenter: (function () {
                    this.sliderPreview.setTempLabel(this.getLabel());
                }).bind(this),
                mouseleave: (function () {
                    this.sliderPreview.resetLabel();
                }).bind(this),
                click: (function () {
                    this.sliderPreview.setDimensions(this.width, this.height);
                    this.sliderPreview.setCurrentDevice(this);
                }).bind(this)
            })
            .prependTo(sliderPreview.$ruler);

        this.refreshScale();
    }

    PreviewDevice.prototype.activate = function () {
        this.$bar.addClass('n2_preview__ruler_device--active');
    };

    PreviewDevice.prototype.deActivate = function () {
        this.$bar.removeClass('n2_preview__ruler_device--active');
    };

    PreviewDevice.prototype.getLabel = function () {
        return this.label + ' ' + this.width + ' x ' + this.height + 'px';
    };

    PreviewDevice.prototype.refreshScale = function () {
        var width = this.width * this.sliderPreview.scale;
        this.$bar.css({
            width: width + 'px',
            marginLeft: (-width / 2) + 'px'
        });
    };

    /**
     *
     * @param {_N2.SliderPreview} sliderPreview
     * @param {integer} id
     * @constructor
     */
    function PreviewSliderInfo(sliderPreview, id) {
        this.sliderPreview = sliderPreview;
        this.id = id;
        this.top = 0;

        this.$tip = $('<div class="n2_preview__slider_info"></div>')
            .appendTo(this.sliderPreview.$infoState);
    }

    PreviewSliderInfo.prototype.update = function (data) {

        this.device = deviceLabels[data.device];
        this.isLandscape = data.isLandscape;
        this.minScreenWidth = data.minScreenWidth;
        this.maxScreenWidth = data.maxScreenWidth;
        this.top = data.top;

        this.sliderPreview.updateInfo(this);
    };

    return SliderPreview;
});_N2.d('SliderChangeType', '$', function () {
    var $ = _N2.$;

    /**
     *
     * @memberOf _N2
     * @param options
     * @constructor
     */
    function SliderChangeType(options) {

        /**
         * @type {_N2.SliderChangeType}
         */
        _N2._changeSliderType = this;

        this.options = _Assign({
            types: {},
            currentType: 'simple',
            ajaxUrl: ''
        }, options);

        _N2.r('documentReady', this.onReady.bind(this));
    }

    SliderChangeType.prototype.onReady = function () {
        $('.n2_slider_action__change_slider_type')
            .on('click', this.showModal.bind(this))
    };

    /**
     * @param {Event} [e]
     */
    SliderChangeType.prototype.showModal = function (e) {
        e && e.preventDefault();

        var modal = new _N2.ModalSimple('activate', {
            destroyOnHide: true,
            width: 630
        });

        modal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--convert', 'blue'));
        modal.addContent(_N2.UI.modalHeading(n2_('Change slider type')));

        var $row1 = _N2.UI.modalFormRow(true);
        modal.addContent($row1);

        _N2.UI.modalNotice($row1, n2_sprintf(n2_('Changing your slider type is irreversible. After changing your slider type, %syou will lose all slider type related settings%s.'), '<b>', '</b>'), 'notice');

        var $row2 = _N2.UI.modalFormRow(true);
        modal.addContent($row2);
        var $targetSliderType = _N2.UI.modalFieldSelectIcon($row2, 'slider-type', n2_('Slider type'), this.options.types, this.options.currentType);


        var $convertButton = _N2.UI.modalButton(n2_('Convert'), 'blue');
        modal.addButton($convertButton);

        $convertButton.on('click', (function (e) {
            e.preventDefault();

            this.convertSliderType($targetSliderType.val());

            modal.hide();
        }).bind(this));

        modal.show();
    };

    SliderChangeType.prototype.convertSliderType = function (targetSliderType) {
        if (this.options.currentType !== targetSliderType) {

            _N2.AjaxHelper.ajax({
                type: "POST",
                url: this.options.ajaxUrl,
                data: {
                    targetSliderType: targetSliderType
                },
                dataType: 'json'
            }).done(function () {
                window.location.reload();
            });
        }
    };

    return SliderChangeType;
});_N2.d('CreateSlider', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param groupID
     * @param ajaxUrl
     * @param shouldSkipLicenseModal
     * @param userEmail
     * @param skipNewsletter
     * @param importUrl
     * @constructor
     */
    function CreateSlider(groupID, ajaxUrl, shouldSkipLicenseModal, userEmail, skipNewsletter, importUrl) {
        this.groupID = groupID;
        this.ajaxUrl = ajaxUrl;
        this.userEmail = userEmail;
        this.shouldSkipLicenseModal = shouldSkipLicenseModal;
        this.skipNewsletter = skipNewsletter;
        this.importUrl = importUrl;

        $('.n2_slider_manager__new_slider').on('click', this.createProject.bind(this));

        if (window.location.hash.substring(1) === 'createslider') {
            this.createProject();
        }
    }

    CreateSlider.prototype.createProject = function () {

        var modal = new _N2.ModalSimple('new-project', {
            destroyOnHide: true,
            width: 1000
        });

        var $content = $('<div class="n2_new_project"></div>');
        modal.addContent($content);

        $('<div class="n2_new_project__heading"></div>').text(n2_('What do you want to create today?'))
            .appendTo($content);

        $('<div class="n2_new_project__sub_heading"></div>').text(n2_('Use our powerful visual editor, or simply import one of our existing template.'))
            .appendTo($content);

        var $boxContainer = $('<div class="n2_new_project__boxes"></div>').appendTo($content);

        var $createNewProjectBox = $('<div class="n2_new_project__box"></div>')
            .on('click', (function (e) {

                modal.hide(e);
                this.createSlider();
            
            }).bind(this))
            .appendTo($boxContainer);

        $('<div class="n2_new_project__box_illustration"><img src="' + _N2._imageHelper.fixed('$ss3-admin$/images/sliders/newproject.svg') + '" alt="" width="160" /></div>')
            .appendTo($createNewProjectBox);

        $('<div class="n2_new_project__box_heading"></div>').text(n2_('Create a New Project'))
            .appendTo($createNewProjectBox);

        $('<div class="n2_new_project__box_sub_heading"></div>').text(n2_('Start a new project from scratch and build exactly what you’ve imagined. You can easily customize every pixels and create anything with layers.'))
            .appendTo($createNewProjectBox);

        var $exampleSlidersBox = $('<div class="n2_new_project__box"></div>')
            .on('click', (function (e) {
                this.showExampleSlidersWindow();
            

                modal.hide(e);
            }).bind(this))
            .appendTo($boxContainer);

        $('<div class="n2_new_project__box_illustration"><img src="' + _N2._imageHelper.fixed('$ss3-admin$/images/sliders/template.svg') + '" alt="" width="160" /></div>')
            .appendTo($exampleSlidersBox);

        $('<div class="n2_new_project__box_heading"></div>').text(n2_('Start with a Template'))
            .appendTo($exampleSlidersBox);

        $('<div class="n2_new_project__box_sub_heading"></div>').text(n2_('Start with a template and make it your own with the innovative drag and drop interface. You can choose from hundreds of premade templates.'))
            .appendTo($exampleSlidersBox);
        $('<a class="n2_new_project__import" href="' + this.importUrl + '"></a>').text(n2_('or import your own files'))
            .appendTo($content);
    

        modal.show();
    };

    CreateSlider.prototype.createSlider = function () {

        var modal = new _N2.ModalSimple('create-new-project', {
            destroyOnHide: true,
            width: 900
        });

        var $content = $('<div class="n2_create_new_project"></div>');
        modal.addContent($content);

        $('<div class="n2_create_new_project__heading"></div>').text(n2_('Create new project'))
            .appendTo($content);

        var $projectTypeRow = _N2.UI.modalFormRowLarge()
            .appendTo($content);

        var $projectType = _N2.UI.modalFieldSelectIcon($projectTypeRow, 'project-type', n2_('Project type'), {
            slider: {
                icon: 'ssi_64 ssi_64--fullwidthslider',
                label: n2_('Slider')
            },
            block: {
                icon: 'ssi_64 ssi_64--block',
                label: n2_('Block')
            }
        }, 'slider');

        var refreshForm = function () {

            var projectType = $projectType.val(),
                sliderType = 'simple';

            $sliderContainer.toggleClass('n2_form_element--hidden', projectType === 'block');

            if (projectType === 'block') {
                sliderType = 'block';
            }
        };

        $projectType.on('nextendChange', refreshForm);


        var $sliderContainer = _N2.UI.modalFormContainer($content, 'slider-settings-container'),
            $sliderTypeRow = _N2.UI.modalFormRowLarge()
                .appendTo($sliderContainer);

        var $sliderType = _N2.UI.modalFieldSelectIcon($sliderTypeRow, 'slider-type', n2_('Slider type'), {
            simple: {
                icon: 'ssi_64 ssi_64--slider',
                label: n2_('Simple')
            },
            carousel: {
                icon: 'ssi_64 ssi_64--carousel',
                label: n2_('Carousel')
            },
            showcase: {
                icon: 'ssi_64 ssi_64--showcase',
                label: n2_('Showcase')
            }
        }, 'simple');

        $sliderType.on('nextendChange', refreshForm);
        var $nonFreeTypes = $sliderTypeRow.find('.n2_field_select_icon__option:not([data-value="simple"])')
            .css({
                'pointer-events': 'none',
                'opacity': 0.5
            });

        $nonFreeTypes.find('.n2_field_select_icon__option_label').append(document.createTextNode(' (' + n2_('Pro') + ')'));
    

        _N2.UI.modalSubHeading(n2_('Settings')).appendTo($content);

        var $settingsRow = _N2.UI.modalFormRow()
            .appendTo($content);

        var $projectNameField = _N2.UI.modalFieldText($settingsRow, 'project-name', n2_('Name'), n2_('My project'))
            .css('width', '200px');

        var $width = _N2.UI.modalNumber($settingsRow, 'slider-width', n2_('Width'), 1200, {
            min: 200,
            unit: 'px'
        });

        var $height = _N2.UI.modalNumber($settingsRow, 'slider-height', n2_('Height'), 600, {
            min: 100,
            unit: 'px'
        });

        var responsiveModes = {
            boxed: n2_('Boxed'),
            fullwidth: n2_('Full width'),
            fullpage: n2_('Full page')
        };

        var $responsiveMode = _N2.UI.modalSelect($settingsRow, 'slider-responsive-mode', n2_('Layout'), responsiveModes, 'fullwidth');
        $settingsRow.find('option[value="fullpage"]')
            .attr('disabled', 'disabled')
            .text(n2_('Full page') + ' (' + n2_('Pro') + ')');
    


        var $saveButton = _N2.UI.modalButton(n2_('Create'), 'green')
            .on('click', (function (e) {
                e.preventDefault();

                var data = {
                    groupID: this.groupID,
                    projectName: $projectNameField.val(),
                    projectType: $projectType.val(),
                    sliderWidth: parseInt($width.val()),
                    sliderHeight: parseInt($height.val()),
                    responsiveMode: $responsiveMode.val()
                };

                _N2.AjaxHelper.ajax({
                    type: "POST",
                    url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, {
                        nextendaction: 'create'
                    }),
                    data: data,
                    dataType: 'json'
                }).done((function (response) {
                    _N2.LoadingScreen.startLoading();
                }).bind(this));

            }).bind(this));
        modal.addButton($saveButton);

        refreshForm();

        modal.show();
    };

    CreateSlider.prototype.showExampleSlidersWindow = function () {
        var that = this;
        $('body').css('overflow', 'hidden');
        var pro = 0;
        var frame = $('<iframe src="//smartslider3.com/demo-import/?pro=' + pro + '&version=' + N2SS3VERSION + '&utm_campaign=' + N2SS3C + '&utm_source=import-slider-frame&utm_medium=smartslider-' + N2PLATFORM + '-' + (pro ? 'pro' : 'free') + '"></iframe>').css({
                position: 'fixed',
                zIndex: 100000,
                left: 0,
                top: 0,
                width: '100%',
                height: '100%'
            }).appendTo('body'),
            closeFrame = function () {
                $('body').css('overflow', '');
                frame.remove();
                window.removeEventListener("message", listener, false);
            },
            importSlider = function (href) {
                _N2.AjaxHelper.ajax({
                    type: "POST",
                    url: _N2.AjaxHelper.makeAjaxUrl(that.ajaxUrl, {
                        nextendaction: 'importDemo'
                    }),
                    data: {
                        groupID: that.groupID,
                        key: _N2.Base64.encode(href.replace(/^(http(s)?:)?\/\//, '//'))
                    },
                    dataType: 'json'
                }).fail(function () {
                    //closeFrame();
                });
            },
            listener = function (e) {
                if (e.origin !== "http://smartslider3.com" && e.origin !== "https://smartslider3.com")
                    return;
                var msg = e.data;
                switch (msg.key) {
                    case 'importSlider':
                        if (!that.skipNewsletter) {
                            that.joinCommunity(function () {
                                importSlider(msg.data.href);
                            });
                        } else {
                            importSlider(msg.data.href);
                        }
                    
                        return;

                        break;
                    case 'closeWindow':
                        closeFrame();
                }
            };

        _N2.Esc.add((function () {
            closeFrame();
            return true;
        }).bind(this));

        window.addEventListener("message", listener, false);
    };

    CreateSlider.prototype.joinCommunity = function (cb) {

        var close = function (e, action) {
            _N2.AjaxHelper
                .ajax({
                    type: "POST",
                    url: _N2.AjaxHelper.makeAjaxUrl(_N2.AjaxHelper.getAdminUrl('ss3-admin'), {
                        nextendcontroller: 'settings',
                        nextendaction: action || 'dismissNewsletterSampleSliders'
                    }),
                    dataType: 'json'
                });

        };

        this.modal = new _N2.ModalSimple('url-modal');

        this.modal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--newsletter', 'purple'));

        this.modal.addContent(_N2.UI.modalHeading(n2_('Join The Smart Slider 3 Community')));

        this.modal.addContent(_N2.UI.modalParagraph(n2_('Join more than 120,000 subscribers and get access to the latest slider templates, tips, tutorials and other exclusive contents directly to your inbox.')).css('margin-bottom', '12px'));

        this.modal.$modalBody.css('text-align', 'center');

        var $form = $('<form></form>');

        $form.append(_N2.UI.modalInput('EMAIL', 'email', '', 'example@google.com'));

        $form.append(_N2.UI.modalInput(N2PLATFORM.toUpperCase(), 'hidden', 'Yes'));

        $form.append(_N2.UI.modalInput('SOURCE', 'hidden', 'Smart Slider 3'));

        this.modal.addContent($form);

        var $insertButton = _N2.UI.modalButton(n2_('Subscribe'), 'purple');
        this.modal.addButton($insertButton);

        $insertButton.on('click', (function (e) {
            _N2.AjaxHelper.ajax({
                type: "POST",
                url: "https://secure.nextendweb.com/mailchimp/subscribe.php",
                data: $form.serialize(),
                dataType: 'json'
            })
                .done(function () {
                    cb();
                });

            close(e, 'subscribed');

        }).bind(this));

        this.modal.$modalBodyOuter.find('.n2_modal__close').on('click', function (e) {
            close(e);
            cb();
        });

        this.modal.show();
    };

    return CreateSlider;
});_N2.d('SliderEdit', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     * @augments {_N2.Form}
     */
    function SliderEdit(options) {

        this.options = _Assign({
            previewInNewWindow: false,
            saveAjaxUrl: '',
            previewUrl: '',
            ajaxUrl: '',
            formData: {}
        }, options);

        this.url = this.options.ajaxUrl;
        this.values = this.options.formData;

        _N2.Form.prototype.constructor.call(this);
        _N2.r('documentReady', this.onReady.bind(this));
    }

    SliderEdit.prototype = Object.create(_N2.Form.prototype);
    SliderEdit.prototype.constructor = SliderEdit;

    SliderEdit.prototype.onReady = function () {

        this.setID('#n2-ss-edit-slider-form');

        _N2.Form.prototype.onReady.call(this);

        this.$form.on('submit', this.onSaveSubmit.bind(this));

        this.initPreview();
    };

    SliderEdit.prototype.onKeyDown = function (e) {

        var handled = false;
        if (e.ctrlKey || e.metaKey) {
            if (e.code === 'Digit1') { // ctrl + 1
                this.showPreview();
                handled = true;
            }
        }

        if (handled) {
            e.preventDefault();
        } else {
            handled = _N2.Form.prototype.onKeyDown.call(this, e);
        }

        return handled;
    };

    SliderEdit.prototype.initButtons = function () {

        this.$saveButton = $('.n2_slider_settings_save')
            .on('click', this.actionSave.bind(this));

        $('.n2_slider_settings_back')
            .on('click', this.actionCancel.bind(this));
    };

    SliderEdit.prototype.actionSave = function (e) {
        e && e.preventDefault();

        this.skipChangeConfirm = true;

        this.$form.trigger("submit");
    };

    SliderEdit.prototype.actionCancel = function (e) {
        e && e.preventDefault();

        window.location = e.currentTarget.href;
    };

    SliderEdit.prototype.onSaveSubmit = function (e) {
        if (this.$form.attr('target') === undefined) {
            e.preventDefault();
            _N2.AjaxHelper
                .ajax({
                    type: 'POST',
                    url: this.options.saveAjaxUrl,
                    data: this.$form.serialize(),
                    dataType: 'json',
                    success: this.onAfterSave.bind(this)
                });
        }
    };

    SliderEdit.prototype.onAfterSave = function (response) {
        $('.n2_header__heading_primary').text($('#slidertitle').val());

        if (response && response.data && response.data.changedFields) {
            for (var k in response.data.changedFields) {
                if (response.data.changedFields.hasOwnProperty(k)) {
                    $('#' + k).val(response.data.changedFields[k]);
                }
            }
            if (response.data.changedFields['slideralias'] !== undefined) {
                var newAlias = response.data.changedFields[k],
                    $aliasCode = $('.n2_ss_slider_publish__option_code[data-mode="alias"]');
                if (!$aliasCode.length) {
                    $aliasCode = $('<div class="n2_ss_slider_publish__option_code" data-mode="alias"></div>')
                        .insertAfter($('.n2_ss_slider_publish__option_code[data-mode="id"]'));
                }

                if (newAlias) {
                    $aliasCode.text('[smartslider3 alias="' + newAlias + '"]');
                } else {
                    $aliasCode.remove();
                }
            }
        }

        this.afterSave();
    };

    SliderEdit.prototype.initPreview = function () {
        if (!this.options.previewInNewWindow) {
            this.modal = new _N2.ModalIframe('preview-slider');
        }

        $('.n2_top_bar_main__preview')
            .on('click', (function (e) {
                e.preventDefault();
                this.showPreview();
            }).bind(this));
    };

    SliderEdit.prototype.showPreview = function () {

        if (_N2.WindowManager.getCurrentWindow() === 'main') {
            if (!this.options.previewInNewWindow) {
                this.modal.show();
            } else {
                _N2.PreviewPopup('', 'preview-slider');
            }
            this.$form
                .attr({
                    action: _N2.AjaxHelper.makeAjaxUrl(this.options.previewUrl, {
                        nextendaction: this.options.previewInNewWindow ? 'index' : 'slider'
                    }),
                    target: 'preview-slider'
                })
                .trigger("submit")
                .attr({
                    action: this.formAction,
                    target: null
                });
        }
    };

    return SliderEdit;
});_N2.d('SliderImport', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     * @augments {_N2.Form}
     */
    function SliderImport(options) {

        _N2.Form.prototype.constructor.call(this);
        _N2.r('documentReady', this.onReady.bind(this));
    }

    SliderImport.prototype = Object.create(_N2.Form.prototype);
    SliderImport.prototype.constructor = SliderImport;

    SliderImport.prototype.onReady = function () {

        this.setID('#n2-ss-form-slider-import');

        _N2.Form.prototype.onReady.call(this);

        this.$localImportSwitch = $('#sliderupload_or_local');
        this.$localImport = $('#sliderlocal-import-file');
        this.$uploadImport = $('#sliderimport-file');

        this.$form.on('submit', this.onSubmit.bind(this));
    };

    SliderImport.prototype.onChange = function () {
        this.hasChange = false;

        if (parseInt(this.$localImportSwitch.val())) {
            if (this.$localImport.val() !== '') {
                this.hasChange = true;
            }
        } else {
            if (this.$uploadImport.val() !== '') {
                this.hasChange = true;
            }
        }

        this.$saveButton.toggleClass('n2_button--inactive', !this.hasChange);
    };

    SliderImport.prototype.initButtons = function () {

        this.$saveButton = $('.n2_slider_import')
            .on('click', this.actionSave.bind(this));

        $('.n2_slider_import_back')
            .on('click', this.actionCancel.bind(this));
    };

    SliderImport.prototype.actionSave = function (e) {
        e && e.preventDefault();

        this.skipChangeConfirm = true;

        this.$form.trigger("submit");

        this.afterSave();
    };

    SliderImport.prototype.actionCancel = function (e) {
        e && e.preventDefault();

        window.location = e.currentTarget.href;
    };

    SliderImport.prototype.onSubmit = function (e) {
        e.preventDefault();

        var formData = new FormData(this.$form[0]);

        _N2.AjaxHelper.ajax({
            type: "POST",
            contentType: false,
            url: this.$form.attr('action'),
            processData: false,
            data: formData,
            dataType: 'json'
        });
    };

    return SliderImport;
});_N2.d('ManageSliders', '$', function () {
        var $ = _N2.$;

        /**
         * @memberOf _N2
         *
         * @param groupID
         * @param options
         * @param shouldSkipLicenseModal
         * @constructor
         */
        function ManageSliders(groupID, options, shouldSkipLicenseModal) {
            this.groupID = groupID;
            this.isDashBoard = this.groupID == 0;
            this.exportAllUrl = options.exportAllUrl;
            this.ajaxUrl = options.ajaxUrl;
            this.previewUrl = options.previewUrl;
            this.importUrl = options.importUrl;
            this.paginationUrl = options.paginationUrl;
            this.sliders = [];
            this.sliderPanel = $('.n2_slider_manager');
            this.actionBar = this.sliderPanel.find('.n2_slider_manager__action_bar');
            this.slidersContainer = this.sliderPanel.find('.n2_slider_manager__content');

            if (this.isDashBoard) {
                /* Paginator */

                this.paginationContainer = this.sliderPanel.find('.n2_slider_manager__paginator');
                this.paginationNumbers = this.sliderPanel.find('.n2_slider_manager__paginator_buttons--numbers');
                this.limiterContainer = this.sliderPanel.find('.n2_slider_manager__paginator_limiter');

                this.sliderCount = this.paginationContainer.data('countstart');
                this.paginationLimit = this.paginationContainer.data('limitstart');
                this.paginationIndex = this.paginationContainer.data('currentstart');

                this.paginationPages = Math.ceil(this.sliderCount / this.paginationLimit);
                /********/

                /*Search*/
                this.searchContainer = this.actionBar[0].querySelector('.n2_slider_manager__search');
                this.isInSearch = false;
                this.searchForm = this.searchContainer.querySelector(".n2_slider_manager__search_form");
                this.searchInput = this.searchForm.querySelector('.n2_slider_manager__search_input');
                this.searchLabel = this.sliderPanel[0].querySelector('.n2_slider_manager__search_label_item');
                this.XHR;
                /********/
            }

            /*Ordering*/
            this.orderBy = this.sliderPanel.data('orderby');
            this.isOrderable = this.orderBy === 'ordering';
            this.orderDirection = this.sliderPanel.data('orderbydirection');
            /********/


            var sliders = this.slidersContainer.find('.n2_slider_box');

            for (var i = 0; i < sliders.length; i++) {
                this.sliders.push(new _N2.Slider(this, sliders.eq(i)));
            }
            this.changed();

            this.initOrderable();

            this.create = new _N2.CreateSlider(groupID, this.ajaxUrl, shouldSkipLicenseModal, options.userEmail, options.skipNewsletter, this.importUrl);

            this.initBulk();
            if (this.isDashBoard) {
                this.initPagination();
                this.initOrdering();
                this.initStateHandler();
                this.setSearchEvents();
            }

        }

        ManageSliders.prototype.changed = function () {
            $('html').attr('data-sliders', this.sliderCount);
        };

        ManageSliders.prototype.initSliders = function () {
            var sliderNodes = this.slidersContainer.find('.n2_slider_box'),
                sliders = [];
            for (var i = 0; i < sliderNodes.length; i++) {
                var slider = sliderNodes.eq(i).data('slider');
                sliders.push(slider);
            }
            this.sliders = sliders;
            this.changed();
            $(window).triggerHandler('SmartSliderSidebarSlidersChanged');
        };

        ManageSliders.prototype.initOrderable = function () {
            if (this.isOrderable && !this.isInSearch) {
                var sortableObject = {
                    helper: 'clone',
                    items: "> .n2_slider_box",
                    stop: this.saveOrder.bind(this),
                    placeholder: 'n2_slider_manager__sortable_placeholder',
                    distance: 10
                };
                this.uiSortable = new _N2.UISortable(this.slidersContainer, sortableObject);
            } else {
                this.uiSortable?.destroy();
            }
        };

        ManageSliders.prototype.setOrderable = function () {
            this.isOrderable = this.orderBy === 'ordering';
        }

        ManageSliders.prototype.saveOrder = function (e, ui) {
            var sliderNodes = this.slidersContainer.find('.n2_slider_box'),
                sliders = [],
                ids = [],
                originalIds = [],
                orders = [],
                i;
            for (i = 0; i < sliderNodes.length; i++) {
                var slider = sliderNodes.eq(i).data('slider');
                sliders.push(slider);
                ids.push(slider.getId());
                if (this.isDashBoard) {
                    orders.push(slider.order);
                }
            }
            for (i = 0; i < this.sliders.length; i++) {
                originalIds.push(this.sliders[i].getId());
            }

            if (JSON.stringify(originalIds) !== JSON.stringify(ids)) {
                $(window).triggerHandler('SmartSliderSidebarSlidersOrderChanged');

                var queries = {
                    nextendcontroller: 'sliders',
                    nextendaction: 'order'
                };

                _N2.AjaxHelper.ajax({
                    type: 'POST',
                    url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, queries),
                    data: {
                        groupID: this.groupID,
                        sliderorder: ids,
                        isReversed: (this.orderDirection === 'DESC' ? 1 : 0),
                        orders: orders
                    }
                });

                this.sliders = sliders;
            }

        };

        ManageSliders.prototype.trashSliders = function (ids, sliders) {
            _N2.AjaxHelper.ajax({
                url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, {
                    nextendcontroller: 'sliders',
                    nextendaction: 'trash'
                }),
                type: 'POST',
                data: {
                    groupID: this.groupID,
                    sliders: ids
                }
            }).done((function () {
                sliders.forEach(slider => {
                    slider.remove();
                    this.sliders.splice(this.sliders.indexOf(slider), 1);
                });

                if (this.isDashBoard) {
                    if (this.isInSearch) {
                        this.refreshSearchLabel();
                        this.changeUiVisibility();
                    } else {
                        this.loadSliders();
                        this.initSliders();
                    }
                }

                this.changed();
                this.leaveBulk();


            }).bind(this));
        };

        ManageSliders.prototype.duplicateSliders = function (ids, slides) {
            this.sliders.forEach(slider => {
                if (slider.selected) {
                    slider.duplicate($.Event("click", {
                        currentTarget: null
                    }));
                }
            });
        };

        ManageSliders.prototype.exportSliders = function (ids, sliders) {

            window.location.href = _N2.N2QueryString.add_query_arg({
                sliders: ids,
                inSearch: this.isInSearch
            }, this.exportAllUrl);
        };

        ManageSliders.prototype.initBulk = function () {

            this.selection = [];

            this.isBulkSelection = false;

            $('#n2_slider_manager_bulk_actions a[data-action]')
                .on('click', (function (e) {
                    e.preventDefault();
                    switch ($(e.currentTarget).data('action')) {
                        case 'duplicate':
                            this.bulkAction('duplicateSliders', false);
                            break;
                        case 'trash':
                            this.bulkAction('trashSliders', false);
                            break;
                        case 'export':
                            this.bulkAction('exportSliders', false);
                            break;
                        case 'select-all':
                            this.bulkSelect(function (slider) {
                                slider.select();
                            });
                            break;
                        case 'select-none':
                            this.bulkSelect(function (slider) {
                                slider.deSelect();
                            });
                            break;
                    }
                }).bind(this));

        };

        ManageSliders.prototype.addSelection = function (slider) {
            if (this.selection.length === 0) {
                this.enterBulk();
            }
            this.selection.push(slider);
        };

        ManageSliders.prototype.removeSelection = function (slider) {
            this.selection.splice(this.selection.indexOf(slider), 1);
            if (this.selection.length === 0) {
                this.leaveBulk();
            }
        };

        ManageSliders.prototype.bulkSelect = function (cb) {
            this.sliders.forEach(slider => {
                cb(slider);
            });
        };

        ManageSliders.prototype.bulkAction = function (action, skipGroups) {
            var sliders = [],
                ids = [];
            this.bulkSelect(function (slider) {
                if (slider.selected && (!skipGroups || !slider.isGroup)) {
                    sliders.push(slider);
                    ids.push(slider.getId());
                }
            });
            if (ids.length) {
                this[action](ids, sliders);
                this.leaveBulk();
            } else {
                if (skipGroups) {
                    _N2.Notification.notice('Please select one or more sliders for the action!');
                } else {
                    _N2.Notification.notice('Please select one or more sliders or groups for the action!');
                }
            }
        };

        ManageSliders.prototype.enterBulk = function () {
            if (!this.isBulkSelection) {
                this.isBulkSelection = true;
                if (this.isOrderable) {
                    this.uiSortable.setOption('disabled', true);
                }
                $('body').addClass('n2_body--bulk-select');
            }
        };

        ManageSliders.prototype.leaveBulk = function () {
            if (this.isBulkSelection) {
                if (this.isOrderable) {
                    this.uiSortable.setOption('disabled', false);
                }
                $('body').removeClass('n2_body--bulk-select');

                this.sliders.forEach(slider => {
                    slider.deSelect();
                });

                this.selection = [];
                this.isBulkSelection = false;
            }
        };

        /*Ordering*/
        ManageSliders.prototype.initOrdering = function () {
            this.sliderPanel[0].querySelectorAll('.n2_floating_menu__item-order').forEach(el => {
                _addEventListener(el, 'click', (e) => this.changeOrdering(e, el));
            });
        }

        ManageSliders.prototype.changeOrdering = function (e, el) {
            if (!this.isInSearch) {
                e.preventDefault();
                this.orderBy = _NodeGetData(el, 'ordering');
                this.orderDirection = _NodeGetData(el, 'orderdirection')
                this.setOrderable();
                this.refreshOrdering(el);

                this.setPaginationIndex(0);

                this.loadSliders();
            }
        }

        ManageSliders.prototype.refreshOrdering = function (el) {
            const activeMenuItem = document.querySelector('.n2_floating_menu__item-order.n2_floating_menu__item--active');
            if (activeMenuItem) {
                activeMenuItem.classList.remove('n2_floating_menu__item--active');
            }
            el.classList.add('n2_floating_menu__item--active');
        }


        /*StateHandler*/

        ManageSliders.prototype.initStateHandler = function () {
            const url = new URL(window.location);
            if (this.isStateExists(url.searchParams)) {
                url.searchParams.set("pageIndex", this.transformPaginationIndex(this.paginationIndex));
            }
            let state = {options: {'pageIndex': this.paginationIndex}};
            history.replaceState(state, null, url);
        }

        ManageSliders.prototype.setStateHandlerEvents = function () {
            _addEventListener(window, 'popstate', this.popStateHandler.bind(this));
        }

        ManageSliders.prototype.isStateExists = function (searchParams) {
            return (searchParams && searchParams.has('pageIndex'));
        }

        ManageSliders.prototype.isStateChanged = function (state, searchParams) {
            if (this.isStateExists(searchParams)) {
                let pageIndex = searchParams.get('pageIndex');
                return (this.transformPaginationIndex(state.options.pageIndex) != pageIndex);
            }
            return true;
        }

        ManageSliders.prototype.setState = function () {
            const url = new URL(window.location);
            let state = {options: {'pageIndex': this.paginationIndex}};
            if (this.isStateChanged(state, url.searchParams)) {
                if (this.isStateExists(url.searchParams)) {
                    url.searchParams.set("pageIndex", this.transformPaginationIndex(this.paginationIndex));
                } else {
                    url.searchParams.append('pageIndex', this.transformPaginationIndex(this.paginationIndex));
                }
                history.pushState(state, null, url)
            }

        }

        ManageSliders.prototype.popStateHandler = function (e) {
            if (e.state) {
                this.setPaginationIndex(e.state.options.pageIndex);
                this.loadSliders();
            }
        }


        /*Transform stored PageIndex to Real Page Number for Query String*/
        ManageSliders.prototype.transformPaginationIndex = function (pageIndex) {
            return pageIndex + 1;
        }

        /*Pagination*/
        ManageSliders.prototype.initPagination = function () {
            this.setPaginatorEvents();
            this.setPaginatorArrowVisibility();

            this.setLimiterEvents();

            this.setStateHandlerEvents();
        }

        ManageSliders.prototype.reloadPagination = function () {
            this.refreshPaginationLabel();

            this.setPaginationPages();
            this.reloadPaginator();

            this.initOrderable();
            this.leaveBulk();
        }


        /*Paginator*/
        ManageSliders.prototype.setPaginatorEvents = function (onlyButtons = false) {
            this.paginationContainer[0].querySelectorAll(`.n2_slider_manager__paginator_item${(onlyButtons) ? ":not(.n2_slider_manager__paginator_item_arrow)" : ""}[data-page]`).forEach(el => {
                _addEventListener(el, 'click', (e) => this.changePaginator(e, el));
            });
        };

        ManageSliders.prototype.reloadPaginator = function () {
            this.paginationNumbers[0].innerHTML = "";
            let pages = this.getPaginatorList(this.paginationPages, this.paginationIndex, 1);

            if (pages.length > 1 && this.paginationLimit !== "all") {
                pages.forEach(el => {
                    _NodeAppendTo(this.renderPaginatorItem(el), this.paginationNumbers[0]);
                });
                this.setPaginatorEvents(true);
            }

            this.setPaginatorArrowVisibility();
        }

        ManageSliders.prototype.changePaginator = function (e, el) {
            e.preventDefault();
            let currentIndex = _NodeGetData(el, 'page')

            if (this.setPaginationIndex(currentIndex) !== false) {
                this.loadSliders();
                this.setState();

                window.scrollTo({top: 0});
            }

        }


        /*
        * @return int/bool - pageIndex or false if page not changed
        * */
        ManageSliders.prototype.setPaginationIndex = function (index) {
            switch (index) {
                case "next":
                    index = this.paginationIndex + 1;
                    break;
                case "prev":
                    index = this.paginationIndex - 1;
                    break;

            }
            let newIndex = Math.max(0, Math.min(this.paginationPages - 1, index));

            if (newIndex === this.paginationIndex)
                return false;
            return this.paginationIndex = +index;

        }

        ManageSliders.prototype.setPaginationPages = function () {
            let pages = Math.ceil(this.sliderCount / this.paginationLimit);
            this.paginationPages = (pages) ? pages : 1;
        }

        ManageSliders.prototype.setPaginatorArrowVisibility = function () {
            this.paginationContainer[0].querySelector('.n2_slider_manager__paginator_item_arrow--prev').classList.toggle("n2_slider_manager__paginator_item_arrow--disabled", (this.paginationLimit === "all" || this.paginationPages < 2 || this.paginationIndex === 0));
            this.paginationContainer[0].querySelector('.n2_slider_manager__paginator_item_arrow--next').classList.toggle("n2_slider_manager__paginator_item_arrow--disabled", (this.paginationLimit === "all" || this.paginationPages < 2 || this.paginationIndex === (this.paginationPages - 1)));
        }

        ManageSliders.prototype.getPaginatorList = function (totalPages, page, delta) {
            let left = page - delta,
                right = page + delta,
                range = [];

            for (let i = 0; i < totalPages; i++) {
                if (i === 0 || i === totalPages - 1 || i >= left && i <= right) {
                    range.push(i);
                } else if (i === left - 1 || i === right + 1) {
                    range.push(-1);
                }
            }
            return range;
        }

        ManageSliders.prototype.renderPaginatorItem = function (index) {
            if (index === -1) {
                return _CreateElementDivText('...', ['n2_slider_manager__paginator_item', 'n2_slider_manager__paginator_item_spacer'])
            } else {
                let classes = ['n2_slider_manager__paginator_item'];
                if (index === this.paginationIndex) {
                    classes.push('n2_slider_manager__paginator_item--active');
                }
                let linkButton = _CreateElementLink('', '#', classes, {'tabindex': -1}, {'page': index});
                _CreateElementTextIn(linkButton, 'span', index + 1, ['n2_button_plain__label']);
                return linkButton;
            }
        }


        /*Limiter*/

        ManageSliders.prototype.setLimiterEvents = function () {
            this.limiterContainer[0].querySelectorAll('.n2_floating_menu__item-limiter').forEach(el => {
                _addEventListener(el, 'click', (e) => this.changeLimiter(e, el));
            });
        };

        ManageSliders.prototype.refreshLimiter = function (el) {
            document.querySelector('.n2_floating_menu__item-limiter.n2_floating_menu__item--active')?.classList.remove('n2_floating_menu__item--active');
            el.classList.add('n2_floating_menu__item--active');
        }

        ManageSliders.prototype.reloadLimiter = function () {
            let limitText = Number.isInteger(+this.paginationLimit) ? this.paginationLimit : n2_('All');
            this.limiterContainer[0].getElementsByClassName('limitNumber')[0].innerHTML = limitText;
        }

        ManageSliders.prototype.changeLimiter = function (e, el) {
            e.preventDefault();
            this.paginationLimit = _NodeGetData(el, 'limit');
            this.setPaginationIndex(0);
            this.setState();
            this.refreshLimiter(el);
            this.reloadLimiter();

            this.loadSliders();

            window.scrollTo({top: 0});
        }


        /*PaginationLabel*/

        ManageSliders.prototype.refreshPaginationLabel = function () {
            this.paginationContainer[0].getElementsByClassName('n2_slider_manager__paginator_label')[0].classList.toggle("n2_slider_manager__paginator_label--nosliders", (this.sliderCount <= 0));
            let convertedPaginationLimit = (this.paginationLimit === "all") ? 0 : this.paginationLimit;
            this.paginationContainer[0].getElementsByClassName('n2_slider_manager__paginator_label_item__from')[0].innerHTML = (this.paginationIndex * convertedPaginationLimit) + 1;
            this.paginationContainer[0].getElementsByClassName('n2_slider_manager__paginator_label_item__to')[0].innerHTML = ((this.paginationIndex * convertedPaginationLimit - 1) + this.sliders.length) + 1;
            this.paginationContainer[0].getElementsByClassName('n2_slider_manager__paginator_label_item__max')[0].innerHTML = this.sliderCount;

        };
        /*AjaxPaginationLoad*/

        ManageSliders.prototype.loadSliders = function () {
            _N2.AjaxHelper.ajax({
                type: "POST",
                url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, {
                    nextendcontroller: 'sliders',
                    nextendaction: 'pagination'
                }),
                data: {
                    'pageIndex': +this.paginationIndex,
                    'limit': this.paginationLimit,
                    'orderBy': this.orderBy,
                    'orderDirection': this.orderDirection
                },
                dataType: 'json'
            }).done((function (response) {
                this.removeSliders(this.sliders);
                this.sliders = [];

                if (response.data.sliders) {
                    if (response.data.pageIndex || response.data.pageIndex === 0) {
                        this.setPaginationIndex(response.data.pageIndex);
                    }
                    this.sliders = this.renderSliders(response.data.sliders);
                }

                this.setSliderCount(response.data.sliderCount);
                this.changed();

                this.reloadPagination();

            }).bind(this));
        }


        /*Search*/

        ManageSliders.prototype.changeUiVisibility = function () {
            this.sliderPanel[0].classList.toggle('n2_slider_manager--search', this.isInSearch);
            this.sliderPanel[0].classList.toggle('n2_slider_manager--noresult', (this.isInSearch && !this.sliders.length));
            this.actionBar[0].querySelector('.n2_slider_order').classList.toggle('n2_button_disabled', this.isInSearch);
        }

        ManageSliders.prototype.setSearchEvents = function () {
            this.searchForm.addEventListener('submit', e => {
                e.preventDefault();
                this.startSearch();
            });

            this.searchInput.addEventListener('input', e => {
                this.startSearch();
            });

            this.searchContainer.querySelector('.n2_slider_manager__search_icon--abort').addEventListener('click', e => {
                this.searchInput.value = "";
                this.abortSearch();
            });
        }

        ManageSliders.prototype.searchDebounced = NextendDeBounce(function (keyword) {
            this.loadSearchResult(keyword)
        }, 300);


        ManageSliders.prototype.startSearch = function () {
            if (this.validateInputValue()) {
                this.abortSearch();
            } else {
                this.searchDebounced(this.searchInput.value);
            }
        }

        ManageSliders.prototype.validateInputValue = function () {
            return (!this.searchInput.value || this.searchInput.value === "");
        }

        ManageSliders.prototype.abortSearch = function () {
            this.loadSliders();
            this.isInSearch = false;
            this.changeUiVisibility();

        }

        ManageSliders.prototype.refreshSearchLabel = function () {
            this.searchLabel.querySelector('.n2_slider_manager__search_label_item__counter').innerText = this.sliders.length;
            this.searchLabel.querySelector('.n2_slider_manager__search_label_item__keyword').innerText = '"' + this.searchInput.value + '"';
        }

        ManageSliders.prototype.loadSearchResult = function (keyword) {
            if (this.validateInputValue()) {
                this.abortSearch();
            } else {
                if (this.XHR) {
                    this.XHR.abort();
                }
                this.XHR = _N2.AjaxHelper.ajax({
                    type: "POST",
                    url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, {
                        nextendcontroller: 'sliders',
                        nextendaction: 'search'
                    }),
                    data: {
                        'keyword': keyword
                    },
                    dataType: 'json'
                }).done((function (response) {
                    this.isInSearch = true;

                    this.removeSliders(this.sliders);
                    this.sliders = [];

                    if (response.data.length) {
                        this.sliders = this.renderSliders(response.data);
                    }
                    this.refreshSearchLabel();
                    this.changeUiVisibility();
                }).bind(this));
            }
        }


        /*Sliders*/
        ManageSliders.prototype.setSliderCount = function (count) {
            this.sliderCount = +count;
        }

        ManageSliders.prototype.removeSliders = function (sliders) {
            sliders.forEach(slider => {
                slider.remove();
            });
        }

        ManageSliders.prototype.renderSliders = function (sliders) {
            let sliderArray = [];
            sliders.forEach(el => {
                slider = this.renderSlider(el);
                sliderArray.push(new _N2.Slider(this, $(slider)));
                _NodeAppendTo(slider, this.slidersContainer[0])
            });
            this.initOrderable();
            this.leaveBulk();

            return sliderArray;
        }


        ManageSliders.prototype.renderSlider = function (el) {
            const container = _CreateElementDiv(
                ['n2_slider_manager__box', 'n2_slider_box', (el.isGroup ? 'n2_slider_box--group' : 'n2_slider_box--slider')],
                null,
                {
                    'group': el.isGroup,
                    'title': el.title,
                    'sliderid': el.id,
                    'ordering': el.order
                },
            );

            /*ImageBox*/
            let imageBox = _CreateElementDivIn(container, 'n2_slider_box__content');

            /*Thumbnail*/
            if (el.thumbnail) {
                _NodeSetStyle(imageBox, 'background-image', 'url("' + el.thumbnail + '")');
            } else {
                let iconBox = _CreateElementDivIn(imageBox, 'n2_slider_box__icon');
                let iconBoxContainer = _CreateElementDivIn(iconBox, 'n2_slider_box__icon_container');
                let iconClass = "ssi_64--image";
                if (el.isGroup) {
                    iconClass = "ssi_64--folder";
                }
                _CreateElementIn(iconBoxContainer, 'i', ['ssi_64', iconClass]);
            }


            /*SliderBox ImageOverlay*/
            let sliderBoxOverLay = _CreateElementDivIn(imageBox, 'n2_slider_box__slider_overlay');
            _CreateElementLinkIn(sliderBoxOverLay, '', el.editUrl, 'n2_slider_box__slider_overlay_link');
            _CreateElementLinkIn(sliderBoxOverLay, n2_('Edit'), el.editUrl, ['n2_slider_box__slider_overlay_edit_button', 'n2_button', 'n2_button--small', 'n2_button--green']);
            let sliderBoxOverlaySelect = _CreateElementDivIn(sliderBoxOverLay, 'n2_slider_box__slider_select_tick');
            _CreateElementIn(sliderBoxOverlaySelect, 'i', ['ssi_16', 'ssi_16--check']);

            /*SliderBox Identifiers*/
            let sliderBoxIdentifiers = _CreateElementDivIn(imageBox, 'n2_slider_box__slider_identifiers');
            _CreateElementDivTextIn(sliderBoxIdentifiers, '#' + el.id, 'n2_slider_box__slider_identifier');
            if (+el.isGroup) {
                _CreateElementDivTextIn(sliderBoxIdentifiers, n2_('Group'), 'n2_slider_box__slider_identifier');
            }
            if (el.alias) {
                _CreateElementDivTextIn(sliderBoxIdentifiers, el.alias, 'n2_slider_box__slider_identifier');
            }
            /*SliderBox MoreActions*/
            let sliderBoxMore = _CreateElementDivIn(imageBox, 'n2_slider_box__slider_actions');
            let sliderBoxContainer = _CreateElementLinkIn(sliderBoxMore, '', '#', ['n2_slider_box__slider_action_more', 'n2_button_icon', 'n2_button_icon--small', 'n2_button_icon--grey-dark']);
            _CreateElementIn(sliderBoxContainer, 'i', ['ssi_16', 'ssi_16--more']);

            /*SliderBox Footer*/
            let sliderBoxFooter = _CreateElementDivIn(container, 'n2_slider_box__footer');
            if (+el.isGroup) {
                let footerIconContainer = _CreateElementDivIn(sliderBoxFooter, 'n2_slider_box__footer_icon')
                _CreateElementIn(footerIconContainer, 'i', ['ssi_16', 'ssi_16--folderclosed']);
            }
            _CreateElementDivTextIn(sliderBoxFooter, '', 'n2_slider_box__footer_title').innerText = el.title;
            _CreateElementDivTextIn(sliderBoxFooter, el.childrenCount, 'n2_slider_box__footer_children_count');

            /*screenReader*/
            _CreateElementLinkIn(container, '', '#', 'n2_slide_box__screen_reader').innerText = n2_('Edit Slider') + ":" + el.title;

            return container;
        }

        return ManageSliders;
    }
)
;_N2.d('SelectSlider', '$', function () {
    var $ = _N2.$;

    var selectSliderModal = false;

    function SelectSliderModal() {
        this.cache = {};

        this.ajaxUrl = _N2.AjaxHelper.makeAjaxUrl(_N2.AjaxHelper.getAdminUrl('ss3-admin'), {
            nextendcontroller: 'sliders',
            nextendaction: 'list'
        });

        this.modal = new _N2.ModalLightbox('', 'select-slider');

        this.modal.insertButton = _N2.UI.modalButton(n2_('Insert'), 'green').addClass('n2_button--inactive');
        this.modal.addButton(this.modal.insertButton);

        this.modal.insertButton.on('click', (function (e) {
            this.insertSlider(e);
        }).bind(this));

        this.$content = $('<div class="n2_modal_select_slider"></div>');

        this.modal.addContent(this.$content);
    }

    SelectSliderModal.prototype.setSkipGroup = function (skipGroup) {
        this.skipGroup = skipGroup;
    };

    SelectSliderModal.prototype.setTitle = function (title) {
        this.modal.setTitle(title);
    };

    SelectSliderModal.prototype.displayList = function (parentID) {
        if (parentID === undefined) {
            parentID = 0;
        }

        if (this.cache[parentID] === undefined) {
            _N2.AjaxHelper.ajax({
                url: this.ajaxUrl,
                type: 'POST',
                data: {
                    parentID: parentID
                }
            }).done((function (parentID, response) {
                this.cache[parentID] = response.data;
                this.renderResponse(parentID, response.data);
            }).bind(this, parentID));
        } else {
            this.renderResponse(parentID, this.cache[parentID]);
        }
    };

    SelectSliderModal.prototype.renderResponse = function (groupID, sliders) {
        this.$content.html('');
        this.selectedSlider = [0];
        this.modal.insertButton.addClass('n2_button--inactive');

        if (groupID > 0) {
            var $up = $('<div class="n2_slider_box__content"></div>')
                .append($('<div class="n2_modal_select_slider__box n2_slider_box"></div>')
                    .append($('<div class="n2_slider_box__icon n2_slider_box__icon_grey"></div>')
                        .append($('<div class="n2_slider_box__icon_container"><i class="ssi_64 ssi_64--folderup"></i></div>'))
                        .append($('<div class="n2_slider_box__icon_text">' + n2_('Up') + '</div>'))))
                .appendTo(this.$content);

            $up.on('click', this.displayList.bind(this, 0));

            if (!this.skipGroup) {
                var $insertGroup = $('<div class="n2_slider_box__content"></div>')
                    .append($('<div class="n2_modal_select_slider__box n2_slider_box"></div>')
                        .append($('<div class="n2_slider_box__icon n2_slider_box__icon_grey"></div>')
                            .append($('<div class="n2_slider_box__icon_container"><i class="ssi_64 ssi_64--folder"></i></div>'))
                            .append($('<div class="n2_slider_box__icon_text">' + n2_('Insert group') + '</div>'))))
                    .appendTo(this.$content);

                $insertGroup.on('click', (function (e) {
                    this.selectedSlider = [groupID];
                    this.insertSlider(e);
                }).bind(this));
            }
        }

        for (var i = 0; i < sliders.length; i++) {
            var slider = sliders[i];

            var $identifiers = $('<div class="n2_slider_box__slider_identifiers"></div>')
                .append($('<div class="n2_slider_box__slider_identifier"></div>').text('#' + slider.id));

            if (slider.isGroup) {
                $identifiers.append($('<div class="n2_slider_box__slider_identifier"></div>').text(n2_('Group')));
            }

            if (slider.alias) {
                $identifiers.append($('<div class="n2_slider_box__slider_identifier"></div>').text(slider.alias));
            }

            var icon = "ssi_64 ssi_64--image";
            if (slider.isGroup) {
                icon = "ssi_64 ssi_64--folder";
            }

            var $empty = $('<div class="n2_slider_box__icon"><div class="n2_slider_box__icon_container"><i class="' + icon + '"></i></div></div>');

            var $content = $('<div class="n2_slider_box__content"></div>')
                .append($identifiers);

            if (slider.thumbnail === '') {
                $content.append($empty);
            }

            var $footer = $('<div class="n2_slider_box__footer"></div>');

            if (slider.isGroup) {
                $footer.append($('<div class="n2_slider_box__footer_icon"><i class="ssi_16 ssi_16--folderclosed"></i></div>'));
            }

            var $slider = $('<div class="n2_modal_select_slider__box n2_slider_box"></div>')
                .css('background-image', 'url(' + slider.thumbnail + ')')
                .append($content)
                .append($footer
                    .append($('<div class="n2_slider_box__footer_title"></div>').text(slider.title))
                    .append($('<div class="n2_slider_box__footer_children_count"></div>').text(slider.childrenCount)))
                .appendTo(this.$content);

            if (slider.isGroup) {
                $slider
                    .addClass('n2_slider_box--group')
                    .on('click', this.displayList.bind(this, slider.id));
            } else {
                $slider.addClass('n2_slider_box--slider')
                    .on('click', this.select.bind(this, slider.id, slider.alias, groupID));
            }
        }
    };

    SelectSliderModal.prototype.select = function (id, alias, groupID, e) {
        this.$content.find('.n2_slider_box--selected').removeClass('n2_slider_box--selected');
        $(e.currentTarget).addClass('n2_slider_box--selected');
        this.selectedSlider = [id, alias, groupID];

        this.modal.insertButton.removeClass('n2_button--inactive');
    };

    SelectSliderModal.prototype.insertSlider = function (e) {
        var id = this.selectedSlider[0],
            alias = this.selectedSlider[1],
            groupID = this.selectedSlider[2];

        if (id !== 0) {
            this.callback.call(window, id, alias, groupID);
            this.modal.hide(e);
        }
    };

    SelectSliderModal.prototype.show = function (callback) {
        this.callback = callback;
        this.displayList();

        this.modal.show();
    };

    /**
     *
     * @memberOf _N2
     */
    function SelectSlider(title, callback, skipGroup) {

        if (selectSliderModal === false) {
            selectSliderModal = new SelectSliderModal();
        }

        selectSliderModal.setSkipGroup(skipGroup);

        selectSliderModal.setTitle(title);

        selectSliderModal.show(callback);
    }

    return SelectSlider;
});_N2.d('Slider', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param {_N2.ManageSliders} manager
     * @param box
     * @constructor
     */
    function Slider(manager, box) {
        this.selected = false;

        /**
         * @type {_N2.ManageSliders}
         */
        this.manager = manager;

        this.box = box.data('slider', this);

        this.isGroup = !!this.box.data('group');

        this.order = this.box.data('ordering');

        this.editUrl = this.box.find('.n2_slider_box__slider_overlay_edit_button').attr('href');

        this.box.on('contextmenu', this.onContextMenu.bind(this));

        this.box.find('.n2_slider_box__slider_select_tick').on('click', (function (e) {
            e.stopPropagation();
            e.preventDefault();

            this.invertSelection();
        }).bind(this));

        this.$more = this.box.find('.n2_slider_box__slider_action_more')
            .on('click', this.onMore.bind(this));

        this.initRename();
    }

    Slider.prototype.getId = function () {
        return this.box.data('sliderid');
    };

    Slider.prototype.onContextMenu = function (e) {
        e.preventDefault();

        this.showContextMenu(e);
    };

    Slider.prototype.onMore = function (e) {
        e.preventDefault();

        this.showContextMenu(this.$more[0]);
    };

    Slider.prototype.showContextMenu = function (target) {

        var items = [];

        items.push({
            priority: 10,
            label: n2_('Preview'),
            icon: 'ssi_16 ssi_16--eye',
            action: this.preview.bind(this),
        });

        items.push({
            priority: 55,
            label: n2_('Duplicate'),
            icon: 'ssi_16 ssi_16--duplicate',
            action: this.duplicate.bind(this)
        });

        items.push({
            priority: 1000,
            label: n2_('Move to trash'),
            icon: 'ssi_16 ssi_16--delete',
            action: this.trash.bind(this),
            color: 'red'
        });

        this.box.addClass('n2_slider_box--context-menu');
        _N2.ContextMenu(target, items, {
            y: 5,
            horizontalAlign: 'left',
            onClose: (function () {
                this.box.removeClass('n2_slider_box--context-menu');
            }).bind(this)
        });
    };

    Slider.prototype.preview = function () {
        window.open(_N2.N2QueryString.add_query_arg({
            sliderid: this.getId()
        }, this.manager.previewUrl), '_blank');
    };


    Slider.prototype.duplicate = function () {
        var deferred = $.Deferred();
        _N2.AjaxHelper.ajax({
            url: _N2.AjaxHelper.makeAjaxUrl(this.manager.ajaxUrl, {
                nextendcontroller: 'slider',
                nextendaction: 'duplicate',
                sliderid: this.getId()
            })
        }).done((function (response) {
            var box = $(response.data.html).insertAfter(this.box);
            var newSlider = new Slider(this.manager, box);
            this.manager.initSliders();
            deferred.resolve(newSlider);
            if (this.manager.isDashBoard) {
                if (this.manager.isInSearch) {
                    this.manager.refreshSearchLabel();
                } else {
                    this.manager.setSliderCount(response.data.sliderCount);
                    this.manager.refreshPaginationLabel();
                }
            }
        }).bind(this));
        return deferred;
    };

    Slider.prototype.trash = function () {
        this.manager.trashSliders([this.getId()], [this]);
    };

    Slider.prototype.remove = function () {
        this.box.remove();
    };

    Slider.prototype.invertSelection = function (e) {
        if (e) {
            e.preventDefault();
        }

        if (!this.selected) {
            this.select();
        } else {
            this.deSelect();
        }
    };

    Slider.prototype.select = function () {
        if (!this.selected) {
            this.selected = true;
            this.box.addClass('n2_slider_box--bulk-selected');
            this.manager.addSelection(this);
        }
    };

    Slider.prototype.deSelect = function () {
        if (this.selected) {
            this.selected = false;
            this.box.removeClass('n2_slider_box--bulk-selected');
            this.manager.removeSelection(this);
        }
    };

    Slider.prototype.initRename = function () {

        this.dblClickTimeout = false;

        this.$label = this.box.find('.n2_slider_box__footer_title')
            .on('click', (function (e) {
                if (this.$label[0].contentEditable !== 'true') {
                    if (!this.dblClickTimeout) {
                        this.dblClickTimeout = setTimeout((function () {
                            window.location = this.editUrl;
                            this.dblClickTimeout = false;
                        }).bind(this), 500);
                    } else {
                        clearTimeout(this.dblClickTimeout);
                        this.dblClickTimeout = false;
                        this.editName();
                    }
                }
            }).bind(this));

        this.title = this.$label.text().trim();
        this.$label.text(this.title);
    };

    Slider.prototype.editName = function () {

        new _N2.InlineTextEditor(this.$label[0], {
            onSave: (function (value) {
                if (this.title !== value) {
                    _N2.AjaxHelper.ajax({
                        url: _N2.AjaxHelper.makeAjaxUrl(this.manager.ajaxUrl, {
                            nextendcontroller: 'slider',
                            nextendaction: 'rename',
                            sliderid: this.getId(),
                            title: value
                        })
                    })
                        .done((function () {
                            this.title = value;
                        }).bind(this))
                        .fail((function () {
                            this.$label.text(this.title);
                        }).bind(this))
                }
            }).bind(this),
            onCancel: (function () {
                this.$label.text(this.title);
            }).bind(this),
            onTab: (function (e) {
                var $renames = $('.n2_slider_box__footer_title');
                for (var i = 0; i < $renames.length; i++) {
                    if (this.$label.is($renames[i])) {
                        if (e.shiftKey) {
                            i--;
                        } else {
                            i++;
                        }
                        break;
                    }
                }

                if (i < 0) {
                    i = $renames.length - 1;
                } else if (i >= $renames.length) {
                    i = 0;
                }

                $renames.eq(i).trigger('click').trigger('click');

            }).bind(this)
        });
    };

    return Slider;
});_N2.d('SlidersTrash', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     */
    function SlidersTrash(options) {
        this.options = options;

        this.$container = $('.n2_slider_trash');

        var sliders = this.$container.find('.n2_slider_box');
        for (var i = 0; i < sliders.length; i++) {
            new SlidersTrashBox(this, sliders.eq(i));
        }

        this.$emptyTrashButton = $('.n2_slider_empty_trash')
            .on('click', this.emptyTrash.bind(this));

        this.refreshEmptyTrashStatus();
    }

    SlidersTrash.prototype.emptyTrash = function (e) {
        e.preventDefault();

        if (!this.isEmpty()) {
            _N2.ModalDeleteConfirm(n2_('empty the trash'), (function () {
                _N2.AjaxHelper.ajax({
                    url: _N2.AjaxHelper.makeAjaxUrl(this.options.ajaxUrl, {
                        nextendcontroller: 'sliders',
                        nextendaction: 'emptyTrash'
                    }),
                    type: 'POST'
                }).done((function () {
                    this.$container.find('.n2_slider_box').remove();

                    this.refreshEmptyTrashStatus();
                }).bind(this));
            }).bind(this));
        }
    };

    /**
     * @private
     * @returns {boolean}
     */
    SlidersTrash.prototype.isEmpty = function () {
        return this.$container.find('.n2_slider_box').length === 0;
    };

    SlidersTrash.prototype.refreshEmptyTrashStatus = function () {

        var isEmpty = this.isEmpty();

        this.$container.toggleClass('n2_slider_trash--empty', isEmpty);
        this.$emptyTrashButton.toggleClass('n2_button--inactive', isEmpty);
    };


    /**
     *
     * @param {SlidersTrash} manager
     * @param {jQuery} $box
     * @constructor
     */
    function SlidersTrashBox(manager, $box) {

        /**
         * @type {SlidersTrash}
         */
        this.manager = manager;

        /**
         * @type {jQuery}
         */
        this.$box = $box;

        this.$box.on('contextmenu', this.onContextMenu.bind(this));

        this.$box.find('.n2_slider_box__slider_overlay_restore_button')
            .on('click', this.restore.bind(this))

        this.$more = this.$box.find('.n2_slider_box__slider_action_more')
            .on('click', this.onMore.bind(this));
    }

    SlidersTrashBox.prototype.getId = function () {
        return this.$box.data('sliderid');
    };

    SlidersTrashBox.prototype.onContextMenu = function (e) {
        e.preventDefault();

        this.showContextMenu(e);
    };

    SlidersTrashBox.prototype.onMore = function (e) {
        e.preventDefault();

        this.showContextMenu(this.$more[0]);
    };

    SlidersTrashBox.prototype.showContextMenu = function (target) {

        var items = [];

        items.push({
            priority: 20,
            label: n2_('Preview'),
            icon: 'ssi_16 ssi_16--eye',
            action: this.preview.bind(this),
        });

        items.push({
            priority: 30,
            label: n2_('Delete permanently'),
            icon: 'ssi_16 ssi_16--delete',
            action: this.deletePermanently.bind(this),
            color: 'red'
        });

        this.$box.addClass('n2_slider_box--context-menu');
        _N2.ContextMenu(target, items, {
            y: 5,
            horizontalAlign: 'left',
            onClose: (function () {
                this.$box.removeClass('n2_slider_box--context-menu');

                this.manager.refreshEmptyTrashStatus();
            }).bind(this)
        });
    };

    SlidersTrashBox.prototype.restore = function (e) {
        e.preventDefault();

        _N2.AjaxHelper.ajax({
            url: _N2.AjaxHelper.makeAjaxUrl(this.manager.options.ajaxUrl, {
                nextendcontroller: 'slider',
                nextendaction: 'restore'
            }),
            type: 'POST',
            data: {
                slider: this.getId()
            }
        }).done((function () {
            this.$box.remove();

            this.manager.refreshEmptyTrashStatus();
        }).bind(this));

    };

    SlidersTrashBox.prototype.preview = function () {
        window.open(_N2.N2QueryString.add_query_arg({
            sliderid: this.getId()
        }, this.manager.options.previewUrl), '_blank');
    };

    SlidersTrashBox.prototype.deletePermanently = function () {

        _N2.ModalDeleteConfirm(n2_('delete this slider'), (function () {
            _N2.AjaxHelper.ajax({
                url: _N2.AjaxHelper.makeAjaxUrl(this.manager.options.ajaxUrl, {
                    nextendcontroller: 'slider',
                    nextendaction: 'deletePermanently'
                }),
                type: 'POST',
                data: {
                    slider: this.getId()
                }
            }).done((function (response) {
                this.$box.remove();

                var deletedSliderIDs = response.data.sliderIDs;

                for (var i = 0; i < deletedSliderIDs.length; i++) {
                    $('.n2_slider_manager__box[data-sliderid="' + deletedSliderIDs[i] + '"]').remove();
                }

                this.manager.refreshEmptyTrashStatus();
            }).bind(this));
        }).bind(this));
    };

    return SlidersTrash;
});_N2.d('FormElementAnimationManager', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param managerIdentifier
     * @constructor
     */
    function FormElementAnimationManager(id, managerIdentifier) {
        this.element = $('#' + id);
        this.parent = this.element.parent();

        this.managerIdentifier = managerIdentifier;

        this.parent.find('.n2_field_chooser__choose, .n2_field_chooser__label')
            .on('click', this.show.bind(this));

        this.parent.find('.n2_field_chooser__clear')
            .on('click', this.clear.bind(this));

        this.$name = this.parent.find('.n2_field_chooser__label');

        this.updateName(this.element.val());

        _N2.FormElement.prototype.constructor.call(this);
    }


    FormElementAnimationManager.prototype = Object.create(_N2.FormElement.prototype);
    FormElementAnimationManager.prototype.constructor = FormElementAnimationManager;


    FormElementAnimationManager.prototype.show = function (e) {
        e.preventDefault();
        _N2['_' + this.managerIdentifier].show(this.element.val(), this.save.bind(this));
    };

    FormElementAnimationManager.prototype.clear = function (e) {
        e.preventDefault();
        e.stopPropagation();
        this.val('');
    };

    FormElementAnimationManager.prototype.save = function (e, value) {
        this.val(value);
    };

    FormElementAnimationManager.prototype.val = function (value) {
        this.element.val(value);
        this.updateName(value);
        this.triggerOutsideChange();
    };

    FormElementAnimationManager.prototype.insideChange = function (value) {
        this.element.val(value);

        this.updateName(value);

        this.triggerInsideChange();
    };

    FormElementAnimationManager.prototype.updateName = function (value) {

        this.parent.toggleClass('n2_field_chooser--empty', value === '');

        var name;
        if (value === '') {
            name = n2_('Disabled');
        } else {
            name = n2_('Enabled');
        }
        this.$name.text(name);
    };

    FormElementAnimationManager.prototype.isEmpty = function (value) {

        return value === '';
    };

    return FormElementAnimationManager;
});_N2.d('FormElementColumns', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @constructor
     */
    function FormElementColumns(id) {
        this.denominators = {
            1: 100,
            2: 100,
            3: 144,
            4: 100,
            5: 100,
            6: 144
        };
        this.element = $('#' + id);
        _N2.FormElement.prototype.constructor.call(this);
        this.$c = $('#' + id).parent();
        this.$container = this.$c.find('.n2_field_columns__content');
        this.containerWidth = 280;
        this.maxWidth = 0;


        new _N2.UISortable(this.$container, {
            axis: 'x',
            items: '.n2_field_columns__column',
            helper: 'clone_hide',
            start: (function (e, ui) {
                this.$container.addClass('n2-sortable-currently-sorted');
                ui.placeholder.css({
                    width: ui.item.width() + 'px',
                    visibility: 'hidden'
                });

                var $columns = this.$container.find('.n2_field_columns__column');
                ui.item.data('index', $columns.index(ui.item));

            }).bind(this),

            stop: (function (e, ui) {
                var $columns = this.$container.find('.n2_field_columns__column');
                var oldIndex = ui.item.data('index'),
                    newIndex = $columns.index(ui.item);
                if (oldIndex != newIndex) {

                    this.currentRow.moveColOrOrder(oldIndex, newIndex);

                    ui.item.data('index', null);
                }
                this.makeResizable();
                this.$container.removeClass('n2-sortable-currently-sorted');
            }).bind(this)
        });

        this.$c.find('.n2_field_columns__add').on({
            click: (function () {
                this.currentRow.createCol();
            }).bind(this)
        });
    }

    FormElementColumns.prototype = Object.create(_N2.FormElement.prototype);
    FormElementColumns.prototype.constructor = FormElementColumns;


    FormElementColumns.prototype.getDenominator = function (i) {
        if (this.denominators[i] === undefined) {
            this.denominators[i] = i * 15;
        }
        return this.denominators[i];
    };

    FormElementColumns.prototype.setRow = function (row) {
        this.currentRow = row;
        this.insideChange(row.getColumnsOrdered());
    };

    FormElementColumns.prototype.setValue = function (newValue) {

    };

    FormElementColumns.prototype.insideChange = function (value) {
        this.start(value);
    };

    FormElementColumns.prototype.activateColumn = function (e) {
        var clickedColIndex = this.$container.find('.n2_field_columns__column').index(e.currentTarget);
        this.currentRow.activateColumn(clickedColIndex, e);
    };

    FormElementColumns.prototype.start = function (value) {
        this.percentages = [];

        var columnWidths = value.split('+');
        for (var i = 0; i < columnWidths.length; i++) {
            this.percentages.push(new Fraction(columnWidths[i]));
        }

        this.refreshMaxWidth();

        this.$container.empty();

        for (var i = 0; i < this.percentages.length; i++) {
            var $column = $('<div class="n2_field_columns__column">')
                .on('click', this.activateColumn.bind(this))
                .appendTo(this.$container);

            _N2._tooltip.addElement($column, n2_('Column'));

            this.updateColumn($column, this.percentages[i]);
        }

        this.makeResizable();

    };

    FormElementColumns.prototype.refreshMaxWidth = function () {
        this.maxWidth = this.containerWidth - (this.percentages.length - 1) * 16;
    };

    FormElementColumns.prototype.updateColumn = function ($col, fraction) {
        $col.css('width', (this.maxWidth * fraction.valueOf()) + 'px')
            .html(Math.round(fraction.valueOf() * 100 * 10) / 10 + '%');
    };

    FormElementColumns.prototype.makeResizable = function () {
        if (this.handles) {
            this.handles.remove();
        }
        this.$columns = this.$container.find('.n2_field_columns__column');
        $('<div class="n2_field_columns__handle"><div class="ssi_16 ssi_16--divide"></div></div>').insertAfter(this.$columns.not(this.$columns.last()));

        this.handles = this.$container.find('.n2_field_columns__handle')
            .on('mousedown', this._resizeStart.bind(this));
    };

    FormElementColumns.prototype._resizeStart = function (e) {
        var index = this.handles.index(e.currentTarget),
            cLeft = this.$container.offset().left + 8;

        this.resizeContext = {
            index: index,
            cLeft: cLeft,
            $currentCol: this.$columns.eq(index),
            $nextCol: this.$columns.eq(index + 1),
            startX: Math.max(0, Math.min(e.clientX - cLeft, this.containerWidth)),
        };

        this._resizeMove(e);

        $('html').off('.resizecol').on({
            'mousemove.resizecol': this._resizeMove.bind(this),
            'mouseup.resizecol mouseleave.resizecol': this._resizeStop.bind(this)
        });
    };

    FormElementColumns.prototype._resizeMove = function (e) {
        e.preventDefault();
        var currentX = Math.max(0, Math.min(e.clientX - this.resizeContext.cLeft, this.containerWidth)),
            currentDenominator = this.getDenominator(this.percentages.length),
            fractionDifference = new Fraction(Math.round((currentX - this.resizeContext.startX) / (this.maxWidth / currentDenominator)), currentDenominator);
        if (fractionDifference.compare(this.percentages[this.resizeContext.index].clone().mul(-1)) < 0) {
            fractionDifference = this.percentages[this.resizeContext.index].clone().mul(-1);
        }
        if (fractionDifference.compare(this.percentages[this.resizeContext.index + 1]) > 0) {
            fractionDifference = this.percentages[this.resizeContext.index + 1].clone();
        }
        var currentP = this.percentages[this.resizeContext.index].add(fractionDifference),
            nextP = this.percentages[this.resizeContext.index + 1].sub(fractionDifference);

        this.updateColumn(this.resizeContext.$currentCol, currentP);
        this.updateColumn(this.resizeContext.$nextCol, nextP);

        var _percentages = _Assign([], this.percentages);
        _percentages[this.resizeContext.index] = currentP;
        _percentages[this.resizeContext.index + 1] = nextP;

        this.onColumnWidthChange(_percentages);

        return [currentP, nextP];
    };

    FormElementColumns.prototype._resizeStop = function (e) {
        var ret = this._resizeMove(e);
        this.percentages[this.resizeContext.index] = ret[0];
        this.percentages[this.resizeContext.index + 1] = ret[1];
        $('html').off('.resizecol');
        delete this.resizeContext;

        this.currentRow.setRealColsWidth(this.percentages);
    };

    FormElementColumns.prototype.onColumnWidthChange = function (_percentages) {
        var percentages = [];
        for (var i = 0; i < _percentages.length; i++) {
            percentages.push(_percentages[i].valueOf());
        }
        this.currentRow.updateColumnWidth(percentages);
    };

    return FormElementColumns;
});
_N2.d('FormElementControlTypePicker', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param options
     * @constructor
     */
    function FormElementControlTypePicker(id, options) {

        this.options = _Assign({
            ajaxUrl: '',
            target: '',
            originalValue: ''
        }, options);

        this.id = id;

        this.element = $('#' + id);

        var $itemsContainer = this.element.parent();
        this.$items = $itemsContainer.find('.n2_field_control_type_picker__item');

        this.target = $('#' + this.options.target);

        this.form = this.element.closest('form');

        this.element.on('nextendChange', this.loadSubform.bind(this));


        this.$items.on('click', (function (e) {
            this.selectOption($(e.currentTarget).data('controltype'));
        }).bind(this));

        this.markSelected(this.options.originalValue);

        _N2.FormElement.prototype.constructor.call(this);
    }


    FormElementControlTypePicker.prototype = Object.create(_N2.FormElement.prototype);
    FormElementControlTypePicker.prototype.constructor = FormElementControlTypePicker;

    FormElementControlTypePicker.prototype.loadSubform = function () {
        var value = this.element.val();

        var values = [];
        if (value === this.options.originalValue) {
            values = this.form.data('form').values;
        }

        var data = {
            values: values,
            value: value
        };

        _N2.AjaxHelper.ajax({
            type: "POST",
            url: _N2.AjaxHelper.makeAjaxUrl(this.options.ajaxUrl),
            data: data,
            dataType: 'json'
        }).done(this.load.bind(this));
    };

    FormElementControlTypePicker.prototype.load = function (response) {
        this.target.html(response.data.html);
        eval(response.data.scripts);

        _N2.FormTipManager.add(this.target);
    };

    FormElementControlTypePicker.prototype.selectOption = function (value) {
        this.setValue(value);

        this.markSelected(value);

        this.triggerOutsideChange();
    };

    FormElementControlTypePicker.prototype.setValue = function (value) {
        this.element.val(value);
    };

    FormElementControlTypePicker.prototype.markSelected = function (value) {

        //remove selected class
        this.$items.removeClass('n2_field_control_type_picker__item--selected');

        for (var i = 0; i < this.$items.length; i++) {
            //add selected class and marker
            if (this.$items.eq(i).data('controltype') === value) {
                var selected = $(this.$items[i]);
                selected.addClass('n2_field_control_type_picker__item--selected');
            }
        }
    };

    return FormElementControlTypePicker;
});
_N2.d('FormElementDatePicker', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     *
     * @param id
     * @param elements
     * @param hasOnOff
     * @constructor
     *
     * @augments _N2.FormElement
     */
    function FormElementDatePicker(id, elements, hasOnOff) {

        this.element = $('#' + id);

        this.hasOnOff = hasOnOff;

        this.elements = [];

        let i = 0;
        if (hasOnOff) {
            this.elements.push($('#' + elements[0])
                .on('outsideChange', this.onFieldOnOffChange.bind(this)));
            i++;
        }
        for (; i < elements.length; i++) {
            this.elements.push($('#' + elements[i])
                .on('outsideChange', this.onFieldChange.bind(this)));
        }

        _N2.FormElement.prototype.constructor.call(this);

        this.syncOnOff();

        this.fixDate();

    }

    FormElementDatePicker.prototype = Object.create(_N2.FormElement.prototype);
    FormElementDatePicker.prototype.constructor = FormElementDatePicker;

    FormElementDatePicker.prototype.getValue = function () {

        var start = 0;
        if (this.hasOnOff && this.isOff) {
            start = 1;
        }

        if (!this.isOff || parseInt(this.elements[0].val()) === 1) {
            var date = [this.elements[start].val(), this.pad(this.elements[start + 1].val()), this.pad(this.elements[start + 2].val())],
                time = [this.pad(this.elements[start + 3].val()), this.pad(this.elements[start + 4].val()), '00'];

            return date.join('-') + ' ' + time.join(':');
        }

        return this.getEmptyDate();
    };

    FormElementDatePicker.prototype.onFieldOnOffChange = function () {

        if (parseInt(this.elements[0].val()) === 1 && this.elements[1].val() === '0000') {
            /**
             * There is no date set in the fields, so we set the current date
             */
            var currentDate = new Date(),
                date = [currentDate.getFullYear(), this.pad(currentDate.getMonth() + 1), this.pad(currentDate.getDate())],
                time = [this.pad(currentDate.getHours()), this.pad(currentDate.getMinutes()), '00'];

            this.insideChange(date.join('-') + ' ' + time.join(':'));
        }

        this.onFieldChange();
    };

    FormElementDatePicker.prototype.onFieldChange = function () {

        if (!this.fixDate()) {
            this.element.val(this.getValue());
            this.triggerOutsideChange();
        }
    };

    FormElementDatePicker.prototype.insideChange = function (value) {

        this.element.val(value);

        if (this.hasOnOff && this.isOff && value === this.getEmptyDate()) {
            //we need to reset the OnOff switch and the Hidden field
            this.elements[0].data('field').insideChange(0);
        } else {

            var values = value.split(' '),
                date = values[0].split('-'),
                time = values[1].split(':'),
                start = 0;

            if (this.hasOnOff && this.isOff) {
                this.elements[0].data('field').insideChange(1);
                start = 1;
            }

            this.elements[start].data('field').insideChange(date[0]);
            this.elements[start + 1].data('field').insideChange(date[1]);
            this.elements[start + 2].data('field').insideChange(date[2]);
            this.elements[start + 3].data('field').insideChange(time[0]);
            this.elements[start + 4].data('field').insideChange(time[1]);

            this.fixDate();
        }

        this.triggerInsideChange();
    };

    /**
     * @private
     */
    FormElementDatePicker.prototype.syncOnOff = function () {
        if (this.element.val() === this.getEmptyDate()) {
            this.isOff = true;
            this.elements[0].data('field').insideChange(0);
        } else {
            this.isOff = false;
            this.elements[0].data('field').insideChange(1);
        }
    };

    /**
     * @private
     * @returns {string} 0000-00-00 00:00:00
     */
    FormElementDatePicker.prototype.getEmptyDate = function () {
        return '0000-00-00 00:00:00';
    };

    /**
     * @private
     * @param {int} num
     * @param {int} [size=2] size
     * @returns {string}
     */
    FormElementDatePicker.prototype.pad = function (num, size) {
        size = size || 2;

        var s = num + "";
        while (s.length < size) s = "0" + s;
        return s;
    };

    /**
     * @private
     * @returns {boolean} is date changed
     */
    FormElementDatePicker.prototype.fixDate = function () {

        if (parseInt(this.elements[0].val()) === 1) {
            var selectedYear = this.elements[1].val(),
                selectedMonth = this.elements[2].val(),
                selectedDay = this.elements[3].val(),
                $daySelect = this.elements[3].data('field').select,
                dayNumOfMonth = new Date(selectedYear, selectedMonth, 0).getDate(),
                totalDays = $daySelect[0].length;
            //Show days
            for (var i = totalDays; i > 0; i--) {
                $daySelect[0][i - 1].removeAttribute('hidden');
            }

            //Hide days
            for (var j = totalDays; j > dayNumOfMonth; j--) {
                $daySelect[0][j - 1].setAttribute('hidden', 'hidden');
            }

            if (selectedDay > dayNumOfMonth) {
                this.elements[3].data('field').insideChange(dayNumOfMonth);
                this.onFieldChange();

                return true;
            }
        }

        return false;
    };

    return FormElementDatePicker;
});
/**
 * @license Fraction.js v3.3.1 09/09/2015
 * http://www.xarg.org/2014/03/rational-numbers-in-javascript/
 *
 * Copyright (c) 2015, Robert Eisele (robert@xarg.org)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 **/


/**
 *
 * This class offers the possibility to calculate fractions.
 * You can pass a fraction in different formats. Either as array, as double, as string or as an integer.
 *
 * Array/Object form
 * [ 0 => <nominator>, 1 => <denominator> ]
 * [ n => <nominator>, d => <denominator> ]
 *
 * Integer form
 * - Single integer value
 *
 * Double form
 * - Single double value
 *
 * String form
 * 123.456 - a simple double
 * 123/456 - a string fraction
 * 123.'456' - a double with repeating decimal places
 * 123.(456) - synonym
 * 123.45'6' - a double with repeating last place
 * 123.45(6) - synonym
 *
 * Example:
 *
 * var f = new Fraction("9.4'31'");
 * f.mul([-4, 3]).div(4.9);
 *
 */

(function (root) {

    "use strict";

    // Maximum search depth for cyclic rational numbers. 2000 should be more than enough.
    // Example: 1/7 = 0.(142857) has 6 repeating decimal places.
    // If MAX_CYCLE_LEN gets reduced, long cycles will not be detected and toString() only gets the first 10 digits
    var MAX_CYCLE_LEN = 2000;

    // Parsed data to avoid calling "new" all the time
    var P = {
        "s": 1,
        "n": 0,
        "d": 1
    };

    function assign(n, s) {

        if (isNaN(n = parseInt(n, 10))) {
            throwInvalidParam();
        }
        return n * s;
    }

    function throwInvalidParam() {
        throw "Invalid Param";
    }

    var parse = function (p1, p2) {

        var n = 0, d = 1, s = 1;
        var v = 0, w = 0, x = 0, y = 1, z = 1;

        var A = 0, B = 1;
        var C = 1, D = 1;

        var N = 10000000;
        var M;

        if (p1 === undefined || p1 === null) {
            /* void */
        } else if (p2 !== undefined) {
            n = p1;
            d = p2;
            s = n * d;
        } else
            switch (typeof p1) {

                case "object": {
                    if ("d" in p1 && "n" in p1) {
                        n = p1["n"];
                        d = p1["d"];
                        if ("s" in p1)
                            n *= p1["s"];
                    } else if (0 in p1) {
                        n = p1[0];
                        if (1 in p1)
                            d = p1[1];
                    } else {
                        throwInvalidParam();
                    }
                    s = n * d;
                    break;
                }
                case "number": {
                    if (p1 < 0) {
                        s = p1;
                        p1 = -p1;
                    }

                    if (p1 % 1 === 0) {
                        n = p1;
                    } else if (p1 > 0) { // check for != 0, scale would become NaN (log(0)), which converges really slow

                        if (p1 >= 1) {
                            z = Math.pow(10, Math.floor(1 + Math.log(p1) / Math.LN10));
                            p1 /= z;
                        }

                        // Using Farey Sequences
                        // http://www.johndcook.com/blog/2010/10/20/best-rational-approximation/

                        while (B <= N && D <= N) {
                            M = (A + C) / (B + D);

                            if (p1 === M) {
                                if (B + D <= N) {
                                    n = A + C;
                                    d = B + D;
                                } else if (D > B) {
                                    n = C;
                                    d = D;
                                } else {
                                    n = A;
                                    d = B;
                                }
                                break;

                            } else {

                                if (p1 > M) {
                                    A += C;
                                    B += D;
                                } else {
                                    C += A;
                                    D += B;
                                }

                                if (B > N) {
                                    n = C;
                                    d = D;
                                } else {
                                    n = A;
                                    d = B;
                                }
                            }
                        }
                        n *= z;
                    } else if (isNaN(p1) || isNaN(p2)) {
                        d = n = NaN;
                    }
                    break;
                }
                case "string": {
                    B = p1.match(/\d+|./g);

                    if (B[A] === '-') {// Check for minus sign at the beginning
                        s = -1;
                        A++;
                    } else if (B[A] === '+') {// Check for plus sign at the beginning
                        A++;
                    }

                    if (B.length === A + 1) { // Check if it's just a simple number "1234"
                        w = assign(B[A++], s);
                    } else if (B[A + 1] === '.' || B[A] === '.') { // Check if it's a decimal number

                        if (B[A] !== '.') { // Handle 0.5 and .5
                            v = assign(B[A++], s);
                        }
                        A++;

                        // Check for decimal places
                        if (A + 1 === B.length || B[A + 1] === '(' && B[A + 3] === ')' || B[A + 1] === "'" && B[A + 3] === "'") {
                            w = assign(B[A], s);
                            y = Math.pow(10, B[A].length);
                            A++;
                        }

                        // Check for repeating places
                        if (B[A] === '(' && B[A + 2] === ')' || B[A] === "'" && B[A + 2] === "'") {
                            x = assign(B[A + 1], s);
                            z = Math.pow(10, B[A + 1].length) - 1;
                            A += 3;
                        }

                    } else if (B[A + 1] === '/' || B[A + 1] === ':') { // Check for a simple fraction "123/456" or "123:456"
                        w = assign(B[A], s);
                        y = assign(B[A + 2], 1);
                        A += 3;
                    } else if (B[A + 3] === '/' && B[A + 1] === ' ') { // Check for a complex fraction "123 1/2"
                        v = assign(B[A], s);
                        w = assign(B[A + 2], s);
                        y = assign(B[A + 4], 1);
                        A += 5;
                    }

                    if (B.length <= A) { // Check for more tokens on the stack
                        d = y * z;
                        s = /* void */
                            n = x + d * v + z * w;
                        break;
                    }

                    /* Fall through on error */
                }
                default:
                    throwInvalidParam();
            }

        if (d === 0) {
            throw "DIV/0";
        }

        P["s"] = s < 0 ? -1 : 1;
        P["n"] = Math.abs(n);
        P["d"] = Math.abs(d);
    };

    var modpow = function (b, e, m) {

        for (var r = 1; e > 0; b = (b * b) % m, e >>= 1) {

            if (e & 1) {
                r = (r * b) % m;
            }
        }
        return r;
    };

    var cycleLen = function (n, d) {

        for (; d % 2 === 0;
               d /= 2) {
        }

        for (; d % 5 === 0;
               d /= 5) {
        }

        if (d === 1) // Catch non-cyclic numbers
            return 0;

        // If we would like to compute really large numbers quicker, we could make use of Fermat's little theorem:
        // 10^(d-1) % d == 1
        // However, we don't need such large numbers and MAX_CYCLE_LEN should be the capstone,
        // as we want to translate the numbers to strings.

        var rem = 10 % d;

        for (var t = 1; rem !== 1; t++) {
            rem = rem * 10 % d;

            if (t > MAX_CYCLE_LEN)
                return 0; // Returning 0 here means that we don't print it as a cyclic number. It's likely that the answer is `d-1`
        }
        return t;
    };

    var cycleStart = function (n, d, len) {

        var rem1 = 1;
        var rem2 = modpow(10, len, d);

        for (var t = 0; t < 300; t++) { // s < ~log10(Number.MAX_VALUE)
            // Solve 10^s == 10^(s+t) (mod d)

            if (rem1 === rem2)
                return t;

            rem1 = rem1 * 10 % d;
            rem2 = rem2 * 10 % d;
        }
        return 0;
    };

    var gcd = function (a, b) {

        if (!a) return b;
        if (!b) return a;

        while (1) {
            a %= b;
            if (!a) return b;
            b %= a;
            if (!b) return a;
        }
    };

    /**
     * Module constructor
     *
     * @constructor
     * @param {number|Fraction} a
     * @param {number=} b
     */
    function Fraction(a, b) {

        if (!(this instanceof Fraction)) {
            return new Fraction(a, b);
        }

        parse(a, b);

        if (Fraction['REDUCE']) {
            a = gcd(P["d"], P["n"]); // Abuse a
        } else {
            a = 1;
        }

        this["s"] = P["s"];
        this["n"] = P["n"] / a;
        this["d"] = P["d"] / a;
    }

    /**
     * Boolean global variable to be able to disable automatic reduction of the fraction
     *
     */
    Fraction['REDUCE'] = 1;

    Fraction.prototype = {

        "s": 1,
        "n": 0,
        "d": 1,

        /**
         * Calculates the absolute value
         *
         * Ex: new Fraction(-4).abs() => 4
         **/
        "abs": function () {

            return new Fraction(this["n"], this["d"]);
        },

        /**
         * Inverts the sign of the current fraction
         *
         * Ex: new Fraction(-4).neg() => 4
         **/
        "neg": function () {

            return new Fraction(-this["s"] * this["n"], this["d"]);
        },

        /**
         * Adds two rational numbers
         *
         * Ex: new Fraction({n: 2, d: 3}).add("14.9") => 467 / 30
         **/
        "add": function (a, b) {

            parse(a, b);
            return new Fraction(
                this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"],
                this["d"] * P["d"]
            );
        },

        /**
         * Subtracts two rational numbers
         *
         * Ex: new Fraction({n: 2, d: 3}).add("14.9") => -427 / 30
         **/
        "sub": function (a, b) {

            parse(a, b);
            return new Fraction(
                this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"],
                this["d"] * P["d"]
            );
        },

        /**
         * Multiplies two rational numbers
         *
         * Ex: new Fraction("-17.(345)").mul(3) => 5776 / 111
         **/
        "mul": function (a, b) {

            parse(a, b);
            return new Fraction(
                this["s"] * P["s"] * this["n"] * P["n"],
                this["d"] * P["d"]
            );
        },

        /**
         * Divides two rational numbers
         *
         * Ex: new Fraction("-17.(345)").inverse().div(3)
         **/
        "div": function (a, b) {

            parse(a, b);
            return new Fraction(
                this["s"] * P["s"] * this["n"] * P["d"],
                this["d"] * P["n"]
            );
        },

        /**
         * Clones the actual object
         *
         * Ex: new Fraction("-17.(345)").clone()
         **/
        "clone": function () {
            return new Fraction(this);
        },

        /**
         * Calculates the modulo of two rational numbers - a more precise fmod
         *
         * Ex: new Fraction('4.(3)').mod([7, 8]) => (13/3) % (7/8) = (5/6)
         **/
        "mod": function (a, b) {

            if (isNaN(this['n']) || isNaN(this['d'])) {
                return new Fraction(NaN);
            }

            if (a === undefined) {
                return new Fraction(this["s"] * this["n"] % this["d"], 1);
            }

            parse(a, b);
            if (0 === P["n"] && 0 === this["d"]) {
                Fraction(0, 0); // Throw div/0
            }

            /*
             * First silly attempt, kinda slow
             *
             return that["sub"]({
             "n": num["n"] * Math.floor((this.n / this.d) / (num.n / num.d)),
             "d": num["d"],
             "s": this["s"]
             });*/

            /*
             * New attempt: a1 / b1 = a2 / b2 * q + r
             * => b2 * a1 = a2 * b1 * q + b1 * b2 * r
             * => (b2 * a1 % a2 * b1) / (b1 * b2)
             */
            return new Fraction(
                (this["s"] * P["d"] * this["n"]) % (P["n"] * this["d"]),
                P["d"] * this["d"]
            );
        },

        /**
         * Calculates the fractional gcd of two rational numbers
         *
         * Ex: new Fraction(5,8).gcd(3,7) => 1/56
         */
        "gcd": function (a, b) {

            parse(a, b);

            // gcd(a / b, c / d) = gcd(a, c) / lcm(b, d)

            return new Fraction(gcd(P["n"], this["n"]), P["d"] * this["d"] / gcd(P["d"], this["d"]));
        },

        /**
         * Calculates the fractional lcm of two rational numbers
         *
         * Ex: new Fraction(5,8).lcm(3,7) => 15
         */
        "lcm": function (a, b) {

            parse(a, b);

            // lcm(a / b, c / d) = lcm(a, c) / gcd(b, d)

            if (P["n"] === 0 && this["n"] === 0) {
                return new Fraction;
            }
            return new Fraction(P["n"] * this["n"] / gcd(P["n"], this["n"]), gcd(P["d"], this["d"]));
        },

        /**
         * Calculates the ceil of a rational number
         *
         * Ex: new Fraction('4.(3)').ceil() => (5 / 1)
         **/
        "ceil": function (places) {

            places = Math.pow(10, places || 0);

            if (isNaN(this["n"]) || isNaN(this["d"])) {
                return new Fraction(NaN);
            }
            return new Fraction(Math.ceil(places * this["s"] * this["n"] / this["d"]), places);
        },

        /**
         * Calculates the floor of a rational number
         *
         * Ex: new Fraction('4.(3)').floor() => (4 / 1)
         **/
        "floor": function (places) {

            places = Math.pow(10, places || 0);

            if (isNaN(this["n"]) || isNaN(this["d"])) {
                return new Fraction(NaN);
            }
            return new Fraction(Math.floor(places * this["s"] * this["n"] / this["d"]), places);
        },

        /**
         * Rounds a rational numbers
         *
         * Ex: new Fraction('4.(3)').round() => (4 / 1)
         **/
        "round": function (places) {

            places = Math.pow(10, places || 0);

            if (isNaN(this["n"]) || isNaN(this["d"])) {
                return new Fraction(NaN);
            }
            return new Fraction(Math.round(places * this["s"] * this["n"] / this["d"]), places);
        },

        /**
         * Gets the inverse of the fraction, means numerator and denumerator are exchanged
         *
         * Ex: new Fraction([-3, 4]).inverse() => -4 / 3
         **/
        "inverse": function () {

            return new Fraction(this["s"] * this["d"], this["n"]);
        },

        /**
         * Calculates the fraction to some integer exponent
         *
         * Ex: new Fraction(-1,2).pow(-3) => -8
         */
        "pow": function (m) {

            if (m < 0) {
                return new Fraction(Math.pow(this['s'] * this["d"], -m), Math.pow(this["n"], -m));
            } else {
                return new Fraction(Math.pow(this['s'] * this["n"], m), Math.pow(this["d"], m));
            }
        },

        /**
         * Check if two rational numbers are the same
         *
         * Ex: new Fraction(19.6).equals([98, 5]);
         **/
        "equals": function (a, b) {

            parse(a, b);
            return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"]; // Same as compare() === 0
        },

        /**
         * Check if two rational numbers are the same
         *
         * Ex: new Fraction(19.6).equals([98, 5]);
         **/
        "compare": function (a, b) {

            parse(a, b);
            var t = (this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"]);
            return (0 < t) - (t < 0);
        },

        /**
         * Check if two rational numbers are divisible
         *
         * Ex: new Fraction(19.6).divisible(1.5);
         */
        "divisible": function (a, b) {

            parse(a, b);
            return !(!(P["n"] * this["d"]) || ((this["n"] * P["d"]) % (P["n"] * this["d"])));
        },

        /**
         * Returns a decimal representation of the fraction
         *
         * Ex: new Fraction("100.'91823'").valueOf() => 100.91823918239183
         **/
        'valueOf': function () {

            return this["s"] * this["n"] / this["d"];
        },

        /**
         * Returns a string-fraction representation of a Fraction object
         *
         * Ex: new Fraction("1.'3'").toFraction() => "4 1/3"
         **/
        'toFraction': function (excludeWhole) {

            var whole, str = "";
            var n = this["n"];
            var d = this["d"];
            if (this["s"] < 0) {
                str += '-';
            }

            if (d === 1) {
                str += n;
            } else {

                if (excludeWhole && (whole = Math.floor(n / d)) > 0) {
                    str += whole;
                    str += " ";
                    n %= d;
                }

                str += n;
                str += '/';
                str += d;
            }
            return str;
        },

        /**
         * Returns a latex representation of a Fraction object
         *
         * Ex: new Fraction("1.'3'").toLatex() => "\frac{4}{3}"
         **/
        'toLatex': function (excludeWhole) {

            var whole, str = "";
            var n = this["n"];
            var d = this["d"];
            if (this["s"] < 0) {
                str += '-';
            }

            if (d === 1) {
                str += n;
            } else {

                if (excludeWhole && (whole = Math.floor(n / d)) > 0) {
                    str += whole;
                    n %= d;
                }

                str += "\\frac{";
                str += n;
                str += '}{';
                str += d;
                str += '}';
            }
            return str;
        },

        /**
         * Returns an array of continued fraction elements
         *
         * Ex: new Fraction("7/8").toContinued() => [0,1,7]
         */
        'toContinued': function () {

            var t;
            var a = this['n'];
            var b = this['d'];
            var res = [];

            do {
                res.push(Math.floor(a / b));
                t = a % b;
                a = b;
                b = t;
            } while (a !== 1);

            return res;
        },

        /**
         * Creates a string representation of a fraction with all digits
         *
         * Ex: new Fraction("100.'91823'").toString() => "100.(91823)"
         **/
        'toString': function () {

            var g;
            var N = this["n"];
            var D = this["d"];

            if (isNaN(N) || isNaN(D)) {
                return "NaN";
            }

            if (!Fraction['REDUCE']) {
                g = gcd(N, D);
                N /= g;
                D /= g;
            }

            var p = String(N).split(""); // Numerator chars
            var t = 0; // Tmp var

            var ret = [~this["s"] ? "" : "-", "", ""]; // Return array, [0] is zero sign, [1] before comma, [2] after
            var zeros = ""; // Collection variable for zeros

            var cycLen = cycleLen(N, D); // Cycle length
            var cycOff = cycleStart(N, D, cycLen); // Cycle start

            var j = -1;
            var n = 1; // str index

            // rough estimate to fill zeros
            var length = 15 + cycLen + cycOff + p.length; // 15 = decimal places when no repitation

            for (var i = 0; i < length; i++, t *= 10) {

                if (i < p.length) {
                    t += Number(p[i]);
                } else {
                    n = 2;
                    j++; // Start now => after comma
                }

                if (cycLen > 0) { // If we have a repeating part
                    if (j === cycOff) {
                        ret[n] += zeros + "(";
                        zeros = "";
                    } else if (j === cycLen + cycOff) {
                        ret[n] += zeros + ")";
                        break;
                    }
                }

                if (t >= D) {
                    ret[n] += zeros + ((t / D) | 0); // Flush zeros, Add current digit
                    zeros = "";
                    t = t % D;
                } else if (n > 1) { // Add zeros to the zero buffer
                    zeros += "0";
                } else if (ret[n]) { // If before comma, add zero only if already something was added
                    ret[n] += "0";
                }
            }

            // If it's empty, it's a leading zero only
            ret[0] += ret[1] || "0";

            // If there is something after the comma, add the comma sign
            if (ret[2]) {
                return ret[0] + "." + ret[2];
            }
            return ret[0];
        }
    };
    root['Fraction'] = Fraction;

})(window);_N2.d('FormElementSliderWidgetArea', ['$', 'FormElement'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @constructor
     */
    function FormElementSliderWidgetArea(id) {
        this.element = $('#' + id);

        this.area = this.element.parent();

        this.areas = this.area.find('.n2_field_widget_area__area');

        this.areas.on('click', this.chooseArea.bind(this));

        _N2.FormElement.prototype.constructor.call(this);
    }


    FormElementSliderWidgetArea.prototype = Object.create(_N2.FormElement.prototype);
    FormElementSliderWidgetArea.prototype.constructor = FormElementSliderWidgetArea;


    FormElementSliderWidgetArea.prototype.chooseArea = function (e) {
        var value = parseInt($(e.target).data('area'));

        this.element.val(value);
        this.setSelected(value);

        this.triggerOutsideChange();
    };

    FormElementSliderWidgetArea.prototype.insideChange = function (value) {
        value = parseInt(value);
        this.element.val(value);
        this.setSelected(value);

        this.triggerInsideChange();
    };

    FormElementSliderWidgetArea.prototype.setSelected = function (index) {
        this.areas.removeClass('n2_field_widget_area__area--selected');
        this.areas.eq(index - 1).addClass('n2_field_widget_area__area--selected');
    };

    return FormElementSliderWidgetArea;
});
_N2.d('FormElementWidgetPosition', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @constructor
     */
    function FormElementWidgetPosition(id) {

        this.id = id;

        this.$element = $('#' + id);

        this.$label = this.$element.find('.n2_field_widget_position__label');

        this.$positionElement = $('#' + id + '-area')
            .on('nextendChange', this.onPositionChange.bind(this));
        this.positionValue = this.$positionElement.val();

        this.$offsetElement = $('#' + id + '-offset')
            .on('nextendChange', this.onOffsetChange.bind(this));
        this.offsetValue = this.$offsetElement.val();

        this.$positionMode = $('#' + id + '-mode')
            .on('nextendChange', this.onModeChange.bind(this));
        this.modeValue = this.$positionMode.val();

        this.refreshLabel();

        this.$element.on('click', this.toggle.bind(this));
        this.$element.find('.n2_field_widget_position__popover').on('click', this.stopPropagation.bind(this));
    }

    FormElementWidgetPosition.prototype.stopPropagation = function (e) {
        if (e.originalEvent['handled' + this.id] === undefined) {
            e.originalEvent['handled' + this.id] = true;

            return false;
        }

        return true;
    };

    FormElementWidgetPosition.prototype.toggle = function (e) {
        if (this.$element.hasClass('n2_field_widget_position--focus')) {
            this.blur(e);
        } else {
            this.focus(e);
        }
    };

    FormElementWidgetPosition.prototype.focus = function (e) {
        if (!this.stopPropagation(e)) {
            this.$element.addClass('n2_field_widget_position--focus');
            $('body').on('click.' + this.id, this.blur.bind(this));
        }
    };

    FormElementWidgetPosition.prototype.blur = function (e) {
        if (!this.stopPropagation(e)) {
            this.$element.removeClass('n2_field_widget_position--focus');
            $('body').off('click.' + this.id);
        }
    };

    FormElementWidgetPosition.prototype.refreshLabel = function () {
        var text = '';

        if (this.modeValue === 'advanced') {
            text = n2_('Advanced');
        } else {
            switch (parseInt(this.positionValue)) {
                case 1:
                    text = n2_sprintf(n2_('Outer %s'), n2_('Top'));
                    break;
                case 2:
                    text = n2_('Top') + ' ' + n2_('Left');
                    break;
                case 3:
                    text = n2_('Top') + ' ' + n2_('Center');
                    break;
                case 4:
                    text = n2_('Top') + ' ' + n2_('Right');
                    break;
                case 5:
                    text = n2_sprintf(n2_('Outer %s'), n2_('Left'));
                    break;
                case 6:
                    text = n2_('Middle') + ' ' + n2_('Left');
                    break;
                case 7:
                    text = n2_('Middle') + ' ' + n2_('Right');
                    break;
                case 8:
                    text = n2_sprintf(n2_('Outer %s'), n2_('Right'));
                    break;
                case 9:
                    text = n2_('Bottom') + ' ' + n2_('Left');
                    break;
                case 10:
                    text = n2_('Bottom') + ' ' + n2_('Center');
                    break;
                case 11:
                    text = n2_('Bottom') + ' ' + n2_('Right');
                    break;
                case 12:
                    text = n2_sprintf(n2_('Outer %s'), n2_('Bottom'));
                    break;
            }

            if (parseInt(this.offsetValue) !== 0) {
                text += ' + ' + this.offsetValue + 'px';
            }
        }

        this.$label.text(text);
    };

    FormElementWidgetPosition.prototype.onPositionChange = function () {
        this.positionValue = this.$positionElement.val();
        this.refreshLabel();
    };

    FormElementWidgetPosition.prototype.onOffsetChange = function () {
        this.offsetValue = this.$offsetElement.val();
        this.refreshLabel();
    };

    FormElementWidgetPosition.prototype.onModeChange = function () {
        this.modeValue = this.$positionMode.val();
        this.refreshLabel();
    };

    return FormElementWidgetPosition;
});
_N2.d('GeneratorAdd', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     * @augments {_N2.Form}
     */
    function GeneratorAdd(options) {

        _N2.Form.prototype.constructor.call(this);
        _N2.r('documentReady', this.onReady.bind(this));
    }

    GeneratorAdd.prototype = Object.create(_N2.Form.prototype);
    GeneratorAdd.prototype.constructor = GeneratorAdd;

    GeneratorAdd.prototype.onReady = function () {

        this.setID('#n2-ss-form-generator-add');

        _N2.Form.prototype.onReady.call(this);

        this.$form.on('submit', this.onSubmit.bind(this));
    };

    GeneratorAdd.prototype.resetChangeTracker = function () {

    };

    GeneratorAdd.prototype.initButtons = function () {
        this.$saveButton = $('.n2_generator_add')
            .on('click', this.actionSave.bind(this));

        $('.n2_generator_add_cancel')
            .on('click', this.actionCancel.bind(this));
    };

    GeneratorAdd.prototype.actionSave = function (e) {
        e && e.preventDefault();

        this.skipChangeConfirm = true;

        this.$form.trigger("submit");

        this.afterSave();
    };

    GeneratorAdd.prototype.actionCancel = function (e) {
        e && e.preventDefault();

        window.location = e.currentTarget.href;
    };

    GeneratorAdd.prototype.onSubmit = function (e) {
        e.preventDefault();

        _N2.AjaxHelper.ajax({
            type: "POST",
            url: this.$form.attr('action'),
            data: this.$form.serialize(),
            dataType: 'json'
        });
    };

    return GeneratorAdd;
});_N2.d('GeneratorConfigure', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     * @augments {_N2.Form}
     */
    function GeneratorConfigure(options) {

        _N2.Form.prototype.constructor.call(this);
        _N2.r('documentReady', this.onReady.bind(this));
    }

    GeneratorConfigure.prototype = Object.create(_N2.Form.prototype);
    GeneratorConfigure.prototype.constructor = GeneratorConfigure;

    GeneratorConfigure.prototype.onReady = function () {

        this.setID('#n2-ss-form-generator-configure');

        _N2.Form.prototype.onReady.call(this);

        this.$form.on('submit', this.onSubmit.bind(this));
    };

    GeneratorConfigure.prototype.initButtons = function () {
        this.$saveButton = $('.n2_generator_configuration_save')
            .on('click', this.actionSave.bind(this));

        $('.n2_generator_configuration_cancel')
            .on('click', this.actionCancel.bind(this));
    };

    GeneratorConfigure.prototype.actionSave = function (e) {
        e && e.preventDefault();

        this.skipChangeConfirm = true;

        this.$form.trigger("submit");

        this.afterSave();
    };

    GeneratorConfigure.prototype.actionCancel = function (e) {
        e && e.preventDefault();

        window.location = e.currentTarget.href;
    };

    GeneratorConfigure.prototype.onSubmit = function (e) {
        e.preventDefault();

        _N2.AjaxHelper.ajax({
            type: "POST",
            url: this.$form.attr('action'),
            data: this.$form.serialize(),
            dataType: 'json'
        });
    };

    return GeneratorConfigure;
});_N2.d('GeneratorEdit', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     * @augments {_N2.Form}
     */
    function GeneratorEdit(options) {

        _N2.Form.prototype.constructor.call(this);

        this.options = _Assign({
            previewInNewWindow: false,
            previewUrl: ''
        }, options);

        _N2.r('documentReady', this.onReady.bind(this));
    }

    GeneratorEdit.prototype = Object.create(_N2.Form.prototype);
    GeneratorEdit.prototype.constructor = GeneratorEdit;

    GeneratorEdit.prototype.onReady = function () {

        this.setID('#n2-ss-form-generator-edit');

        _N2.Form.prototype.onReady.call(this);

        this.$form.on('submit', this.onSubmit.bind(this));

        this.initPreview();
    };

    GeneratorEdit.prototype.initButtons = function () {
        this.$saveButton = $('.n2_generator_settings_save')
            .on('click', this.actionSave.bind(this));

        $('.n2_generator_settings_back')
            .on('click', this.actionCancel.bind(this));
    };

    GeneratorEdit.prototype.actionSave = function (e) {
        e && e.preventDefault();

        this.skipChangeConfirm = true;

        this.$form.trigger("submit");

        this.afterSave();
    };

    GeneratorEdit.prototype.actionCancel = function (e) {
        e && e.preventDefault();

        window.location = e.currentTarget.href;
    };

    GeneratorEdit.prototype.onSubmit = function (e) {

        if (this.$form.data('preview') !== true) {

            e.preventDefault();

            _N2.AjaxHelper.ajax({
                type: "POST",
                url: this.$form.attr('action'),
                data: this.$form.serialize(),
                dataType: 'json'
            });
        }
    };

    GeneratorEdit.prototype.initPreview = function () {
        if (!this.options.previewInNewWindow) {
            this.modal = new _N2.ModalIframe('preview-slider');
        }

        $('#n2-ss-preview')
            .on('click', (function (e) {
                e.preventDefault();

                this.$form.data('preview', true);

                if (!this.options.previewInNewWindow) {
                    this.modal.show();
                } else {
                    _N2.PreviewPopup('', 'preview-slider');
                }

                var formAction = this.$form.attr('action');

                this.$form
                    .attr({
                        action: _N2.AjaxHelper.makeAjaxUrl(this.options.previewUrl, {
                            nextendaction: this.options.previewInNewWindow ? 'index' : 'generator'
                        }),
                        target: 'preview-slider'
                    })
                    .trigger("submit")
                    .attr({
                        action: formAction,
                        target: null
                    });

                this.$form.data('preview', false);
            }).bind(this));
    };

    return GeneratorEdit;

});_N2.d('FieldRecordViewer', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param id
     * @param ajaxUrl
     * @constructor
     */
    function FieldRecordViewer(id, ajaxUrl) {
        this.ajaxUrl = ajaxUrl;

        this.$element = $("#" + id)
            .on("click", this.showRecords.bind(this));

        this.$form = this.$element.closest('form');
    }

    FieldRecordViewer.prototype.showRecords = function (e) {
        e.preventDefault();
        _N2.AjaxHelper.ajax({
            type: "POST",
            url: this.ajaxUrl,
            data: this.$form.serialize(),
            dataType: "json"
        }).done(function (response) {

            if (response.data !== null) {
                _N2.ModalGeneratorRecordViewer(response.data.headings, response.data.rows);
            }
        });
    };

    return FieldRecordViewer;
});_N2.d('SettingsFonts', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     * @augments {_N2.Form}
     */
    function SettingsFonts(options) {

        _N2.Form.prototype.constructor.call(this);
        _N2.r('documentReady', this.onReady.bind(this));
    }

    SettingsFonts.prototype = Object.create(_N2.Form.prototype);
    SettingsFonts.prototype.constructor = SettingsFonts;

    SettingsFonts.prototype.onReady = function () {

        this.setID('#n2-ss-form-settings-fonts');

        _N2.Form.prototype.onReady.call(this);

        this.$form.on('submit', this.onSubmit.bind(this));
    };

    SettingsFonts.prototype.initButtons = function () {
        this.$saveButton = $('.n2_settings_save')
            .on('click', this.actionSave.bind(this));
    };

    SettingsFonts.prototype.actionSave = function (e) {
        e && e.preventDefault();

        this.skipChangeConfirm = true;

        this.$form.trigger("submit");

        this.afterSave();
    };

    SettingsFonts.prototype.onSubmit = function (e) {
        e.preventDefault();

        _N2.AjaxHelper.ajax({
            type: "POST",
            url: this.$form.attr('action'),
            data: this.$form.serialize(),
            dataType: 'json'
        });
    };

    return SettingsFonts;
});_N2.d('SettingsFramework', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     * @augments {_N2.Form}
     */
    function SettingsFramework(options) {

        _N2.Form.prototype.constructor.call(this);
        _N2.r('documentReady', this.onReady.bind(this));
    }

    SettingsFramework.prototype = Object.create(_N2.Form.prototype);
    SettingsFramework.prototype.constructor = SettingsFramework;

    SettingsFramework.prototype.onReady = function () {

        this.setID('#n2-ss-form-settings-framework');

        _N2.Form.prototype.onReady.call(this);

        this.$form.on('submit', this.onSubmit.bind(this));
    };

    SettingsFramework.prototype.initButtons = function () {
        this.$saveButton = $('.n2_settings_save')
            .on('click', this.actionSave.bind(this));
    };

    SettingsFramework.prototype.actionSave = function (e) {
        e && e.preventDefault();

        this.skipChangeConfirm = true;

        this.$form.trigger("submit");

        this.afterSave();
    };

    SettingsFramework.prototype.onSubmit = function (e) {
        e.preventDefault();

        _N2.AjaxHelper.ajax({
            type: "POST",
            url: this.$form.attr('action'),
            data: this.$form.serialize(),
            dataType: 'json'
        });
    };

    return SettingsFramework;
});_N2.d('SettingsGeneral', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     * @augments {_N2.Form}
     */
    function SettingsGeneral(options) {

        _N2.Form.prototype.constructor.call(this);
        _N2.r('documentReady', this.onReady.bind(this));
    }

    SettingsGeneral.prototype = Object.create(_N2.Form.prototype);
    SettingsGeneral.prototype.constructor = SettingsGeneral;

    SettingsGeneral.prototype.onReady = function () {

        this.setID('#n2-ss-form-settings-general');

        _N2.Form.prototype.onReady.call(this);

        this.$form.on('submit', this.onSubmit.bind(this));
    };

    SettingsGeneral.prototype.initButtons = function () {
        this.$saveButton = $('.n2_settings_save')
            .on('click', this.actionSave.bind(this));
    };

    SettingsGeneral.prototype.actionSave = function (e) {
        e && e.preventDefault();

        this.skipChangeConfirm = true;

        this.$form.trigger("submit");

        this.afterSave();
    };

    SettingsGeneral.prototype.onSubmit = function (e) {
        e.preventDefault();

        _N2.AjaxHelper.ajax({
            type: "POST",
            url: this.$form.attr('action'),
            data: this.$form.serialize(),
            dataType: 'json'
        });
    };

    return SettingsGeneral;
});_N2.d('SettingsItemDefaults', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     * @augments {_N2.Form}
     */
    function SettingsItemDefaults(options) {

        _N2.Form.prototype.constructor.call(this);
        _N2.r('documentReady', this.onReady.bind(this));
    }

    SettingsItemDefaults.prototype = Object.create(_N2.Form.prototype);
    SettingsItemDefaults.prototype.constructor = SettingsItemDefaults;

    SettingsItemDefaults.prototype.onReady = function () {

        this.setID('#n2-ss-form-settings-item-defaults');

        _N2.Form.prototype.onReady.call(this);

        this.$form.on('submit', this.onSubmit.bind(this));
    };

    SettingsItemDefaults.prototype.initButtons = function () {
        this.$saveButton = $('.n2_settings_save')
            .on('click', this.actionSave.bind(this));
    };

    SettingsItemDefaults.prototype.actionSave = function (e) {
        e && e.preventDefault();

        this.skipChangeConfirm = true;

        this.$form.trigger("submit");

        this.afterSave();
    };

    SettingsItemDefaults.prototype.onSubmit = function (e) {
        e.preventDefault();

        _N2.AjaxHelper.ajax({
            type: "POST",
            url: this.$form.attr('action'),
            data: this.$form.serialize(),
            dataType: 'json'
        });
    };

    return SettingsItemDefaults;
});_N2.d('Slide', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param manager
     * @param box
     * @constructor
     */
    function Slide(manager, box) {
        this.selected = false;
        this.manager = manager;

        this.box = box.data('slide', this);

        this.editUrl = this.box.find('.n2_slide_box__slide_overlay_edit_button').attr('href');
        this.isEdited = this.box.hasClass('n2_slide_box--currently-edited');

        this.box.on('contextmenu', this.onContextMenu.bind(this));

        this.box.find('.n2_slide_box__footer_status_published, .n2_slide_box__footer_status_unpublished')
            .on('click', this.switchPublished.bind(this));

        this.box.find('.n2_slide_box__slide_select_tick').on('click', (function (e) {
            e.preventDefault();

            this.invertSelection();
        }).bind(this));

        this.$more = this.box.find('.n2_slide_box__slide_action_more')
            .on('click', this.onMore.bind(this));

        this.initRename();
    }

    Slide.prototype.onContextMenu = function (e) {
        e.preventDefault();

        this.showContextMenu(e);
    };

    Slide.prototype.onMore = function (e) {
        e.preventDefault();

        this.showContextMenu(this.$more[0]);
    };

    Slide.prototype.showContextMenu = function (target) {

        var items = [];

        items.push({
            priority: 55,
            label: n2_('Duplicate'),
            icon: 'ssi_16 ssi_16--duplicate',
            action: this.duplicate.bind(this)
        });
        items.push({
            priority: 55,
            label: n2_('Copy'),
            icon: 'ssi_16 ssi_16--copy',
            action: this.copy.bind(this)
        });

        if (!this.box.hasClass('n2_slide_box--published')) {
            items.push({
                priority: 55,
                label: n2_('Publish'),
                icon: 'ssi_16 ssi_16--filledcheck',
                action: this.publish.bind(this)
            });
        } else {
            items.push({
                priority: 55,
                label: n2_('Unpublish'),
                icon: 'ssi_16 ssi_16--filledremove',
                action: this.unpublish.bind(this)
            });
        }

        if (this.box.hasClass('n2_slide_box--has-generator')) {
            items.push({
                priority: 55,
                label: n2_('Edit generator'),
                icon: 'ssi_16 ssi_16--cog',
                action: this.goToEditGenerator.bind(this)
            });
        }

        if (!this.box.hasClass('n2_slide_box--first-slide') && !this.box.hasClass('n2_slide_box--static-overlay')) {
            items.push({
                priority: 55,
                label: n2_('Set as first'),
                icon: 'ssi_16 ssi_16--star',
                action: (function () {
                    this.setFirst();
                }).bind(this)
            });
        }

        if (this.box.hasClass('n2_slide_box--static-overlay')) {
            items.push({
                priority: 55,
                label: n2_('Convert to slide'),
                icon: 'ssi_16 ssi_16--paste',
                action: this.convertToSlide.bind(this)
            });
        }

        items.push({
            priority: 1000,
            label: n2_('Delete'),
            icon: 'ssi_16 ssi_16--delete',
            action: (function () {
                this.delete();
            }).bind(this),
            color: 'red'
        });

        this.box.addClass('n2_slide_box--context-menu');
        _N2.ContextMenu(target, items, {
            y: 5,
            horizontalAlign: n2const.isRTL() ? 'right' : 'left',
            onClose: (function () {
                this.box.removeClass('n2_slide_box--context-menu');
            }).bind(this)
        });
    };

    Slide.prototype.getSliderId = function () {
        return this.box.data('sliderid');
    };

    Slide.prototype.getId = function () {
        return this.box.data('slideid');
    };

    Slide.prototype.setFirst = function (e) {
        e && e.preventDefault();

        _N2.AjaxHelper.ajax({
            url: _N2.AjaxHelper.makeAjaxUrl(this.manager.ajaxUrl, {
                nextendaction: 'first'
            }),
            type: 'POST',
            data: {
                id: this.getId()
            }
        }).done((function () {
            this.manager.unsetFirst();
            this.box.addClass('n2_slide_box--first-slide');
        }).bind(this));
    };

    Slide.prototype.unsetFirst = function () {
        this.box.removeClass('n2_slide_box--first-slide');
    };

    Slide.prototype.publish = function () {
        this.switchPublished();
    };

    Slide.prototype.unpublish = function () {
        this.switchPublished();
    };

    Slide.prototype.switchPublished = function (e) {

        e && e.preventDefault();

        if (this.isPublished()) {
            this.manager.unPublishSlides([this.getId()], [this]);
        } else {
            this.manager.publishSlides([this.getId()], [this]);
        }
    };

    Slide.prototype.isPublished = function () {
        return this.box.hasClass('n2_slide_box--published');
    };

    Slide.prototype.published = function () {
        this.box.addClass('n2_slide_box--published');

        if (!this.box.hasClass('n2_slide_box--static-overlay')) {
            html.setAttribute('data-published-regular-slides', parseInt(html.getAttribute('data-published-regular-slides')) + 1);
        }
    };

    Slide.prototype.unPublished = function () {
        this.box.removeClass('n2_slide_box--published');

        if (!this.box.hasClass('n2_slide_box--static-overlay')) {
            html.setAttribute('data-published-regular-slides', parseInt(html.getAttribute('data-published-regular-slides')) - 1);
        }
    };

    Slide.prototype.duplicate = function () {
        var deferred = $.Deferred();
        _N2.AjaxHelper.ajax({
            url: _N2.AjaxHelper.makeAjaxUrl(this.manager.ajaxUrl, {
                nextendcontroller: 'slides',
                nextendaction: 'duplicate',
                sliderid: this.getSliderId(),
                slideid: this.getId()
            })
        }).done((function (response) {
            var box = $(response.data).insertAfter(this.box);
            var newSlide = new Slide(this.manager, box);
            this.manager.initSlides();
            deferred.resolve(newSlide);
            _N2._tooltip.add(box);
        }).bind(this));
        return deferred;
    };

    Slide.prototype.delete = function (e) {

        e && e.preventDefault();

        this.manager.deleteSlides([this.getId()], [this]);
    };

    Slide.prototype.deleted = function () {
        this.box.remove();
    };

    Slide.prototype.invertSelection = function (e) {

        e && e.preventDefault();

        if (!this.selected) {
            this.select();
        } else {
            this.deSelect();
        }
    };

    Slide.prototype.select = function () {
        if (!this.selected) {
            this.selected = true;
            this.box.addClass('n2_slide_box--bulk-selected');
            this.manager.addSelection(this);
        }
    };

    Slide.prototype.deSelect = function () {
        if (this.selected) {
            this.selected = false;
            this.box.removeClass('n2_slide_box--bulk-selected');
            this.manager.removeSelection(this);
        }
    };

    Slide.prototype.goToEditGenerator = function () {
        window.location = this.box.data('generator-edit');
    };

    Slide.prototype.copy = function () {

        _N2.SelectSlider(n2_('Copy slide to') + ' ...', (function (sliderID, alias, groupID) {
            _N2.AjaxHelper.ajax({
                url: _N2.AjaxHelper.makeAjaxUrl(this.manager.ajaxUrl, {
                    nextendcontroller: 'slides',
                    nextendaction: 'copy',
                    targetGroupID: groupID,
                    targetSliderID: sliderID,
                    sliderid: this.getSliderId(),
                    slideid: this.getId()
                })
            });
        }).bind(this), true);
    };

    Slide.prototype.initRename = function () {

        this.dblClickTimeout = false;

        this.$label = this.box.find('.n2_slide_box__footer_title')
            .on('click', (function (e) {
                if (this.$label[0].contentEditable !== 'true') {
                    if (!this.dblClickTimeout) {
                        this.dblClickTimeout = setTimeout((function () {
                            if (!this.isEdited) {
                                window.location = this.editUrl;
                            }
                            this.dblClickTimeout = false;
                        }).bind(this), 500);
                    } else {
                        clearTimeout(this.dblClickTimeout);
                        this.dblClickTimeout = false;
                        this.editName();
                    }
                }
            }).bind(this));

        this.title = this.$label.text().trim();
        this.$label.text(this.title);
    };

    Slide.prototype.editName = function () {

        new _N2.InlineTextEditor(this.$label[0], {
            onSave: (function (value) {
                if (this.title !== value) {
                    _N2.AjaxHelper.ajax({
                        url: _N2.AjaxHelper.makeAjaxUrl(this.manager.ajaxUrl, {
                            nextendcontroller: 'slides',
                            nextendaction: 'rename',
                            slideid: this.getId(),
                            title: value
                        })
                    })
                        .done((function () {
                            this.title = value;
                            if (this.isEdited) {
                                _N2.History.get().off();
                                _N2._currentEditor.fragmentEditor.mainContainer.outsideStoreProperty(undefined, 'title', value, 'slideTitleRename');
                                _N2.History.get().on();
                            }
                        }).bind(this))
                        .fail((function () {
                            this.$label.text(this.title);
                        }).bind(this))
                }
            }).bind(this),
            onCancel: (function () {
                this.$label.text(this.title);
            }).bind(this),
            onTab: (function (e) {
                var $renames = $('.n2_slide_box__footer_title');
                for (var i = 0; i < $renames.length; i++) {
                    if (this.$label.is($renames[i])) {
                        if (e.shiftKey) {
                            i--;
                        } else {
                            i++;
                        }
                        break;
                    }
                }

                if (i < 0) {
                    i = $renames.length - 1;
                } else if (i >= $renames.length) {
                    i = 0;
                }

                $renames.eq(i).trigger('click').trigger('click');

            }).bind(this)
        });
    };

    Slide.prototype.convertToSlide = function () {
        _N2.AjaxHelper.ajax({
            url: _N2.AjaxHelper.makeAjaxUrl(this.manager.ajaxUrl, {
                nextendcontroller: 'slides',
                nextendaction: 'convertToSlide',
                sliderid: this.getSliderId(),
                slideid: this.getId()
            })
        }).done((function () {
            this.box.removeClass('n2_slide_box--static-overlay');
            this.box.find('.n2_slide_box__details_static_slide').remove();
        }).bind(this));
    }

    return Slide;
});_N2.d('SlidesManager', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function SlidesManager(options, parameters, isUploadDisabled, uploadUrl, uploadDir) {
        this.quickPostModal = null;
        this.parameters = parameters;
        this.slides = [];

        this.options = _Assign({
            sliderUrl: ''
        }, options);

        this.url = options.url;
        this.ajaxUrl = options.ajaxUrl;
        this.contentAjaxUrl = options.contentAjaxUrl;
        this.$slideManager = $('.n2_slide_manager');
        this.slidesContainer = $('.n2_slide_manager__content');

        this.initSlidesOrderable();

        var slides = this.slidesContainer.find('.n2_slide_box');
        for (var i = 0; i < slides.length; i++) {
            this.slides.push(new _N2.Slide(this, slides.eq(i)));
        }

        html.setAttribute('data-slides', this.slides.length);
        html.setAttribute('data-published-regular-slides', this.slidesContainer.find('.n2_slide_box--published:not(.n2_slide_box--static-overlay)').length);

        $('.n2_slide_manager__add_slide, .n2_slide_manager__dummy_slide')
            .on('click', (function () {
                this.$slideManager.toggleClass('n2_slide_manager--add-slide');
            }).bind(this));

        $('.n2_slide_manager__add_slide_action[data-action]')
            .on('click', (function (e) {
                e.preventDefault();

                switch ($(e.currentTarget).data('action')) {
                    case 'media':
                        this.addQuickMedia(e);
                        break;
                    case 'empty-slide':
                        e.preventDefault();
                        this.createSlide('empty-slide');
                        break;
                    case 'post':
                        this.addQuickPost(e);
                        break;
                    case 'static-overlay':
                        e.preventDefault();
                        this.createSlide('static-overlay');
                        break;
                }
            }).bind(this));

        $('.n2-box-slide-dummy').on('click', this.addQuickMedia.bind(this));

        $('.n2_slide_manager__block_notice_button').on('click', this.changeSliderType.bind(this));

        $('.n2_slide_manager__autoplay_notice_button').on('click', this.openAutoplayTab.bind(this));

        if (window.location.hash.substring(1) === 'changeslidertype') {
            window.location.hash = '';
            this.changeSliderType();
        }

        this.initBulk();


        if (!isUploadDisabled) {

            var slidesContainerElement = this.slidesContainer[0];
            _addEventListener(slidesContainerElement, 'dragover', (function (e) {
                e.preventDefault();
            }).bind(this));
            _addEventListener(slidesContainerElement, 'drop', (function (e) {
                e.preventDefault();
                var files = [];
                if (e.dataTransfer.items) {
                    // Use DataTransferItemList interface to access the file(s)
                    for (var i = 0; i < e.dataTransfer.items.length; i++) {
                        // If dropped items aren't files, reject them
                        if (e.dataTransfer.items[i].kind === 'file') {
                            files.push(e.dataTransfer.items[i].getAsFile());
                        }
                    }
                } else {
                    // Use DataTransfer interface to access the file(s)
                    for (var i = 0; i < e.dataTransfer.files.length; i++) {
                        files.push(e.dataTransfer.files[i]);
                    }
                }

                if (files.length) {
                    var medias = [],
                        promises = [];
                    _N2.LoadingScreen.startLoading();

                    for (var i = 0; i < files.length; i++) {

                        promises.push(new Promise(function (resolve, reject) {
                            var xhr = new XMLHttpRequest(),
                                formData = new FormData();
                            xhr.open('POST', uploadUrl, true);
                            xhr.onload = function () {
                                var response = JSON.parse(xhr.response);
                                if (response.data && response.data.name) {
                                    medias.push({
                                        title: response.data.name.replace(/\.[^\/.]+$/, ""),
                                        description: '',
                                        media: response.data.url
                                    });
                                } else {
                                    _N2.AjaxHelper.notification(response);
                                }

                                resolve();
                            };
                            xhr.onerror = function () {
                                reject();
                            };
                            formData.append('media', files[i]);
                            formData.append('path', '/' + uploadDir);
                            xhr.send(formData)
                        }));

                    }

                    Promise.all(promises).finally((function () {
                        if (medias.length) {
                            this._addQuickMedias(medias);
                        } else {
                            setTimeout(function () {
                                _N2.LoadingScreen.stopLoading();
                            }, 100);
                        }
                        medias = [];
                    }).bind(this));
                }
            }).bind(this));
        }
    }

    SlidesManager.prototype.changed = function () {

    };

    SlidesManager.prototype.initSlidesOrderable = function () {
        this.uiSortable = new _N2.UISortable(this.slidesContainer, {
            items: ".n2_slide_box",
            stop: this.saveSlideOrder.bind(this),
            placeholder: 'n2_slide_manager__sortable_placeholder',
            distance: 10,
            helper: 'clone'
        });
    };

    SlidesManager.prototype.saveSlideOrder = function () {
        var slideNodes = this.slidesContainer.find('.n2_slide_box'),
            slides = [],
            ids = [],
            originalIds = [];
        for (var i = 0; i < slideNodes.length; i++) {
            var slide = slideNodes.eq(i).data('slide');
            slides.push(slide);
            ids.push(slide.getId());
        }
        for (var i = 0; i < this.slides.length; i++) {
            originalIds.push(this.slides[i].getId());
        }

        if (JSON.stringify(originalIds) !== JSON.stringify(ids)) {
            $(window).triggerHandler('SmartSliderSidebarSlidesOrderChanged');
            var queries = {
                nextendcontroller: 'slides',
                nextendaction: 'order'
            };
            _N2.AjaxHelper.ajax({
                type: 'POST',
                url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, queries),
                data: {
                    slideorder: ids
                }
            });
            this.slides = slides;
            this.changed();
        }
    };

    SlidesManager.prototype.initSlides = function () {
        var slideNodes = this.slidesContainer.find('.n2_slide_box'),
            slides = [];
        for (var i = 0; i < slideNodes.length; i++) {
            var slide = slideNodes.eq(i).data('slide');
            slides.push(slide);
        }
        this.slides = slides;
        this.changed();
        $(window).triggerHandler('SmartSliderSidebarSlidesChanged');

        html.setAttribute('data-slides', this.slides.length);
        html.setAttribute('data-published-regular-slides', this.slidesContainer.find('.n2_slide_box--published:not(.n2_slide_box--static-overlay)').length);
    };

    SlidesManager.prototype.unsetFirst = function () {
        for (var i = 0; i < this.slides.length; i++) {
            this.slides[i].unsetFirst();
        }
        this.changed();
    };

    SlidesManager.prototype.addBoxes = function (boxes) {

        boxes.appendTo(this.slidesContainer);
        boxes.addClass('n2_slide_box--just-added')
            .each((function (i, el) {
                new _N2.Slide(this, $(el));
            }).bind(this));
        this.initSlides();
        setTimeout(function () {
            boxes.removeClass('n2_slide_box--just-added');
        }, 200);
    };

    SlidesManager.prototype.createSlide = function (type, data) {

        return _N2.AjaxHelper.ajax({
            type: 'POST',
            url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, {
                nextendaction: 'create',
                type: type
            }),
            data: data
        }).done((function (response) {
            this.addBoxes($(response.data));
        }).bind(this));
    };

    SlidesManager.prototype.addQuickMedia = function (e) {
        e.preventDefault();
        _N2._imageHelper.openMultipleLightbox(this._addQuickMedias.bind(this));
    };

    SlidesManager.prototype._addQuickMedias = function (_medias) {
        var images = [];
        var videos = [];

        for (var i = 0; i < _medias.length; i++) {
            if (!_medias[i].media.match(/\.(mp4)/i)) {
                images.push(_medias[i]);
            } else {
                videos.push(_medias[i]);
            }
        }
        if (images.length) {
            this.createSlide('image', {
                images: _N2.Base64.encode(JSON.stringify(images))
            });
        }

        if (videos.length) {
            this.createSlide('video', {
                videos: _N2.Base64.encode(JSON.stringify(videos))
            });
        }
    };

    SlidesManager.prototype.addQuickPost = function (e) {
        e.preventDefault();
        if (!this.quickPostModal) {
            var manager = this,
                cache = {},
                getContent = (function (search) {
                    if (cache[search] === undefined) {
                        cache[search] = _N2.AjaxHelper.ajax({
                            type: "POST",
                            url: _N2.AjaxHelper.makeAjaxUrl(this.contentAjaxUrl),
                            data: {
                                keyword: search
                            },
                            dataType: 'json'
                        });
                    }
                    return cache[search];
                }).bind(this);

            this.quickPostModal = new _N2.ModalSimple('create-quick-post');
            this.quickPostModal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--post', 'green'));
            this.quickPostModal.addContent(_N2.UI.modalHeading(n2_('Add post')));

            var $row1 = _N2.UI.modalFormRow(true);
            this.quickPostModal.addContent($row1);

            var $searchField = _N2.UI.modalFieldText($row1, 'keyword', n2_('Search keyword'), ''),
                searchString = '';


            var $row2 = _N2.UI.modalFormRow(true);
            this.quickPostModal.addContent($row2);

            var selectedValue = false,
                selectPost = _N2.UI.modalSelectList($row2, 'quick-post', n2_('Posts'), function (value) {
                    selectedValue = value;
                });

            $searchField.on('keyup', (function () {
                searchString = $searchField.val();
                getContent(searchString).done((function (r) {
                    if ($searchField.val() === searchString) {

                        selectPost.removeOptions();

                        var rows = r.data;

                        for (var i = 0; i < rows.length; i++) {
                            selectPost.addOption(rows[i].title, rows[i].info, rows[i]);
                        }
                    }
                }).bind(this));
            }).bind(this))
                .trigger('keyup').focus();

            var $createButton = _N2.UI.modalButton(n2_('Add post'), 'green');
            this.quickPostModal.addButton($createButton);
            $createButton.on('click', (function () {
                if (selectedValue) {
                    manager._addQuickPost(this.quickPostModal, selectedValue);
                } else {
                    _N2.Notification.error(n2_('Please select a Post first!'));
                }

            }).bind(this));

        }

        this.quickPostModal.show();
    };

    SlidesManager.prototype._addQuickPost = function (modal, post) {
        if (!post.image) {
            post.image = '';
        }

        this.createSlide('post', {
            post: post
        });

        if (modal) {
            this.quickPostModal.hide();
        }
    };

    SlidesManager.prototype.initBulk = function () {

        this.selection = [];

        this.isBulkSelection = false;
        $('#n2_slide_manager_bulk_actions a[data-action]')
            .on('click', (function (e) {
                e.preventDefault();
                switch ($(e.currentTarget).data('action')) {
                    case 'duplicate':
                        this.bulkAction('duplicateSlides');
                        break;
                    case 'copy':
                        this.bulkAction('copySlides');
                        break;
                    case 'delete':
                        this.bulkAction('deleteSlides');
                        break;
                    case 'publish':
                        this.bulkAction('publishSlides');
                        break;
                    case 'unpublish':
                        this.bulkAction('unPublishSlides');
                        break;
                    case 'select-all':
                        this.bulkSelect(function (slide) {
                            slide.select();
                        });
                        break;
                    case 'select-none':
                        this.bulkSelect(function (slide) {
                            slide.deSelect();
                        });
                        break;
                    case 'select-published':
                        this.bulkSelect(function (slide) {
                            if (slide.box.hasClass('n2_slide_box--published')) {
                                slide.select();
                            } else {
                                slide.deSelect();
                            }
                        });
                        break;
                    case 'select-unpublished':
                        this.bulkSelect(function (slide) {
                            if (slide.box.hasClass('n2_slide_box--published')) {
                                slide.deSelect();
                            } else {
                                slide.select();
                            }
                        });
                        break;
                }
            }).bind(this));
        $('.n2-bulk-actions a').on('click', (function (e) {
            var action = $(e.currentTarget).data('action');
            if (action) {
                e.preventDefault();
                this.bulkAction(action);
            }
        }).bind(this));
    };

    SlidesManager.prototype.addSelection = function (slide) {
        if (this.selection.length == 0) {
            this.enterBulk();
        }
        this.selection.push(slide);
    };

    SlidesManager.prototype.removeSelection = function (slide) {
        this.selection.splice(this.selection.indexOf(slide), 1);
        if (this.selection.length == 0) {
            this.leaveBulk();
        }
    };

    SlidesManager.prototype.bulkSelect = function (cb) {
        for (var i = 0; i < this.slides.length; i++) {
            cb(this.slides[i]);
        }
    };

    SlidesManager.prototype.bulkAction = function (action) {
        var slides = [],
            ids = [];
        this.bulkSelect(function (slide) {
            if (slide.selected) {
                slides.push(slide);
                ids.push(slide.getId());
            }
        });
        if (ids.length) {
            this[action](ids, slides);
        } else {
            _N2.Notification.notice('Please select one or more slides for the action!');
        }
    };

    SlidesManager.prototype.enterBulk = function () {
        if (!this.isBulkSelection) {
            this.isBulkSelection = true;
            this.uiSortable.setOption('disabled', true);
            $('.n2_slide_manager').addClass('n2_slide_manager--bulk-select');
        }
    };

    SlidesManager.prototype.leaveBulk = function () {
        if (this.isBulkSelection) {
            this.uiSortable.setOption('disabled', false);
            $('.n2_slide_manager').removeClass('n2_slide_manager--bulk-select');

            for (var i = 0; i < this.slides.length; i++) {
                this.slides[i].deSelect();
            }
            this.selection = [];
            this.isBulkSelection = false;
        }
    };

    SlidesManager.prototype.deleteSlides = function (ids, slides) {

        var label = slides.length > 1 ? n2_('delete these slides') : n2_('delete this slide');

        var currentSlide = parseInt($('.n2-ss-currently-edited-slide').attr('data-id'));

        _N2.ModalDeleteConfirm(label, (function () {
            _N2.AjaxHelper.ajax({
                url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, {
                    nextendaction: 'delete'
                }),
                type: 'POST',
                data: {
                    slides: ids
                }
            }).done((function () {
                for (var i = 0; i < slides.length; i++) {
                    slides[i].deleted();
                }
                this.initSlides();
                this.leaveBulk();
                if (ids.indexOf(currentSlide) !== -1) {
                    window.location = this.options.sliderUrl;
                }
            }).bind(this));
        }).bind(this));
    };

    SlidesManager.prototype.duplicateSlides = function (ids, slides) {
        for (var i = 0; i < this.slides.length; i++) {
            if (this.slides[i].selected) {
                this.slides[i].duplicate($.Event("click", {
                    currentTarget: null
                }));
            }
        }
    };

    SlidesManager.prototype.copySlides = function (ids) {

        _N2.SelectSlider(n2_('Copy slide to') + ' ...', (function (sliderID, alias, groupID) {
            _N2.AjaxHelper.ajax({
                url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, {
                    nextendaction: 'copySlides',
                    targetGroupID: groupID,
                    targetSliderID: sliderID
                }),
                type: 'POST',
                data: {
                    slides: ids
                }
            });
        }).bind(this), true);
    };

    SlidesManager.prototype.publishSlides = function (ids, slides) {
        _N2.AjaxHelper.ajax({
            url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, {
                nextendaction: 'publish'
            }),
            type: 'POST',
            data: {
                slides: ids
            }
        }).done((function () {
            for (var i = 0; i < slides.length; i++) {
                slides[i].published();
            }
            this.changed();
        }).bind(this));
    };

    SlidesManager.prototype.unPublishSlides = function (ids, slides) {
        _N2.AjaxHelper.ajax({
            url: _N2.AjaxHelper.makeAjaxUrl(this.ajaxUrl, {
                nextendaction: 'unpublish'
            }),
            type: 'POST',
            data: {
                slides: ids
            }
        }).done((function () {
            for (var i = 0; i < slides.length; i++) {
                slides[i].unPublished();
            }
            this.changed();
        }).bind(this));
    };

    SlidesManager.prototype.changeSliderType = function (e) {
        if (_N2._changeSliderType !== undefined) {
            e && e.preventDefault();
            _N2._changeSliderType.showModal();
        }
    };

    SlidesManager.prototype.openAutoplayTab = function () {
        _N2.$('.n2_form__tab_button[data-related-tab=n2_form__tab_slider_autoplay]').trigger('click');
    };

    return SlidesManager;
});_N2.d('SmartSliderSlideBackgroundAdmin', ['$', 'SmartSliderSlideBackground'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param {_N2.FrontendSliderSlide} slide
     * @param {Element} element
     * @param {_N2.SmartSliderBackgrounds} manager
     * @constructor
     */
    function SmartSliderSlideBackgroundAdmin(slide, element, manager) {

        this.types = {
            color: 'SmartSliderAdminSlideBackgroundColor',
            image: 'SmartSliderAdminSlideBackgroundImage',
            video: 'SmartSliderAdminSlideBackgroundVideo'
        };
        this.allowVisualLoad = false;
    

        this.slider = slide.slider;

        _N2.SmartSliderSlideBackground.prototype.constructor.call(this, slide, element, manager);
    }

    SmartSliderSlideBackgroundAdmin.prototype = Object.create(_N2.SmartSliderSlideBackground.prototype);
    SmartSliderSlideBackgroundAdmin.prototype.constructor = SmartSliderSlideBackgroundAdmin;

    /**
     *
     * @param {_N2.SectionSlide} sectionSlide
     */
    SmartSliderSlideBackgroundAdmin.prototype.setRelatedSectionSlide = function (sectionSlide) {
        /**
         * @type {_N2.SectionSlide}
         */
        this.sectionSlide = sectionSlide;
    };

    SmartSliderSlideBackgroundAdmin.prototype.setVisualLoad = function (state) {
        this.allowVisualLoad = state;
    };

    SmartSliderSlideBackgroundAdmin.prototype.setType = function (type) {
        if (type === 'color') {
            if (!this.elements.color) {
                this.createColorElement(true);
            }

            if (this.elements.image) {
                this.elements.image.kill();
                this.elements.image = false;
            }
        } else if (type === 'image') {
            if (!this.elements.color) {
                this.createColorElement(true);
            }

            if (!this.elements.image) {
                this.createImageElement();
            }
        } else {
        }
    };

    SmartSliderSlideBackgroundAdmin.prototype.setMode = function (newMode) {
        if (newMode === 'blurfit') {
            this.elements.image.blurFitMode = "";
        }

        if (newMode === 'default') {
            newMode = this.slider.editor.options.slideBackgroundMode;

            if (this.slider.editor.options.slideBackgroundMode === 'blurfit') {
                this.elements.image.blurFitMode = 'default';
            }
        }


        _NodeSetData(this.element, 'mode', newMode);

        if (this.elements.image) {
            this.elements.image.updateMode(newMode, this.mode);
        }

        this.mode = newMode;
    };

    SmartSliderSlideBackgroundAdmin.prototype.setFocus = function (x, y) {
        if (this.elements.image) {
            this.elements.image.updateFocus(x, y);
        }
    };

    SmartSliderSlideBackgroundAdmin.prototype.setImageOpacity = function (opacity) {
        if (this.elements.image) {
            this.elements.image.updateOpacity(opacity);
        }
    };

    SmartSliderSlideBackgroundAdmin.prototype.setBlur = function (blur, blurFit = false) {
        if (this.elements.image) {
            this.elements.image.updateBlur(blur, blurFit);
        }

    };

    SmartSliderSlideBackgroundAdmin.prototype.createColorElement = function (needRefresh) {
        needRefresh = needRefresh || false;
        var el = _CreateElementDiv();
        _NodeAddClass(el, 'n2-ss-slide-background-color');
        this.element.appendChild(el);
        this.elements.color = new _N2[this.types.color](this, el);

        if (needRefresh) {
            this.elements.color.update(this.sectionSlide.getProperty('backgroundColor'), this.sectionSlide.getProperty('backgroundGradient'), this.sectionSlide.getProperty('backgroundColorEnd'), this.sectionSlide.getProperty('backgroundColorOverlay'));
        }
    };

    SmartSliderSlideBackgroundAdmin.prototype.updateColor = function (color, gradient, colorEnd, isOverlay) {
        if (!this.elements.color) {
            this.createColorElement();
        }


        this.elements.color.update(color, gradient, colorEnd, isOverlay);
    };

    SmartSliderSlideBackgroundAdmin.prototype.createImageElement = function () {
        var image = _N2._currentEditor.generator.fill(this.sectionSlide.getProperty('backgroundImage'));
        if (image !== '') {
            var imageUrl = _N2._imageHelper.fixed(image),
                imageElement = _CreateElementDiv(),
                pictureElement = _CreateElement('picture'),
                imgElement = _CreateElement('img');
            imageElement.appendChild(pictureElement);
            pictureElement.appendChild(imgElement);

            _NodeAddClass(imageElement, 'n2-ss-slide-background-image');
            _NodeSetData(imageElement, 'hash', md5(image));
            _NodeSetData(imageElement, 'desktop', imageUrl);
            _NodeSetData(imageElement, 'blur', this.sectionSlide.getProperty('backgroundImageBlur'));
            _NodeSetStyles(imageElement, {
                opacity: this.sectionSlide.getProperty('backgroundImageOpacity') / 100,
                backgroundPosition: _N2._currentEditor.generator.fill(this.sectionSlide.getProperty('backgroundFocusX')) + '% ' + _N2._currentEditor.generator.fill(this.sectionSlide.getProperty('backgroundFocusY')) + '%'
            });

            this.element.appendChild(imageElement);

            this.elements.image = new _N2[this.types.image](this.slide, this.manager, this, [imageElement]);
            this.elements.image.preLoadAdmin(image);
        }
    };

    SmartSliderSlideBackgroundAdmin.prototype.setImage = function (image) {
        if (!image) {
            if (this.elements.image) {
                this.elements.image.kill();
                this.elements.image = false;
            }
        } else if (this.elements.image) {
            this.elements.image.setDesktopSrc(image);
        } else if (image !== '') {
            if (image.match(/\.(png|jpg|jpeg|gif|webp|svg)/i) === null) {
                _N2.Notification.error('The background image format is not correct! The supported image formats are: png, jpg, jpeg, gif, webp, svg.');
                $('#layerslide-backgroundImage').val('').trigger('change');
            } else if (this.sectionSlide.getProperty('background-type') === 'image') {
                this.createImageElement();
            }
        }
    };

    SmartSliderSlideBackgroundAdmin.prototype.setThumbnail = function (image, oldBackgroundImage) {
        var $thumbnail = $('#layerslide-thumbnail');

        if (image !== '' && image.match(/\.(png|jpg|jpeg|gif|webp|svg)/i) && ($thumbnail.val() === '' || $thumbnail.val() === oldBackgroundImage)) {
            $thumbnail.val(image).trigger('change');
        }

    };

    return SmartSliderSlideBackgroundAdmin;
});_N2.d('SlideEdit', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param options
     * @constructor
     * @augments {_N2.Form}
     */
    function SlideEdit(options) {

        this.options = _Assign({
            ajaxUrl: '',
            slideAsFile: 0,
            nextendAction: '',
            previewInNewWindow: false,
            previewUrl: '',
            sliderElementID: '',
            slideEditorOptions: {}
        }, options);

        this.editor = new _N2.EditorSlide(this, this.options.sliderElementID, this.options.slideEditorOptions);

        _N2.Form.prototype.constructor.call(this);
        _N2.r('documentReady', this.onReady.bind(this));
    }

    SlideEdit.prototype = Object.create(_N2.Form.prototype);
    SlideEdit.prototype.constructor = SlideEdit;

    SlideEdit.prototype.onReady = function () {
        if (this.options.nextendAction === 'create') {
            this.initBackgroundOpen();
        }

        this.setID('#n2-ss-form-slide-edit');

        _N2.Form.prototype.onReady.call(this);

        this.initPreview();
    };

    SlideEdit.prototype.initBackgroundOpen = function () {
        _N2.r(['$', 'windowLoad'], function () {
            var $ = _N2.$;
            $('.n2_form__tab_button[data-related-tab=n2_form__tab_slide_slide-background]').trigger('click');
        });
    };

    SlideEdit.prototype.resetChangeTracker = function () {
        this.hasChange = false;

        document.addEventListener('historyChange', this.onChangeCallback, {
            passive: true,
            capture: true
        });

        this.$saveButton.addClass('n2_button--inactive');
    };

    SlideEdit.prototype.onChange = function () {
        this.hasChange = true;

        this.$saveButton.removeClass('n2_button--inactive');

        document.removeEventListener('historyChange', this.onChangeCallback, {
            passive: true,
            capture: true
        });
    };

    SlideEdit.prototype.initButtons = function () {

        this.$saveButton = $('.n2_slide_settings_save')
            .on('click', this.actionSave.bind(this));

        $('.n2_slide_settings_back')
            .on('click', this.actionCancel.bind(this));

        $('.n2_slide_generator_static_save')
            .on('click', (function (e) {
                this.actionSave(e, true);
            }).bind(this));
    };

    SlideEdit.prototype.actionSave = function (e, isGeneratorStatic) {
        e && e.preventDefault();

        var formData = this.editor.getFormData();

        if (isGeneratorStatic === true) {
            formData.generatorStatic = '1';
        }

        if (this.options.slideAsFile && typeof window.FormData !== undefined && window.File !== undefined) {
            var fd = new FormData();

            try {
                fd.append('slide', new Blob([formData.slide]), "slide.txt");
            } catch (e) {
                try {
                    fd.append('slide', new Blob([formData.slide]));
                } catch (e) {
                    try {
                        fd.append('slide', new File([formData.slide], "slide.txt"));
                    } catch (e) {
                        _N2.Notification.notice('Your browser does not support File api, please disable "Send slide as file" option in the global settings.');
                    }
                }
            }

            delete formData.slide;

            for (var k in formData) {
                fd.append(k, formData[k]);
            }

            _N2.AjaxHelper.ajax({
                url: _N2.AjaxHelper.makeAjaxUrl(this.options.ajaxUrl),
                type: 'POST',
                data: fd,
                contentType: false,
                processData: false
            }).done(this.afterSave.bind(this));
        } else {
            _N2.AjaxHelper.ajax({
                type: 'POST',
                url: _N2.AjaxHelper.makeAjaxUrl(this.options.ajaxUrl),
                data: formData,
                dataType: 'json'
            }).done(this.afterSave.bind(this));
        }
    };

    SlideEdit.prototype.actionCancel = function (e) {
        e && e.preventDefault();

        window.location = e.currentTarget.href;
    };

    SlideEdit.prototype.afterSave = function () {

        var slide = this.editor.fragmentEditor.mainContainer,
            $slideBox = $('.n2_admin_editor__ui_slide_manager .n2_slide_box--currently-edited ');

        $slideBox.find('.n2_slide_box__footer_title')
            .text(slide.getProperty('title'));

        $slideBox.find('.n2_slide_box__content')
            .css('background-image', 'url(' + _N2._imageHelper.fixed(this.editor.generator.fill(slide.getProperty('thumbnail') || slide.getProperty('backgroundImage') || '$ss3-frontend$/images/placeholder/image.png')) + ')');

        var hiddenOn = [];
        if (!parseInt(slide.getProperty('desktopPortrait'))) {
            hiddenOn.push(n2_('Desktop'));
        }
        if (!parseInt(slide.getProperty('tabletPortrait'))) {
            hiddenOn.push(n2_('Tablet'));
        }
        if (!parseInt(slide.getProperty('mobilePortrait'))) {
            hiddenOn.push(n2_('Mobile'));
        }

        var hiddenOnText = '';

        if (hiddenOn.length) {
            hiddenOnText = n2_sprintf(n2_('This slide is hidden on the following devices: %s'), hiddenOn.join(', '));
        }

        $slideBox.find('.n2_slide_box__footer_status_hidden')
            .data('n2tip', hiddenOnText);


        _N2.Form.prototype.afterSave.call(this);
    };

    SlideEdit.prototype.initPreview = function () {
        if (!this.options.previewInNewWindow) {
            this.modal = new _N2.ModalIframe('preview-slider');
        }

        $('#n2-ss-preview')
            .on('click', (function (e) {
                e.preventDefault();

                if (!this.options.previewInNewWindow) {
                    this.modal.show();
                } else {
                    _N2.PreviewPopup('', 'preview-slider');
                }

                var $form = $('<form style="display:none;"></form>')
                    .attr({
                        method: 'post',
                        action: _N2.AjaxHelper.makeAjaxUrl(this.options.previewUrl, {
                            nextendaction: 'slide',
                            frame: this.options.previewInNewWindow ? 0 : 1
                        }),
                        target: 'preview-slider'
                    })
                    .appendTo('body');

                var formData = this.editor.getFormData();

                $('<input name="slide">')
                    .val(formData.slide)
                    .appendTo($form);

                $form.trigger("submit");

            }).bind(this));
    };

    return SlideEdit;

});_N2.d('EditorAbstract', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param {string} sliderElementID
     * @param {object} options
     * @constructor
     * @abstract
     */
    function EditorAbstract(sliderElementID, options) {

        warnInternetExplorerUsers();

        this.readyDeferred = $.Deferred();

        this.sliderElementID = sliderElementID;

        this.readyDeferred.done((function () {
            _N2.d('SSEditor', (function () {
                return this;
            }).bind(this));
        }).bind(this));

        this.options = _Assign({
            isUploadDisabled: true,
            uploadUrl: '',
            uploadDir: '',
            settingsGoProUrl: '',
            sectionLibraryFree: true,
            sectionLibraryUrl: '',
            sectionLibraryGoProUrl: ''
        }, options);

        /**
         * @type {_N2.EditorAbstract}
         */
        _N2._currentEditor = this;

        /** @type {_N2.SmartSliderAbstract} */
        this.frontend = null;
        /** @type {_N2.Generator} */
        this.generator = null;
        /** @type {_N2.FragmentEditor} */
        this.fragmentEditor = null;

        this.$editedElement = null;
        this.editedInstance = null;

        _N2.r(['$', 'documentReady'], (function () {
            n2const.fonts.then(this.startEditor.bind(this));
        }).bind(this));
    }

    /**
     * @abstract
     */
    EditorAbstract.prototype.startEditor = function () {
    };

    EditorAbstract.prototype.ready = function (fn) {
        this.readyDeferred.done(fn);
    };

    EditorAbstract.prototype.getSelf = function () {
        return this;
    };

    /**
     * @returns {{mobileLandscape: number, mobilePortrait: number, desktopPortrait: number, tabletLandscape: number, desktopLandscape: number, tabletPortrait: number}}
     */
    EditorAbstract.prototype.getAvailableDeviceModes = function () {
        return {
            "desktopPortrait": 1,
            "desktopLandscape": 0,
            "tabletPortrait": 1,
            "tabletLandscape": 0,
            "mobilePortrait": 1,
            "mobileLandscape": 0
        };
    };

    EditorAbstract.prototype.getGeneratorVariables = function () {
        return this.$editedElement.data('variables');
    };

    /**
     *
     * @returns {*} .n2-ss-layers-container
     */
    EditorAbstract.prototype.getMainContainerElement = function () {
        return this.$editedElement.find('.n2-ss-layers-container').addBack().last();
    };

    function warnInternetExplorerUsers() {
        var ie = isInternetExplorer();
        if (ie && ie < 10) {
            alert(window.ss2lang.The_editor_was_tested_under_Internet_Explorer_10_Firefox_and_Chrome_Please_use_one_of_the_tested_browser);
        }
    }

    function isInternetExplorer() {
        var myNav = navigator.userAgent.toLowerCase();
        return (myNav.indexOf('msie') !== -1) ? parseInt(myNav.split('msie')[1]) : false;
    }


    return EditorAbstract;
});_N2.d('EditorSlide', ['$', 'EditorAbstract'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param {_N2.SlideEdit} slideEdit
     * @param {string} sliderElementID
     * @param {object} options
     * @constructor
     * @augments {_N2.EditorAbstract}
     */
    function EditorSlide(slideEdit, sliderElementID, options) {

        this.slideEdit = slideEdit;

        _N2.EditorAbstract.prototype.constructor.call(this, sliderElementID, _Assign({
            slideBackgroundMode: 'fill'
        }, options));

        this.onTickCallback = this.onTick.bind(this);
    }

    EditorSlide.prototype = Object.create(_N2.EditorAbstract.prototype);
    EditorSlide.prototype.constructor = EditorSlide;


    EditorSlide.prototype.startEditor = function () {

        _N2.r('#' + this.sliderElementID, (function () {
            var slider = _N2['#' + this.sliderElementID];
            /** @type {_N2.SmartSliderAbstract} */
            this.frontend = slider;
            this.frontend.editor = this;
            _N2._pre = 'div#' + this.frontend.elementID + ' ';

            this.frontend.visible(this.sliderStarted.bind(this));
        }).bind(this));
    };

    EditorSlide.prototype.sliderStarted = function () {

        $('body').addClass('n2-ss-slider-visible');

        this.$editedElement = $(this.frontend.sliderElement.querySelector('.n2-ss-currently-edited-slide'));
        this.editedInstance = this.$editedElement[0].ssSlide;

        this.generator = new _N2.Generator(this);

        this.generator.registerGlobalField('slide', 'name', '#layerslide-title');
        this.generator.registerGlobalField('slide', 'description', '#layerslide-description');

        this.getMainContainerElement().on({
            'updateSize': this.updateSize.bind(this),
            'interaction': this.updateSize.bind(this)
        });

        /**
         *
         * @type {_N2.FragmentEditor}
         */
        this.fragmentEditor = new _N2.FragmentEditor(this, $(this.getFrontendSlide().element), this.options);

        var $frontendElement = this.getFrontendElement();

        _addEventListener($frontendElement[0], 'SliderDeviceOrientation', this.fragmentEditor.onChangeDeviceOrientation.bind(this.fragmentEditor));

        _addEventListener($frontendElement[0], 'SliderResize', (function (e) {
            this.fragmentEditor.onResize(e.detail.ratios);
        }).bind(this));

        this.readyDeferred.resolve();
    };

    /**
     *
     * @returns {{guides: string, slide: string}}
     */
    EditorSlide.prototype.getFormData = function () {

        return {
            guides: _N2.Base64.encode(JSON.stringify(this.fragmentEditor.canvasSettings.ruler.toArray())),
            slide: _N2.Base64.encode(_N2._UnicodeToHTMLEntity(JSON.stringify(this.fragmentEditor.getData())))
        };
    };

    EditorSlide.prototype.getAvailableDeviceModes = function () {
        return this.frontend.responsive.parameters.enabledDevices;
    };

    /**
     *
     * @returns {_N2.SmartSliderSlideBackgroundAdmin}
     */
    EditorSlide.prototype.getSlideBackground = function () {
        return this.$editedElement[0].ssSlideBackground;
    };

    /**
     *
     * @returns {jQuery} #n2-ss-0
     */
    EditorSlide.prototype.getFrontendElement = function () {
        return $(this.frontend.sliderElement);
    };

    /**
     * @returns {_N2.FrontendSliderSlide}
     */
    EditorSlide.prototype.getFrontendSlide = function () {
        return this.editedInstance;
    };

    EditorSlide.prototype.getHorizontalRatio = function () {
        return this.frontend.responsive.ratios.slideW;
    };

    EditorSlide.prototype.getVerticalRatio = function () {
        return this.frontend.responsive.ratios.slideH;
    };

    EditorSlide.prototype.updateSize = function () {

        if (!this.___rafRequested) {
            this.frontend.responsive.doResize();

            _N2.___Ticker.add(this.onTickCallback);
            this.___rafRequested = true;
        }
    };

    EditorSlide.prototype.onTick = function () {

        _N2.___Ticker.remove(this.onTickCallback);

        this.frontend.responsive.doResize();
        delete this.___rafRequested;
    }

    EditorSlide.prototype.getDeviceMode = function () {
        return this.frontend.responsive.getNormalizedModeString();
    };

    return EditorSlide;
});_N2.d('Generator', ['$', 'EditorAbstract'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.EditorAbstract} editor
     * @constructor
     */
    function Generator(editor) {

        /**
         * @type {_N2.EditorAbstract}
         */
        this.editor = editor;
        this._refreshTimeout = null;
        this.modal = false;
        this.group = 0;
        this.editor.generator = this;
        var variables = this.editor.getGeneratorVariables();
        if (variables) {
            this.variables = variables;

            for (var i in this.variables) {
                if (!isNaN(parseFloat(i)) && isFinite(i)) {
                    this.group = Math.max(this.group, parseInt(i) + 1);
                }
            }

            this.fill = this.generatorFill;
            if (this.group > 0) {
                this.registerField = this.generatorRegisterField;

                this.button = $('<div class="n2_field_generator_data_button"><div class="n2_field_generator_data_button_icon"><i class="ssi_16 ssi_16--data"></i></div><div class="n2_field_generator_data_button_label">' + n2_('Data') + '</div></div>')
                    .on('click', (function (e) {
                        this.showModal();
                    }).bind(this));


                $('body').addClass('n2_ss_body--dynamic-slide');
            }
        } else {
            this.variables = null;
        }
    }

    Generator.prototype.isDynamicSlide = function () {
        return this.group > 0;
    };

    Generator.prototype.splitTokens = function (input) {
        var tokens = [];
        var currentToken = "";
        var nestingLevel = 0;
        for (var i = 0; i < input.length; i++) {
            var currentChar = input[i];
            if (currentChar === "," && nestingLevel === 0) {
                tokens.push(currentToken);
                currentToken = "";
            } else {
                currentToken += currentChar;
                if (currentChar === "(") {
                    nestingLevel++;
                } else if (currentChar === ")") {
                    nestingLevel--;
                }
            }
        }
        if (currentToken.length) {
            tokens.push(currentToken);
        }
        return tokens;
    };

    Generator.prototype.fill = function (value) {
        return value;
    };

    Generator.prototype.generatorFill = function (value) {
        if (typeof value === 'string') {
            return value.replace(/{((([a-zA-Z]+)\(([^}]+)\))|([a-zA-Z0-9][a-zA-Z0-9_\/]*))}/g, this.parseFunction.bind(this));
        }

        return value;
    };

    Generator.prototype.parseFunction = function (s, s2, s3, functionName, argumentString, variable) {
        if (variable === undefined) {

            var args = this.splitTokens(argumentString);
            for (var i = 0; i < args.length; i++) {
                args[i] = this.parseVariable(args[i]);
            }
            if (typeof this[functionName] === 'function') {
                return this[functionName].apply(this, args);
            }
            return s;
        } else {
            return this.parseVariable(variable);
        }
    };

    Generator.prototype.parseVariable = function (variable) {
        var _string = variable.match(/^("|')(.*)("|')$/);
        if (_string) {
            return _string[2];
        }

        var functionMatch = variable.match(/((([a-z]+)\(([^}]+)\)))/);
        if (functionMatch) {
            return this.parseFunction.apply(this, functionMatch);
        } else {
            var variableMatch = variable.match(/([a-zA-Z][0-9a-zA-Z_]*)(\/([0-9a-z]+))?/);
            if (variableMatch) {
                var index = variableMatch[3];
                if (index === undefined) {
                    index = 0;
                } else {
                    var i = parseInt(index);
                    if (!isNaN(i)) {
                        index = Math.max(index, 1) - 1;
                    }
                }
                if (this.variables[index] !== undefined && this.variables[index][variableMatch[1]] !== undefined) {
                    return this.variables[index][variableMatch[1]];
                }
                return '';
            }
            return variable;
        }
    };

    Generator.prototype.fallback = function (variable, def) {
        if (variable == '') {
            return def;
        }
        return variable;
    };

    Generator.prototype.cleanhtml = function (variable) {
        return this.stripTags(variable, '<p><a><b><br /><br/><br><i>');
    };

    Generator.prototype.stripTags = function (input, allowed) {
        allowed = (((allowed || '') + '')
            .toLowerCase()
            .match(/<[a-z][a-z0-9]*>/g) || [])
            .join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
        var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
            commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
        return input.replace(commentsAndPhpTags, '')
            .replace(tags, function ($0, $1) {
                return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
            });
    };

    Generator.prototype.removehtml = function (variable) {
        return $('<div>' + variable + '</div>').text();
    };

    Generator.prototype.splitbychars = function (s, start, length) {
        return s.substr(start, length);
    };

    Generator.prototype.splitbywords = function (variable, start, length) {
        var s = variable,
            len = s.length;

        if (len > start) {
            var posStart = Math.max(0, start == 0 ? 0 : s.indexOf(' ', start)),
                posEnd = Math.max(0, length > len ? len : s.indexOf(' ', length));
            if (posEnd == 0 && length <= len) posEnd = len;
            return s.substr(posStart, posEnd);
        } else {
            return '';
        }
    };

    Generator.prototype.findimage = function (variable, index) {
        var s = variable,
            re = /(<img.*?src=[\'"](.*?)[\'"][^>]*>)|(background(-image)??\s*?:.*?url\((["|\']?)?(.+?)(["|\']?)?\))/gi,
            r = [],
            tmp = null;

        index = index !== undefined ? parseInt(index) - 1 : 0;

        while (tmp = re.exec(s)) {
            if (tmp[2] !== undefined) {
                r.push(tmp[2]);
            } else if (tmp[6] !== undefined) {
                r.push(tmp[6]);
            }
        }

        if (r.length) {
            if (r.length > index) {
                return r[index];
            } else {
                return r[r.length - 1];
            }
        } else {
            return '';
        }
    };

    Generator.prototype.findlink = function (variable, index) {
        var s = variable,
            re = /href=["\']?([^"\'>]+)["\']?/gi,
            r = [],
            tmp = null;

        index = index !== undefined ? parseInt(index) - 1 : 0;

        while (tmp = re.exec(s)) {
            if (tmp[1] !== undefined) {
                r.push(tmp[1]);
            }
        }

        if (r.length) {
            if (r.length > index) {
                return r[index];
            } else {
                return r[r.length - 1];
            }
        } else {
            return '';
        }
    };

    Generator.prototype.removevarlink = function (variable) {
        var s = String(variable),
            re = /<a href=\"(.*?)\">(.*?)<\/a>/g;

        return s.replace(re, '');
    };

    Generator.prototype.removelinebreaks = function (variable) {
        var s = String(variable),
            re = /\r?\n|\r/g;
        return s.replace(re, '');
    };

    Generator.prototype.registerFields = function (fields) {
        for (var i = 0; i < fields.length; i++) {
            this.registerField(fields[i]);
        }
    };

    Generator.prototype.registerGlobalField = function (namespace, name, field) {
        if (this.variables !== null) {
            field = $(field).on('outsideChange', (function () {
                this.variables[namespace][name] = field.val();
                this.refresh();
            }).bind(this));
        }
    };

    Generator.prototype.registerField = function (field) {
    };

    Generator.prototype.generatorRegisterField = function (field) {
        field = $(field);
        var parent = field.parent();
        parent.on({
            mouseenter: (function () {
                this.activeField = field;
                this.button.prependTo(parent);
            }).bind(this)
        });
    };

    Generator.prototype.getModal = function () {
        if (!this.selectVariableModal) {

            var active = {
                    key: '',
                    group: 1,
                    filter: 'no',
                    split: 'no',
                    splitStart: 0,
                    splitLength: 300,
                    findImage: 0,
                    findImageIndex: 1,
                    findLink: 0,
                    findLinkIndex: 1,
                    removeVarLink: 0,
                    removelinebreaks: 0
                },
                getVariableString = function () {
                    var variable = active.key + '/' + active.group;
                    if (active.findImage != 0) {
                        variable = 'findimage(' + variable + ',' + Math.max(1, active.findImageIndex) + ')';
                    }
                    if (active.findLink != 0) {
                        variable = 'findlink(' + variable + ',' + Math.max(1, active.findLinkIndex) + ')';
                    }
                    if (active.removeVarLink != 0) {
                        variable = 'removevarlink(' + variable + ')';
                    }
                    if (active.removelinebreaks != 0) {
                        variable = 'removelinebreaks(' + variable + ')';
                    }
                    if (active.filter != 'no') {
                        variable = active.filter + '(' + variable + ')';
                    }
                    if (active.split != 'no' && active.splitStart >= 0 && active.splitLength > 0) {
                        variable = active.split + '(' + variable + ',' + active.splitStart + ',' + active.splitLength + ')';
                    }
                    return '{' + variable + '}';
                },
                updateResult = (function () {
                    $result.text(this.fill(getVariableString()));
                }).bind(this);

            this.selectVariableModal = new _N2.ModalSimple('generator-insert-variable', {
                width: 760
            });
            this.selectVariableModal.addContent(_N2.UI.modalIcon('ssi_48 ssi_48--data', 'blue'));

            if (this.group > 1) {
                _N2.UI.modalGeneratorVariableList(this.selectVariableModal, this.group, 'Select group', 'group', 'Groups', function (variableKey) {
                    active.group = variableKey;
                    updateResult();
                });
            }

            _N2.UI.modalGeneratorVariableList(this.selectVariableModal, this.variables[0], 'Insert variable', 'variable', 'Variables', function (variableKey) {
                active.key = variableKey;
                updateResult();
            });

            active.key = Object.keys(this.variables[0])[0];

            var $row1 = _N2.UI.modalFormRow();
            this.selectVariableModal.addContent($row1);

            _N2.UI.modalSelect($row1, 'filter-select', n2_('Filter'), {
                'no': n2_('No'),
                'cleanhtml': n2_('Clean HTML'),
                'removehtml': n2_('Remove HTML')
            }, 'no').on('nextendChange', function () {
                active.filter = $(this).val();
                updateResult();
            });

            _N2.UI.modalSelect($row1, 'split-select', n2_('Split by Chars'), {
                'no': n2_('No'),
                'splitbychars': n2_('Strict'),
                'splitbywords': n2_('Respect words')
            }, 'no').on('nextendChange', function () {
                active.split = $(this).val();
                updateResult();
            });

            _N2.UI.modalNumber($row1, 'split-start', '', 0, {
                wide: 2,
                sublabel: 'start'
            }).on('nextendChange', function () {
                active.splitStart = $(this).val();
                updateResult();
            });

            _N2.UI.modalNumber($row1, 'split-length', '', 300, {
                wide: 3,
                sublabel: 'length'
            }).on('nextendChange', function () {
                active.splitLength = $(this).val();
                updateResult();
            });

            var $row2 = _N2.UI.modalFormRow();
            this.selectVariableModal.addContent($row2);

            _N2.UI.modalOnOff($row2, 'find-image', n2_('Find image'), 0).on('nextendChange', function () {
                active.findImage = $(this).val();
                updateResult();
            });

            _N2.UI.modalNumber($row2, 'find-image-index', '', 1, {
                wide: 2,
                sublabel: 'index'
            }).on('nextendChange', function () {
                active.findImageIndex = $(this).val();
                updateResult();
            });

            _N2.UI.modalOnOff($row2, 'find-link', n2_('Find link'), 0).on('nextendChange', function () {
                active.findLink = $(this).val();
                updateResult();
            });

            _N2.UI.modalNumber($row2, 'find-link-index', '', 1, {
                wide: 2,
                sublabel: 'index'
            }).on('nextendChange', function () {
                active.findLinkIndex = $(this).val();
                updateResult();
            });

            _N2.UI.modalOnOff($row2, 'remove-link', n2_('Remove links'), 0).on('nextendChange', function () {
                active.removeVarLink = $(this).val();
                updateResult();
            });

            _N2.UI.modalOnOff($row2, 'remove-line-break', n2_('Remove line breaks'), 0).on('nextendChange', function () {
                active.removelinebreaks = $(this).val();
                updateResult();
            });

            var $row3 = _N2.UI.modalFormRow(true);
            this.selectVariableModal.addContent($row3);

            var resultContainer = _N2.UI.modalFieldWrapper($row3, 'result', n2_('Result')),
                $result = $('<div class="n2_generator_result"></div>').appendTo(resultContainer.$element);

            var $insertButton = _N2.UI.modalButton(n2_('Insert'), 'green');
            this.selectVariableModal.addButton($insertButton);

            $insertButton.on('click', (function (e) {
                e.preventDefault();
                this.insert(getVariableString());
                this.selectVariableModal.hide();
            }).bind(this));

            updateResult();
        }
        return this.selectVariableModal;
    };

    Generator.prototype.showModal = function () {

        this.getModal().show();
    };

    Generator.prototype.insert = function (value) {
        this.activeField.val(value).trigger('change');
    };


    Generator.prototype.refresh = function () {
        if (this._refreshTimeout) {
            clearTimeout(this._refreshTimeout);
            this._refreshTimeout = null;
        }
        this._refreshTimeout = setTimeout(this._refresh.bind(this), 100);
    };

    Generator.prototype._refresh = function () {
        var layers = this.editor.fragmentEditor.mainContainer.container.getAllLayers();
        for (var j = 0; j < layers.length; j++) {
            if (layers[j].type === 'layer') {
                layers[j].item.reRender();
            }
        }
    };

    return Generator;
});_N2.d('Historical', '$', function () {
    "use strict";

    /**
     * @memberOf _N2
     * @param c class
     */
    function Historical(c) {
        for (var k in Historical.prototype) {
            c.prototype[k] = Historical.prototype[k];
        }
    }

    /**
     * @param {_N2.Historical} self
     */
    Historical.prototype.setSelf = function (self) {
        if (self === undefined) {
            console.error(self);
        }
        if (this.self !== undefined && this.self !== this) {
            this.self.setSelf(self);
        }
        /**
         * @type {_N2.Historical}
         */
        this.self = self;

        this.onSelfChange();
    };

    /**
     * @returns {_N2.Historical}
     */
    Historical.prototype.getSelf = function () {
        if (this.self === undefined) {
            this.self = this;
        } else if (this.self !== this) {
            this.self = this.self.getSelf();
        }
        return this.self;
    };

    Historical.prototype.onSelfChange = function () {

    };

    return Historical;
});_N2.d('History', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function History() {

        this.historyStates = 50;
        this.enabled = this.historyStates !== 0;
        this.historyActionInProgress = false;
        this.historyAddAllowed = true;
        this.isBatched = false;
        this.currentBatch = this;
        this.index = -1;
        this.stackedOff = [];

        /**
         @type {Array.<Array.<Task>>}
         */
        this.tasks = [];

        this.preventUndoRedo = false;

        this.undoBTN = $('.n2_ss_history_action--undo').on({
            click: this.undo.bind(this)
        });
        this.redoBTN = $('.n2_ss_history_action--redo').on({
            click: this.redo.bind(this)
        });
    }

    var history;

    _N2.r('documentReady', function () {
        history = new History();
    });

    /**
     * @returns {_N2.History}
     */
    History.get = function () {
        return history;
    };

    History.prototype.updateUI = function () {

        this.undoBTN.toggleClass('n2_ss_history_action--allowed', this.index !== 0 && this.tasks.length > 0);

        this.redoBTN.toggleClass('n2_ss_history_action--allowed', this.index > -1 && this.index < this.tasks.length);

        document.dispatchEvent(new CustomEvent('historyChange', {cancelable: false, bubbles: false}));
    };

    History.prototype.throttleUndoRedo = function () {
        if (!this.preventUndoRedo) {
            this.preventUndoRedo = true;
            setTimeout((function () {
                this.preventUndoRedo = false;
            }).bind(this), 100);
            return false;
        }
        return true;
    };

    History.prototype.isEnabled = function () {
        return this.enabled && this.historyAddAllowed;
    };

    /**
     *
     * @returns {Batch}
     */
    History.prototype.startBatch = function () {
        if (this.isEnabled()) {
            var batch = new Batch(this.currentBatch);
            this.currentBatch._add(batch);
            this.currentBatch = batch;
            return batch;
        }
        return false;
    };

    History.prototype.endBatch = function () {
        if (this.isEnabled()) {
            if (this.currentBatch.parent === undefined) {
                debugger;
            }
            this.currentBatch = this.currentBatch.parent;
        }
    };

    History.prototype.addControl = function (mode) {
        return this.currentBatch._add(new Control(mode));
    };

    History.prototype.addSimple = function (that, undoAction, redoAction, context) {
        if (this.isEnabled()) {
            return this.currentBatch._add(new Task(that, undoAction, redoAction, context));
        }
        return false;
    };

    /**
     *
     * @param that
     * @param action
     * @param context
     * @returns {TaskValue}
     */
    History.prototype.addValue = function (that, action, context) {
        if (this.isEnabled()) {
            if (this.isBatched || this.currentBatch !== this) {
                var currentBatch = this.getCurrentBatchStack();
                for (var i = 0; i < currentBatch.length; i++) {
                    if (currentBatch[i].isEqual(that, action, context)) {
                        currentBatch.push(currentBatch.splice(i, 1)[0]);
                        return currentBatch[currentBatch.length - 1];
                    }
                }
            }
            return this.currentBatch._add(new TaskValue(that, action, action, context));
        }
        return false;
    };

    History.prototype.getCurrentBatchStack = function () {
        if (this.currentBatch !== this) {
            return this.currentBatch.tasks;
        }
        return this.tasks[this.tasks.length - 1];
    };

    /**
     *
     * @param {Task} task
     * @returns {Task}
     */
    History.prototype._add = function (task) {
        if (this.index !== -1) {
            this.tasks.splice(this.index, this.tasks.length);
        }
        this.index = -1;
        if (!this.isBatched) {
            this.tasks.push([task]);
            this.isBatched = true;
            setTimeout((function () {
                this.isBatched = false;
            }).bind(this), 100);
        } else {
            this.tasks[this.tasks.length - 1].push(task);
        }
        if (this.tasks.length > this.historyStates) {
            this.tasks.shift();
        }
        this.updateUI();
        return task;
    };

    History.prototype.off = function () {
        this.historyAddAllowed = false;
        this.stackedOff.push(1);
    };

    History.prototype.on = function () {
        this.stackedOff.pop();
        if (this.stackedOff.length === 0) {
            this.historyAddAllowed = true;
        }
    };

    History.prototype.undo = function (e) {
        if (e) {
            e.preventDefault();
        }
        if (this.throttleUndoRedo()) {
            return false;
        }

        this.historyActionInProgress = true;
        this.off();
        if (this.index === -1) {
            this.index = this.tasks.length - 1;
        } else {
            this.index--;
        }
        if (this.index >= 0) {
            var tasks = this.tasks[this.index];

            for (var i = tasks.length - 1; i >= 0; i--) {
                if (!tasks[i].undo()) {
                    break;
                }
            }
        } else {
            this.index = 0;
            // No more undo
        }
        this.on();
        this.historyActionInProgress = false;

        this.updateUI();
        return true;
    };

    History.prototype.redo = function (e) {
        if (e) {
            e.preventDefault();
        }
        if (this.throttleUndoRedo()) {
            return false;
        }

        this.historyActionInProgress = true;
        this.off();
        if (this.index !== -1) {
            if (this.index < this.tasks.length) {
                var tasks = this.tasks[this.index];
                this.index++;
                for (var i = 0; i < tasks.length; i++) {
                    if (!tasks[i].redo()) {
                        break;
                    }
                }
            } else {
                // No more redo
            }
        } else {
            // No redo
        }
        this.on();
        this.historyActionInProgress = false;

        this.updateUI();
        return true;
    };

    History.prototype.actionInProgress = function () {
        return this.historyActionInProgress;
    };

    function Batch(parent) {
        this.parent = parent;
        this.tasks = [];
    }

    Batch.prototype._add = function (task) {
        this.tasks.push(task);
        return task;
    };

    Batch.prototype.undo = function () {
        for (var i = 0; i < this.tasks.length; i++) {
            if (!this.tasks[i].undo()) {
                break;
            }
        }
        return true;
    };

    Batch.prototype.redo = function () {
        for (var i = 0; i < this.tasks.length; i++) {
            if (!this.tasks[i].redo()) {
                break;
            }
        }
        return true;
    };

    Batch.prototype.isEqual = function () {
        return false;
    };

    function Control(mode) {
        switch (mode) {
            case 'skipForwardUndos':
                this.undo = function () {
                    return false;
                };
                break;
        }
    }


    Control.prototype.undo = function () {
        return true;
    };

    Control.prototype.redo = function () {
        return true;
    };

    Control.prototype.isEqual = function () {
        return false;
    };

    function Task(that, undoAction, redoAction, context) {
        this.that = that;
        this.undoAction = undoAction;
        this.redoAction = redoAction;
        this.context = context || [];
    }


    Task.prototype.undo = function () {
        this.undoAction.apply(this.that.getSelf(), this.context);
        return true;
    };

    Task.prototype.redo = function () {
        this.redoAction.apply(this.that.getSelf(), this.context);
        return true;
    };

    Task.prototype.isEqual = function () {
        return false;
    };

    function TaskValue() {
        Task.prototype.constructor.apply(this, arguments);
    }

    TaskValue.prototype = Object.create(Task.prototype);
    TaskValue.prototype.constructor = TaskValue;

    TaskValue.prototype.setValues = function (undoValue, redoValue) {
        this.undoValue = undoValue;
        this.redoValue = redoValue;
    };

    TaskValue.prototype.undo = function () {
        this.context.unshift(this.undoValue);
        this.undoAction.apply(this.that.getSelf(), this.context);
        this.context.shift();
        return true;
    };

    TaskValue.prototype.redo = function () {
        this.context.unshift(this.redoValue);
        this.redoAction.apply(this.that.getSelf(), this.context);
        this.context.shift();
        return true;
    };

    TaskValue.prototype.isEqual = function (that, action, context) {
        if (that === this.that && action === this.undoAction) {
            for (var i = 0; i < context.length; i++) {
                if (context[i] !== this.context[i]) {
                    return false;
                }
            }
            this.setValues = (function (undoValue, redoValue) {
                this.redoValue = redoValue;
            }).bind(this);
            return true;
        }
        return false;
    };

    return History;
});
_N2.d('InlineTextEditor', '$', function () {

    /**
     * @memberOf _N2
     *
     * @param {Node} el
     * @param {{}} options
     * @constructor
     */
    function InlineTextEditor(el, options) {
        this.destroyed = false;
        this.el = el;
        el.contentEditable = true;

        this.options = _Assign({
            onSave: function () {
            },
            onCancel: function () {
            },
            onTab: function (e) {
            },
            filter: false
        }, options);

        this.listeners = [];

        this.selectAll();

        this.addListener('blur', this.onBlur.bind(this), window);
        this.addListener('input', this.onInput.bind(this));
        this.addListener('blur', this.onBlur.bind(this));
        this.addListener('keydown', this.onKeydown.bind(this));
        this.addListener('mousedown', this.stopPropagation.bind(this));
        this.addListener('mouseup', this.stopPropagation.bind(this));
        this.addListener('click', this.stopPropagation.bind(this));
    }

    InlineTextEditor.prototype.addListener = function (eventName, callback, el) {
        el = el || this.el;
        el.addEventListener(eventName, callback);
        this.listeners.push({
            el: el,
            eventName: eventName,
            callback: callback
        });
    };

    InlineTextEditor.prototype.stopPropagation = function (e) {
        e.stopPropagation();
    };

    InlineTextEditor.prototype.onBlur = function () {
        this.destroy();

        var value = this.filterText(this.el.textContent);

        this.el.scrollLeft = 0;
        this.el.scrollTop = 0;

        this.options.onSave(value);
    };

    InlineTextEditor.prototype.cancel = function () {
        this.destroy();

        this.options.onCancel();
    };

    InlineTextEditor.prototype.destroy = function () {

        this.selectNone();

        for (var i = 0; i < this.listeners.length; i++) {
            this.listeners[i].el.removeEventListener(this.listeners[i].eventName, this.listeners[i].callback);
        }

        this.el.contentEditable = false;
    };

    InlineTextEditor.prototype.onKeydown = function (e) {
        if (!e.defaultPrevented) {
            if (e.code === 'Enter') {
                this.onBlur();
                e.preventDefault();
            } else if (e.code === 'Escape') {
                this.cancel();
                e.preventDefault();
            } else if (e.code === 'Tab') {
                this.onBlur();
                this.options.onTab(e);
                e.preventDefault();
            }
        }
    };

    function getRecursiveTextContent(el) {
        var parts = [];
        for (var i = 0; i < el.childNodes.length; i++) {
            if (el.childNodes[i].childNodes.length) {
                parts.push.apply(parts, getRecursiveTextContent(el.childNodes[i]));
            } else {
                parts.push(el.childNodes[i].textContent)
            }
        }

        return parts;
    }

    InlineTextEditor.prototype.onInput = function () {
        var rawContent = this.el.textContent,
            parts = getRecursiveTextContent(this.el);

        var content = this.filterText(parts.join(' '));

        if (this.el.innerText !== content) {
            var caretPosition = this.getCaretPosition(),
                length = rawContent.length;

            this.setCaretPosition(caretPosition - (length - content.length));
        }
    };

    InlineTextEditor.prototype.filterText = function (text) {
        text = text.replace(/[\s]/gi, ' ').replace(/\s+/g, ' ');

        if (this.options.filter) {
            text = this.options.filter(text);
        }

        return text;
    };

    InlineTextEditor.prototype.getCaretPosition = function () {
        var range = window.getSelection().getRangeAt(0),
            selected = range.toString().length,
            preCaretRange = range.cloneRange();
        preCaretRange.selectNodeContents(this.el);
        preCaretRange.setEnd(range.endContainer, range.endOffset);

        return preCaretRange.toString().length - selected;
    };

    InlineTextEditor.prototype.setCaretPosition = function (position) {
        position = Math.max(0, Math.min(this.el.textContent.length, position));
        var sel = window.getSelection(),
            range = document.createRange();
        range.setStart(this.el.childNodes.length ? this.el.childNodes[0] : this.el, position);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
    };

    InlineTextEditor.prototype.selectAll = function () {

        var element = this.el;
        if (this.el.childNodes.length) {
            element = this.el.childNodes[0]
        }
        var sel = window.getSelection(),
            range = document.createRange();
        range.setStart(element, 0);
        range.setEnd(element, this.el.textContent.length);
        sel.removeAllRanges();
        sel.addRange(range);
    };

    InlineTextEditor.prototype.selectNone = function () {
        window.getSelection().removeAllRanges();
    };

    return InlineTextEditor;
});_N2.d('SmartSliderAdminSlideBackgroundColor', ['SmartSliderSlideBackgroundColor'], function () {

    /**
     * @memberOf _N2
     *
     * @param {_N2.SmartSliderSlideBackground} background
     * @param el
     * @constructor
     * @augments _N2.SmartSliderSlideBackgroundColor
     */
    function SmartSliderAdminSlideBackgroundColor(background, el) {
        _N2.SmartSliderSlideBackgroundColor.prototype.constructor.apply(this, arguments);
    }

    SmartSliderAdminSlideBackgroundColor.prototype = Object.create(_N2.SmartSliderSlideBackgroundColor.prototype);
    SmartSliderAdminSlideBackgroundColor.prototype.constructor = SmartSliderAdminSlideBackgroundColor;

    SmartSliderAdminSlideBackgroundColor.prototype.update = function (color, gradient, colorEnd, isOverlay) {
        color = this.fixColor(color);

        _NodeSetAttribute(this.el, 'data-overlay', parseInt(isOverlay) ? 1 : 0);

        if (gradient !== 'off') {
            this.updateGradient(color, gradient, colorEnd)
        } else {
            this.updateColor(color);
        }
    };

    SmartSliderAdminSlideBackgroundColor.prototype.updateColor = function (color) {

        if (color.substr(6, 8) !== '00') {
            _NodeSetStyle(this.el, 'background', N2Color.hex2rgbaCSS(color));
        } else {
            _NodeRemoveStyle(this.el, 'background');
        }
    };

    SmartSliderAdminSlideBackgroundColor.prototype.updateGradient = function (color, gradient, colorEnd) {

        colorEnd = this.fixColor(colorEnd);

        switch (gradient) {
            case 'horizontal':
                _NodeSetStyle(this.el, 'background', 'linear-gradient(to right, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorEnd) + ' 100%)');
                break;
            case 'vertical':
                _NodeSetStyle(this.el, 'background', 'linear-gradient(to bottom, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorEnd) + ' 100%)');
                break;
            case 'diagonal1':
                _NodeSetStyle(this.el, 'background', 'linear-gradient(45deg, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorEnd) + ' 100%)');
                break;
            case 'diagonal2':
                _NodeSetStyle(this.el, 'background', 'linear-gradient(135deg, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorEnd) + ' 100%)');
                break;
            default:
                _NodeRemoveStyle(this.el, 'background');
                break;
        }
    };

    SmartSliderAdminSlideBackgroundColor.prototype.fixColor = function (color) {
        if (color.length && color.charAt(0) === '#') {
            color = color.substring(1);
            if (color.length === 6) {
                color += 'ff';
            }
        }
        return color;
    };

    return SmartSliderAdminSlideBackgroundColor;
});_N2.d('SmartSliderAdminSlideBackgroundImage', ['$', 'SmartSliderSlideBackgroundImage'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param {_N2.FrontendSliderSlide} slide
     * @param {_N2.SmartSliderBackgrounds} manager
     * @param {_N2.SmartSliderSlideBackground} background
     * @param {NodeList} imageElements
     * @constructor
     * @augments _N2.SmartSliderSlideBackgroundImage
     */
    function SmartSliderAdminSlideBackgroundImage(slide, manager, background, imageElements) {

        this.hash = _NodeGetData(imageElements[0], 'hash');

        this.src = {
            desktop: _NodeGetData(imageElements[0], 'srcDesktop'),
            tablet: _NodeGetData(imageElements[0], 'srcTablet'),
            mobile: _NodeGetData(imageElements[0], 'srcMobile')
        };

        _N2.SmartSliderSlideBackgroundImage.prototype.constructor.apply(this, arguments);

        this.listenImageManager();
    }

    SmartSliderAdminSlideBackgroundImage.prototype = Object.create(_N2.SmartSliderSlideBackgroundImage.prototype);
    SmartSliderAdminSlideBackgroundImage.prototype.constructor = SmartSliderAdminSlideBackgroundImage;


    SmartSliderAdminSlideBackgroundImage.prototype.listenImageManager = function () {

        if (this.hash !== '') {
            $(window).on(this.hash, this.onImageManagerChanged.bind(this));
        }
    };

    SmartSliderAdminSlideBackgroundImage.prototype.notListenImageManager = function () {

        if (this.hash !== '') {
            $(window).off(this.hash, null, this.onImageManagerChanged.bind(this));
        }
    };

    SmartSliderAdminSlideBackgroundImage.prototype.onImageManagerChanged = function (e, imageData) {

        this.src.tablet = imageData.tablet.image;
        this.src.mobile = imageData.mobile.image;

        this.onSlideDeviceChanged(this.manager.device);
    };

    SmartSliderAdminSlideBackgroundImage.prototype.preLoadAdmin = function (image) {

        this.src.desktop = _N2._imageHelper.fixed(image);

        this.setSrc(this.src.desktop);

        this.preLoad();
    };

    SmartSliderAdminSlideBackgroundImage.prototype.setDesktopSrc = function (src) {

        this.notListenImageManager();
        this.src.desktop = src;
        this.hash = md5(src);

        if (src !== '' && this.background.allowVisualLoad) {
            var img = new Image();
            img.addEventListener("load", (function () {
                $.when(_N2._imageManager.getVisual(src))
                    .done((function (visual) {
                        this.onImageManagerChanged(null, visual.value);
                        this.listenImageManager();
                    }).bind(this));
            }).bind(this), false);

            img.src = _N2._imageHelper.fixed(src);

        } else {

            this.src = {
                desktop: _N2._imageHelper.fixed(src),
                tablet: '',
                mobile: ''
            };

            this.setSrc(_N2._imageHelper.fixed(src));
        }
    };

    SmartSliderAdminSlideBackgroundImage.prototype.setSrc = function (src) {

        src = _N2._imageHelper.fixed(src);

        for (var i = 0; i < this.imageElements.length; i++) {
            _NodeSetAttribute(this.imageElements[i].querySelector('img'), 'src', src);
        }

        if (src === '') {
            this.onImageLoaded();
        }
    };

    SmartSliderAdminSlideBackgroundImage.prototype.onSlideDeviceChanged = function (device) {

        var newSrc = this.src.desktop;
        if (device === 'mobilePortrait' || device === 'mobileLandscape') {
            if (this.src.mobile) {
                newSrc = this.src.mobile;
            } else if (this.src.tablet) {
                newSrc = this.src.tablet;
            }
        } else if (device === 'tabletPortrait' || device === 'tabletLandscape') {
            if (this.src.tablet) {
                newSrc = this.src.tablet;
            }
        }
        if (newSrc) {
            this.setSrc(newSrc);
        } else {
            this.setSrc('');
        }
    };

    SmartSliderAdminSlideBackgroundImage.prototype.startFixed = function () {

    };

    SmartSliderAdminSlideBackgroundImage.prototype.updateMode = function (newMode, oldMode) {
        if (newMode === 'blurfit') {
            if (this.imageElements.length === 1) {
                // Clone image and use in the back
                var imageElement = this.imageElements[0].cloneNode(true);
                this.imageElements[0].parentNode.insertBefore(imageElement, this.imageElements[0]);
                this.imageElements.unshift(imageElement);
            }
            let blur;
            if (this.blurFitMode) {
                blur = this.globalBlurFit;
            } else {
                blur = (this.editorBlur) ? this.editorBlur : 7;
            }
            _NodeSetStyles(this.imageElements[0], {
                margin: -blur * 2 + 'px',
                padding: blur * 2 + 'px'
            });
            _NodeSetStyle(this.imageElements[0].children[0], 'filter', 'blur(' + blur + 'px)');
        }

        if (oldMode === 'blurfit' && newMode !== 'blurfit') {

            // Remove back image
            _NodeRemove(this.imageElements.shift());
        }
    };

    SmartSliderAdminSlideBackgroundImage.prototype.updateFocus = function (x, y) {

        _NodeListSetStyles(this.imageElements, {
            '--ss-o-pos-x': x + '%',
            '--ss-o-pos-y': y + '%'
        });
    };

    SmartSliderAdminSlideBackgroundImage.prototype.updateOpacity = function (opacity) {

        _NodeListSetStyle(this.imageElements, 'opacity', opacity / 100);
    };


    SmartSliderAdminSlideBackgroundImage.prototype.updateBlur = function (blur, blurFit = false) {

        let imageElement;
        if (blurFit) {
            imageElement = this.imageElements[0];
        } else {
            imageElement = this.imageElements[this.imageElements.length - 1];
        }

        if (blur > 0) {
            _NodeSetStyles(imageElement, {
                margin: '-' + (blur * 2) + 'px',
                padding: (blur * 2) + 'px'
            });

            _NodeSetStyle(imageElement.children[0], 'filter', 'blur(' + blur + 'px)');
            if (blurFit) {
                this.editorBlur = blur;
            }
        } else {
            _NodeRemoveStyles(imageElement, ['margin', 'padding']);
            _NodeRemoveStyle(imageElement.children[0], 'filter');
        }

        this.blur = blur;
    };

    SmartSliderAdminSlideBackgroundImage.prototype.kill = function () {
        this.notListenImageManager();
        _NodeListRemove(this.imageElements);
    };

    return SmartSliderAdminSlideBackgroundImage;
});_N2.d('LayerContainer', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param component
     * @constructor
     */
    function LayerContainer(component) {

        /**
         * @type {_N2.ComponentAbstract}
         */
        this.component = component;

        this.$ul = $('<div class="n2_layer_navigation_list_layer__children"></div>')
            .data('container', this);

        this.childrenSelector = '> .n2-ss-layer';

        this.layerContainerElement = component.layer;
    }

    LayerContainer.prototype.setLayerContainerElement = function ($layerContainerElement) {
        this.layerContainerElement = $layerContainerElement;
    };

    LayerContainer.prototype.startWithExistingNodes = function (isEditorStart) {
        var nodes = this.layerContainerElement.find(this.childrenSelector);
        for (var i = 0; i < nodes.length; i++) {
            this._loadNode(nodes.eq(i), isEditorStart);
        }
        this.component.onChildCountChange();
    };

    LayerContainer.prototype._loadNode = function ($el, isEditorStart) {
        var type = $el.data('sstype');
        /** @type {_N2.ComponentAbstract} */
        var component;
        switch (type) {
            case 'content':
                component = new _N2.Content(this.component.fragmentEditor, this.component);
                break;
            case 'layer':
                component = new _N2.Layer(this.component.fragmentEditor, this.component);
                break;
            case 'row':
                component = new _N2.Row(this.component.fragmentEditor, this.component);
                break;
            case 'col':
                component = new _N2.Col(this.component.fragmentEditor, this.component);
                break;
        }

        if (component) {
            component.load($el, isEditorStart);
            if (!isEditorStart) {
                component.sync();
            }
            return component;
        }
        return false;
    };

    LayerContainer.prototype.getLayerCount = function (sstype) {
        if (sstype !== undefined) {
            this.childrenSelector += '[data-sstype="' + sstype + '"]';
        }

        return this.layerContainerElement.find(this.childrenSelector).length;
    };

    LayerContainer.prototype.getLayerIndex = function ($layer) {
        return this.layerContainerElement.find(this.childrenSelector).index($layer);
    };

    /**
     *
     * @returns {_N2.ComponentAbstract[]}
     */
    LayerContainer.prototype.getSortedLayers = function () {
        var layers = [];
        this.layerContainerElement.find(this.childrenSelector).each(function (i, el) {
            var layer = $(el).data('layerObject');
            if (layer !== undefined) {
                layers.push(layer);
            }
        });
        return layers;
    };

    LayerContainer.prototype.append = function ($layer) {
        $layer.appendTo(this.layerContainerElement);
        var layer = this._loadNode($layer, false);
        this.component.onChildCountChange();
        return layer;
    };

    LayerContainer.prototype.insertAt = function ($layer, index) {

        var layers = this.getSortedLayers();
        if (index >= layers.length) {
            $layer.appendTo(this.layerContainerElement);
        } else {
            $layer.insertBefore(layers[index].layer);
        }

        var layer = this._loadNode($layer, false);
        this.component.onChildCountChange();
        return layer;
    };

    LayerContainer.prototype.insert = function (layer) {
        layer.getRootElement().appendTo(this.layerContainerElement);
    };

    /**
     *
     * @param {_N2.ComponentAbstract} layer
     * @param {int} index
     */
    LayerContainer.prototype.insertLayerAt = function (layer, index) {

        var layers = this.getSortedLayers(),
            oldGroup = layer.group;

        var layerIndex = layers.indexOf(layer);
        if (layerIndex > -1 && layerIndex < index) {
            // we have to readjust the target index of the layer
            index++;
        }

        if (index >= layers.length) {
            layer.getRootElement().appendTo(this.layerContainerElement);
        } else {
            layer.getRootElement().insertBefore(layers[index].getRootElement());
        }

        this.syncLayerRow(layer);

        if (oldGroup !== this.component) {
            oldGroup.onChildCountChange();

            /**
             * Notify other absolute component which was the child component of this component,
             * that the link is not available anymore as they to do not share the same group.
             */
            layer.layer.triggerHandler('LayerAbsoluteUnavailable');
        }

        oldGroup.triggerLayerResized();
        this.component.triggerLayerResized();

    };

    LayerContainer.prototype.syncLayerRow = function (layer) {
        var relatedLayer,
            isReversed = (this.component.type === 'slide');
        if (isReversed) {
            relatedLayer = layer.getRootElement().prevAll('.n2-ss-layer').first().data('layerObject');
        } else {
            relatedLayer = layer.getRootElement().nextAll('.n2-ss-layer').first().data('layerObject');
        }

        if (relatedLayer !== undefined) {
            layer.layerRow.insertBefore(relatedLayer.layerRow);
        } else {
            this.$ul.append(layer.layerRow);
        }

        if (layer.animations) {
            layer.animations.syncRow(relatedLayer, isReversed);
        }
    };

    LayerContainer.prototype.getChildLayersRecursive = function (nodeOnly) {
        var _layers = this.getSortedLayers();
        var layers = [];
        for (var i = 0; i < _layers.length; i++) {
            if (nodeOnly) {
                layers.push(_layers[i].layer[0]);
            } else {
                layers.push(_layers[i]);
            }
            if (_layers[i].container) {
                layers.push.apply(layers, _layers[i].container.getChildLayersRecursive(nodeOnly));
            }
        }
        return layers;
    };

    /**
     *
     * @param layers
     * @param {array} [newLocalIndexes]
     */
    LayerContainer.prototype.moveLayersToGroup = function (layers, newLocalIndexes) {
        newLocalIndexes = newLocalIndexes || [];

        var originalGroups = [];
        for (var i = 0; i < layers.length; i++) {
            var layer = layers[i],
                originalGroup = layer.group;

            var originalIndex = layer.getIndex();
            if (newLocalIndexes[i] !== undefined) {
                this.insertLayerAt(layer, newLocalIndexes[i]);
            } else {
                this.insert(layer);
            }
            layer.changeGroup(originalIndex, this.component);

            if (this !== originalGroup) {
                if (originalGroups.indexOf(originalGroup) === -1) {
                    originalGroups.push(originalGroup);
                }
            }
        }

        for (var i = 0; i < originalGroups.length; i++) {
            originalGroups[i].update();
        }
    };

    LayerContainer.prototype.activateFirst = function () {
        var layers = this.getSortedLayers();
        if (layers.length > 0) {
            layers[layers.length - 1].activate(); //Do not show editor on load!
        }
    };

    LayerContainer.prototype.resetModes = function (mode) {
        var layers = this.getSortedLayers();
        for (var i = 0; i < layers.length; i++) {
            layers[i].resetMode(mode);
            if (layers[i].container !== undefined) {
                layers[i].container.resetModes(mode);
            }
        }
    };

    LayerContainer.prototype.copyModes = function (mode, currentMode) {
        var layers = this.getSortedLayers();
        for (var i = 0; i < layers.length; i++) {
            layers[i].copyMode(mode, currentMode);
            if (layers[i].container !== undefined) {
                layers[i].container.copyModes(mode, currentMode);
            }
        }
    };

    LayerContainer.prototype.changeEditorModes = function (mode) {
        var layers = this.getSortedLayers();
        for (var i = 0; i < layers.length; i++) {
            layers[i].changeEditorMode(mode);
            if (layers[i].container !== undefined) {
                layers[i].container.changeEditorModes(mode);
            }
        }
    };

    LayerContainer.prototype.renderModeProperties = function () {
        var layers = this.getSortedLayers();
        for (var i = 0; i < layers.length; i++) {
            layers[i].renderModeProperties();
            if (layers[i].container !== undefined) {
                layers[i].container.renderModeProperties();
            }
        }
    };

    LayerContainer.prototype.getAllLayers = function (layers) {
        layers = layers || [];
        var sortedLayers = this.getSortedLayers();
        for (var i = 0; i < sortedLayers.length; i++) {
            layers.push(sortedLayers[i]);
            if (sortedLayers[i].container !== undefined) {
                sortedLayers[i].container.getAllLayers(layers);
            }
        }
        return layers;
    };

    LayerContainer.prototype.getData = function (params) {
        params = _Assign({
            layersIncluded: true,
            itemsIncluded: true
        }, params);
        var layers = [];

        var sortedLayers = this.getSortedLayers();
        if (this.component.type === 'slide') {
            for (var i = sortedLayers.length - 1; i >= 0; i--) {
                layers.push(sortedLayers[i].getData(params));
            }
        } else {
            for (var i = 0; i < sortedLayers.length; i++) {
                layers.push(sortedLayers[i].getData(params));
            }
        }

        return layers;
    };

    LayerContainer.prototype.getHTML = function (base64) {
        var layers = this.getSortedLayers(),
            nodes = [];
        for (var i = 0; i < layers.length; i++) {
            nodes.push(layers[i].getHTML(base64));
        }
        return nodes;
    };

    /**
     * Used for layer editor
     * @param exclude
     * @returns {[]}
     */
    LayerContainer.prototype.getDroppables = function (exclude) {
        var droppables = [],
            layers = this.getSortedLayers();

        for (var i = 0; i < layers.length; i++) {
            if (layers[i] !== exclude) {
                var droppable = layers[i].getDroppable();
                if (typeof droppable == 'object') {
                    droppables.push(droppable);
                }
                if (droppable !== 'hidden' && layers[i].container) {
                    droppables.push.apply(droppables, layers[i].container.getDroppables(exclude));
                }
            }
        }

        return droppables;
    };

    /**
     * Used for Layer List
     * @param layer
     * @returns {[]}
     */
    LayerContainer.prototype.getLLDroppables = function (layer) {
        var droppables = [];

        var droppable = this.component.getLLDroppable(layer);
        if (droppable) {
            droppables.push(droppable);
        }

        var layers = this.getSortedLayers();
        for (var i = 0; i < layers.length; i++) {
            if (!layers[i].container || layers[i] === layer) continue;

            droppables.push.apply(droppables, layers[i].container.getLLDroppables(layer));
        }

        return droppables;
    };

    return LayerContainer;
});_N2.d('EditorContextualBarAbstract', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param {_N2.EditorInteractionManager} interactionManager
     *
     * @abstract
     * @constructor
     */
    function EditorContextualBarAbstract(interactionManager) {
        /**
         * @type {_N2.EditorInteractionManager}
         */
        this.interactionManager = interactionManager;

        this.forceEnabledCounter = 0;

        this.disabledCounter = 1;

        /**
         * @type {_N2.ComponentAbstract|boolean}
         */
        this.component = false;

        this.___positionBar = this.___positionBar.bind(this);

        this.$contextual = $('<div class="n2_layer_contextual"></div>')
            .on({
                contextmenu: this.interactionManager.handleContextMenu.bind(this.interactionManager)
            })
            .appendTo(this.interactionManager.$containerInner);

        var $contextualInner = $('<div class="n2_layer_contextual__inner"></div>')
            .appendTo(this.$contextual);

        this.$contextualLabel = $('<div class="n2_layer_contextual__label"></i></div>')
            .appendTo($contextualInner);

        this.$buttonResponsiveTools = $('<div class="n2_layer_contextual__button n2_layer_contextual__button--responsive-tools"><i class="ssi_16 ssi_16--fontresize"></i></div>')
            .appendTo($contextualInner);

        this.$buttonMore = $('<div class="n2_layer_contextual__button"><i class="ssi_16 ssi_16--more"></i></div>')
            .appendTo($contextualInner);

    }

    EditorContextualBarAbstract.prototype.enableActiveForced = function () {
        this.forceEnabledCounter++;
        this._enable();
    };

    EditorContextualBarAbstract.prototype.disableActiveForced = function () {
        this.forceEnabledCounter = Math.max(0, this.forceEnabledCounter - 1);
        this._disable();
    };

    EditorContextualBarAbstract.prototype.disable = function () {
        this.disabledCounter++;
        this._disable();
    };

    EditorContextualBarAbstract.prototype._disable = function () {
        if (this.forceEnabledCounter === 0 && this.disabledCounter === 1) {
            this.blurComponent();
        }
    };

    EditorContextualBarAbstract.prototype.enable = function () {
        this.disabledCounter = Math.max(0, this.disabledCounter - 1);
        this._enable();
    };

    EditorContextualBarAbstract.prototype._enable = function () {
        if (this.forceEnabledCounter > 0 || this.disabledCounter === 0) {
            this.focusComponent();
        }
    };

    EditorContextualBarAbstract.prototype.refreshPosition = function () {
        if (((this.interactionManager.isHover && this.disabledCounter === 0) || this.forceEnabledCounter > 0)) {
            this.positionBar();
        }
    };

    EditorContextualBarAbstract.prototype.positionBar = function () {

        this.___positionBar();
        _N2.___Ticker.add(this.___positionBar);
    };

    EditorContextualBarAbstract.prototype.___positionBar = function () {
        if (this.component && !this.component.isDeleted) {
            var rect = this.component.layer[0].getBoundingClientRect(),
                position = {
                    top: rect.top - this.interactionManager.innerOffset.top + 'px'
                };

            if (parseInt(position.top) < 28) {
                position.top = parseInt(position.top) + 28 + 'px';
                this.$contextual.addClass('n2_layer_contextual--invert');
            } else {
                this.$contextual.removeClass('n2_layer_contextual--invert');
            }

            if (this.component.contextualBarPosition === 'left') {
                position.left = rect.left - this.interactionManager.innerOffset.left + 'px';
            } else if (this.component.contextualBarPosition === 'center') {
                position.left = Math.floor(rect.left - this.interactionManager.innerOffset.left + rect.width / 2 - this.$contextual.width() / 2) + 'px';
            }

            this.$contextual.css(position);

        }

        _N2.___Ticker.remove(this.___positionBar);
    }

    /**
     *
     * @param {MouseEvent} e
     */
    EditorContextualBarAbstract.prototype.onProxyMouseDown = function (e) {
        if (this.component) {
            e = e.originalEvent;
            this.component.layer[0].dispatchEvent(new MouseEvent('mousedown', {
                bubbles: true,
                cancelable: true,
                screenX: e.screenX,
                screenY: e.screenY,
                clientX: e.clientX,
                clientY: e.clientY,
                button: e.button,
                buttons: e.buttons
            }));
        }
    };

    return EditorContextualBarAbstract;
});_N2.d('EditorContextualBarActive', ['$', 'EditorContextualBarAbstract'], function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.EditorInteractionManager} interactionManager
     * @constructor
     * @augments _N2.EditorContextualBarAbstract
     */
    function EditorContextualBarActive(interactionManager) {

        this.contextualMenuActive = false;
        this.responsiveToolsActive = false;

        _N2.EditorContextualBarAbstract.prototype.constructor.call(this, interactionManager);

        this.$contextual.addClass('n2_layer_contextual--active')
            .on('mousedown', this.onProxyMouseDown.bind(this));

        this.$contextualLabel.on('click', this.actionDisplayLayerWindow.bind(this));
        this.$buttonResponsiveTools.on('click', this.actionResponsiveTools.bind(this));
        this.$buttonMore.on('click', this.actionContextualMenu.bind(this));

    }

    EditorContextualBarActive.prototype = Object.create(_N2.EditorContextualBarAbstract.prototype);
    EditorContextualBarActive.prototype.constructor = EditorContextualBarActive;

    /**
     * @param {_N2.ComponentAbstract} component
     */
    EditorContextualBarActive.prototype.changeActiveComponent = function (component) {

        this.blurComponent();

        /**
         * @type {_N2.ComponentAbstract}
         */
        this.component = component;

        this.$contextualLabel.text(this.component.getUILabel());

        this.$contextual
            .data('layerObject', this.component)
            .toggleClass('n2_layer_contextual--has-responsive-tools', this.component.hasResponsiveTools());

        this.focusComponent();

        if (this.component.hasResponsiveTools() && this.responsiveToolsActive) {
            this.showResponsiveTools();
        }
    };

    EditorContextualBarActive.prototype.blurComponent = function () {
        if (this.component && !this.component.isDeleteStarted) {

            this.$contextual.removeClass('n2_layer_contextual--visible');
            this.component.layer.removeClass('n2_layer_contextual_active_layer');

            if (this.component.editorUI.normalSizing) {
                this.component.editorUI.normalSizing.removeClass('nui-enabled')
            }

            if (this.component.editorUI.margin) {
                this.component.editorUI.margin.removeClass('nui-enabled')
            }

            if (this.component.editorUI.padding) {
                this.component.editorUI.padding.removeClass('nui-enabled')
            }

            if (this.component.editorUI.absoluteCC) {
                this.component.editorUI.absoluteCC.removeClass('nui-enabled')
            }

            if (this.component.editorUI.resize) {
                this.component.editorUI.resize.removeClass('nui-enabled')
            }

            if (this.component.editorUI.columns) {
                this.component.editorUI.columns.hideHandles();
            }

            this.component.layer.off('.contextualBarActive');
        }
    };

    EditorContextualBarActive.prototype.focusComponent = function () {
        if ((this.disabledCounter === 0 || this.forceEnabledCounter > 0) && !this.component.isDeleted) {

            this.positionBar();

            var color = 'blue';
            if (this.component.isAbsolute()) {
                color = 'purple';
            }

            this.$contextual
                .attr('data-color', color)
                .addClass('n2_layer_contextual--visible');
            this.component.layer.addClass('n2_layer_contextual_active_layer');

            if (this.component.editorUI.normalSizing) {
                this.component.editorUI.normalSizing.addClass('nui-enabled');
            }

            if (this.component.editorUI.margin) {
                this.component.editorUI.margin.addClass('nui-enabled');
            }

            if (this.component.editorUI.padding) {
                this.component.editorUI.padding.addClass('nui-enabled');
            }

            if (this.component.editorUI.absoluteCC) {
                this.component.editorUI.absoluteCC.addClass('nui-enabled');
            }

            if (this.component.editorUI.resize) {
                this.component.editorUI.resize.addClass('nui-enabled');
            }

            if (this.component.editorUI.columns) {
                this.component.editorUI.columns.showHandles();
            }

            this.component.layer.on({
                'LayerDeleted.contextualBarActive': this.onLayerDelete.bind(this),
                'interaction.contextualBarActive': this.onLayerChange.bind(this),
                'LayerShowChange.contextualBarActive': this.onLayerChange.bind(this)
            });
        }
    };

    EditorContextualBarActive.prototype.onLayerDelete = function () {
        this.$contextual.removeClass('n2_layer_contextual--visible');
    };

    EditorContextualBarActive.prototype.onLayerChange = function () {
        this.refreshPosition();
    };

    EditorContextualBarActive.prototype.actionDisplayLayerWindow = function () {
        this.interactionManager.fragmentEditor.layerWindow.show(this.component);
    };

    EditorContextualBarActive.prototype.actionResponsiveTools = function () {
        if (this.responsiveToolsActive) {
            this.hideResponsiveTools();
        } else {
            this.showResponsiveTools();
        }

    };

    EditorContextualBarActive.prototype.showResponsiveTools = function () {
        if (this.hideResponsiveToolsTimeout) {
            clearTimeout(this.hideResponsiveToolsTimeout);
        }

        if (!this.$buttonResponsiveTools.hasClass('n2_layer_contextual__button--active')) {

            this.responsiveToolsActive = true;

            this.$buttonResponsiveTools.addClass('n2_layer_contextual__button--active');

            this.component.onContextResponsiveTools(this.$contextual[0], (function () {
                this.$buttonResponsiveTools.removeClass('n2_layer_contextual__button--active');

                this.hideResponsiveToolsTimeout = setTimeout(this.hideResponsiveTools.bind(this), 300);
            }).bind(this));
        }
    };

    EditorContextualBarActive.prototype.hideResponsiveTools = function () {
        this.responsiveToolsActive = false;
        this.$buttonResponsiveTools.removeClass('n2_layer_contextual__button--active');
    };

    EditorContextualBarActive.prototype.actionContextualMenu = function () {

        if (this.contextualMenuActive) {
            this.hideContextualMenu();
        } else {
            this.showContextualMenu();
        }
    };

    EditorContextualBarActive.prototype.showContextualMenu = function (force) {

        if (this.hideContextualMenuTimeout) {
            clearTimeout(this.hideContextualMenuTimeout);
        }


        if (force || !this.$buttonMore.hasClass('n2_layer_contextual__button--active')) {

            this.contextualMenuActive = true;

            this.$buttonMore.addClass('n2_layer_contextual__button--active');

            this.component.onContextMenu(this.$contextual[0], {
                onClose: (function () {

                    this.hideContextualMenuTimeout = setTimeout(this.hideContextualMenu.bind(this), 300);

                }).bind(this)
            });
        }
    }

    EditorContextualBarActive.prototype.hideContextualMenu = function () {
        this.contextualMenuActive = false;
        this.$buttonMore.removeClass('n2_layer_contextual__button--active');
    };

    return EditorContextualBarActive;
});_N2.d('EditorContextualBarHoverSection', ['$', 'EditorContextualBarHover'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.EditorInteractionManager} interactionManager
     * @constructor
     * @augments _N2.EditorContextualBarHover
     */
    function EditorContextualBarHoverSection(interactionManager) {

        _N2.EditorContextualBarHover.prototype.constructor.call(this, interactionManager);

        this.$contextual.addClass('n2_layer_contextual--hover-section');
    }

    EditorContextualBarHoverSection.prototype = Object.create(_N2.EditorContextualBarHover.prototype);
    EditorContextualBarHoverSection.prototype.constructor = EditorContextualBarHoverSection;

    EditorContextualBarHoverSection.prototype.handleMouseOver = function (e) {

        this.processHoveredElement(this.findRelatedSection(e.target));
    };

    EditorContextualBarHoverSection.prototype.processMouseCoordinates = function (clientX, clientY) {
        this.processHoveredElement(this.findRelatedSection(document.elementFromPoint(clientX, clientY)));
    };

    /**
     *
     * @param target
     * @returns {boolean|_N2.ComponentAbstract}
     */
    EditorContextualBarHoverSection.prototype.findRelatedSection = function (target) {
        var $target = $(target),
            $component;
        if ($target.attr('data-sstype') === 'slide') {
            $component = $target;
        } else {
            $component = $target.closest('.n2-ss-layer[data-sstype="slide"]');
            if (!$component.length) {
                var $targetContextual;
                if ($target.hasClass('n2_layer_contextual')) {
                    $targetContextual = $target;
                } else {
                    $targetContextual = $target.closest('.n2_layer_contextual');
                }

                if (!$targetContextual.length) {
                    /**
                     * Target section not found, mouse is out of the editor area. Return the last hovered section.
                     */
                    if (this.component) {
                        return this.component;
                    }

                    return false;
                }

                /**
                 * Find the closest section near to the hovered contextual bar
                 */
                return this.findRelatedSection($targetContextual.data('layerObject').layer[0]);
            }
        }
        if ($component !== undefined) {
            return $component.data('layerObject');
        }

        return false;
    };

    EditorContextualBarHoverSection.prototype.blurComponent = function () {

        this.$contextual.removeClass('n2_layer_contextual--visible');
    };

    EditorContextualBarHoverSection.prototype.focusComponent = function () {

        if (this.disabledCounter === 0 && this.component) {
            this.positionBar();

            this.refreshActiveState();

            this.$contextual
                .attr('data-color', 'grey')
                .addClass('n2_layer_contextual--visible');
        }
    };

    return EditorContextualBarHoverSection;
});_N2.d('EditorContextualBarHover', ['$', 'EditorContextualBarAbstract'], function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.EditorInteractionManager} interactionManager
     * @constructor
     * @augments _N2.EditorContextualBarAbstract
     */
    function EditorContextualBarHover(interactionManager) {

        this.namespace = this.namespace || 'contextualBarHover';

        _N2.EditorContextualBarAbstract.prototype.constructor.call(this, interactionManager);

        this.$contextual.addClass('n2_layer_contextual--hover')
            .on('mousedown', this.onProxyMouseDown.bind(this));

        this.$contextualLabel.on('click', this.actionActivate.bind(this));
        this.$buttonResponsiveTools.on('click', this.actionActivateWithResponsiveTools.bind(this));
        this.$buttonMore.on('click', this.actionActivateWithContextualMenu.bind(this));

        this.interactionManager.$container[0].addEventListener('mouseover', this.handleMouseOver.bind(this), {
            passive: true,
            capture: true
        });
    }

    EditorContextualBarHover.prototype = Object.create(_N2.EditorContextualBarAbstract.prototype);
    EditorContextualBarHover.prototype.constructor = EditorContextualBarHover;

    EditorContextualBarHover.prototype.handleMouseOver = function (e) {

        this.processHoveredElement(this.interactionManager.findRelatedComponent(e.target));
    };

    EditorContextualBarHover.prototype.processMouseCoordinates = function (clientX, clientY) {
        this.processHoveredElement(this.interactionManager.findRelatedComponent(document.elementFromPoint(clientX, clientY)));
    };

    /**
     *
     * @param {_N2.ComponentAbstract|bool} nextLayerUnderMouse
     */
    EditorContextualBarHover.prototype.processHoveredElement = function (nextLayerUnderMouse) {
        if (nextLayerUnderMouse !== this.component) {

            this.blurComponent();

            this.component = nextLayerUnderMouse;

            if (this.component) {

                this.$contextualLabel.text(this.component.getUILabel());

                this.$contextual.data('layerObject', this.component)
                    .toggleClass('n2_layer_contextual--has-responsive-tools', this.component.hasResponsiveTools());

                this.focusComponent();
            }
        }
    };

    EditorContextualBarHover.prototype.blurComponent = function () {

        this.$contextual.removeClass('n2_layer_contextual--visible');

        if (this.component && !this.component.isDeleteStarted) {
            this.component.layer.removeClass('n2_layer_contextual_hover_layer');

            this.component.layer.off('interaction.' + this.namespace);

            this.component.markOut();
        }
    };

    EditorContextualBarHover.prototype.focusComponent = function () {

        if ((this.disabledCounter === 0 || this.forceEnabledCounter > 0) && this.component && !this.component.isDeleted) {
            this.positionBar();

            this.refreshActiveState();

            this.component.layer.addClass('n2_layer_contextual_hover_layer');

            var color = 'grey';
            if (this.component.isAbsolute()) {
                color = 'purple';
            }

            this.$contextual
                .attr('data-color', color)
                .addClass('n2_layer_contextual--visible');

            this.component.layer.on('LayerDeleted.' + this.namespace, this.onLayerDelete.bind(this));
            this.component.layer.on('interaction.' + this.namespace, this.onLayerInteraction.bind(this));

            this.component.markOver();
        }
    };

    EditorContextualBarHover.prototype.onLayerDelete = function () {
        this.$contextual.removeClass('n2_layer_contextual--visible');
    };

    EditorContextualBarHover.prototype.onLayerInteraction = function () {
        this.refreshPosition();
    };

    EditorContextualBarHover.prototype.actionActivate = function (e) {
        this.component.activate(e);
    };

    EditorContextualBarHover.prototype.actionActivateWithResponsiveTools = function (e) {
        this.component.activate(false);

        this.interactionManager.contextualBarActive.showResponsiveTools();
    };

    EditorContextualBarHover.prototype.actionActivateWithContextualMenu = function (e) {
        if (this.interactionManager.contextualBarActive.responsiveToolsActive) {
            this.interactionManager.contextualBarActive.hideResponsiveTools();
        }

        this.component.activate(false);

        this.interactionManager.contextualBarActive.showContextualMenu(true);
    };

    EditorContextualBarHover.prototype.refreshActiveState = function () {
        this.$contextual.toggleClass('n2_layer_contextual--hover-active', this.interactionManager.activeComponent === this.component);
    }

    return EditorContextualBarHover;
});
_N2.d('LayerDataStorage', function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function LayerDataStorage() {
        this.isDeviceProp = {};
        this.propertyScope = {};

        /**
         * @type {{}}
         */
        this.property = {};

        /**
         *
         * @type {{mobileLandscape: {}, mobilePortrait: {}, desktopPortrait: {}, tabletLandscape: {}, desktopLandscape: {}, tabletPortrait: {}}}
         */
        this.deviceProperty = {
            desktopPortrait: {},
            desktopLandscape: {},
            tabletPortrait: {},
            tabletLandscape: {},
            mobilePortrait: {},
            mobileLandscape: {}
        };

        this.advancedProperties = {};
    }

    LayerDataStorage.prototype.getMode = function () {
        return this.fragmentEditor.getMode();
    };

    LayerDataStorage.prototype.getProperties = function () {
        var properties = {};
        for (var k in this.property) {
            if (this.advancedProperties[k] !== undefined) {
                if (k === this.advancedProperties[k].getName()) {
                    var value = this.property[k],
                        baseName = this.advancedProperties[k].getBaseName();
                    if (this.property[k] === undefined) {
                        value = this.property[baseName];
                    }
                    properties[baseName] = value;
                }
            } else {
                properties[k] = this.property[k];
            }
        }
        return properties;
    };

    LayerDataStorage.prototype.getRawProperty = function (name) {

        if (this.isDeviceProp[name]) {
            var mode = this.getMode(),
                properties = this.deviceProperty[mode];
            if (properties[name] !== undefined) {
                return properties[name];
            }
            return undefined;
        }
        return this.property[name];
    };

    LayerDataStorage.prototype.getProperty = function (name) {

        if (this.isDeviceProp[name]) {
            var mode = this.getMode(),
                properties = this.deviceProperty[mode],
                fallbackProperties = this.deviceProperty['desktopPortrait'];
            if (properties[name] !== undefined) {
                return properties[name];
            } else if (fallbackProperties[name] !== undefined) {
                return fallbackProperties[name];
            }
        }
        return this.property[name];
    };

    LayerDataStorage.prototype.historyStore = function (value, name, mode) {
        if (!this.isDeleteStarted) {

            this.store(mode, name, value, true, 'history');

            if (!this.isDeviceProp[name] || mode === this.getMode()) {
                this.$.trigger('propertyChanged', [name, this.getProperty(name)]);
            }
        }
    };

    LayerDataStorage.prototype.store = function (mode, name, value, needRender, from) {
        mode = mode || this.getMode();

        var oldValue,
            oldValueFilled;
        if (this.isDeviceProp[name]) {
            oldValue = this.deviceProperty[mode][name];
            oldValueFilled = this.getProperty(name);
        } else {
            oldValueFilled = oldValue = this.property[name];
        }
        var task = _N2.History.get().addValue(this, this.historyStore, [name, mode]);
        if (task) {
            task.setValues(oldValue, value);
        }

        this.property[name] = value;

        if (this.isDeviceProp[name]) {
            this.deviceProperty[mode][name] = value;
        }

        if (needRender) {
            this.render(name, oldValueFilled, from);
        }
    };

    LayerDataStorage.prototype.render = function (name, oldValue, from) {
        if (this.propertyScope[name]['_sync' + name] !== undefined) {
            this.propertyScope[name]['_sync' + name](oldValue, from);
        }
    };

    LayerDataStorage.prototype.isDimensionPropertyAccepted = function (value) {
        return (value + '').match(/[0-9]+%/) || value === 'auto';
    };

    LayerDataStorage.prototype.changeEditorMode = function (mode) {
        var value = parseInt(this.property[mode]);

        this.layer.triggerHandler('LayerShowChange', [mode, value]);

        this.renderModeProperties(false);
    };

    LayerDataStorage.prototype.renderModeProperties = function (isReset) {
        for (var k in this.isDeviceProp) {
            if (this.isDeviceProp[k]) {
                this.property[k] = this.getProperty(k);
                this.$.trigger('propertyChanged', [k, this.property[k]]);
            }
        }
    };

    LayerDataStorage.prototype.historyResetMode = function (value, mode) {

        this.deviceProperty[mode] = _Assign({}, value);

        if (mode === this.fragmentEditor.getMode()) {
            this.renderModeProperties(true);
        }
    };

    LayerDataStorage.prototype.resetMode = function (mode) {
        if (mode !== 'desktopPortrait') {
            var undefined;

            var task = _N2.History.get().addValue(this, this.historyResetMode, [mode]);
            if (task) {
                task.setValues(_Assign({}, this.deviceProperty[mode]), {});
            }

            for (var k in this.deviceProperty[mode]) {
                this.deviceProperty[mode][k] = undefined;
            }
            if (mode === this.fragmentEditor.getMode()) {
                this.renderModeProperties(true);
            }
        }
    };

    LayerDataStorage.prototype.copyMode = function (from, to) {
        if (from !== to) {
            var originalValues = this.deviceProperty[to];

            this.deviceProperty[to] = _Assign({}, this.deviceProperty[to], this.deviceProperty[from]);

            var task = _N2.History.get().addValue(this, this.historyResetMode, [to]);
            if (task) {
                task.setValues(originalValues, this.deviceProperty[to]);
            }
        }
    };

    LayerDataStorage.prototype._getDefault = function (name, def) {
        if (this.originalProperties[name] !== undefined) {
            return this.originalProperties[name];
        }
        return def;
    };

    LayerDataStorage.prototype.createProperty = function (name, def, $layer, scope) {
        this.isDeviceProp[name] = false;
        this.propertyScope[name] = scope || this;
        if ($layer) {
            this.property[name] = $layer.data(name.toLowerCase());
            if (this.property[name] === undefined || this.property[name] === null) {
                this.property[name] = this._getDefault(name, def);
            }
        } else {
            this.property[name] = this._getDefault(name, def);
        }
    };

    /**
     * @param {_N2.LayerAdvancedProperty} advancedProperty
     * @param $layer
     * @param scope
     */
    LayerDataStorage.prototype.createAdvancedProperty = function (advancedProperty, $layer, scope) {
        var names = advancedProperty.getNames();
        for (var k in names) {
            this.advancedProperties[k] = advancedProperty;
            this.createProperty(k, names[k], $layer, scope);
        }
    };

    LayerDataStorage.prototype.syncAdvancedField = function (baseName) {
        var name = this.advancedProperties[baseName].getName(),
            value = this.property[name];
        if (this.property[name] === undefined) {
            value = this.property[baseName];
        }

        this.fragmentEditor.layerOptions.updateField(name, value);
    };

    LayerDataStorage.prototype.createDeviceProperty = function (name, def, $layer, scope) {
        var k, defaultValue;
        this.isDeviceProp[name] = true;
        this.propertyScope[name] = scope || this;
        if ($layer) {
            for (k in this.deviceProperty) {
                this.deviceProperty[k][name] = $layer.data(k.toLowerCase() + name.toLowerCase());
                if (this.deviceProperty[k][name] === "") {
                    this.deviceProperty[k][name] = undefined;
                }
            }
            for (k in this.deviceProperty) {
                if (this.deviceProperty[k][name] === undefined || this.deviceProperty[k][name] === "") {
                    defaultValue = this._getDefault(k.toLowerCase() + name.toLowerCase());
                    if (defaultValue !== undefined) {
                        this.deviceProperty[k][name] = defaultValue;
                    }
                }
            }
            for (k in def) {
                if (this.deviceProperty[k][name] === undefined || this.deviceProperty[k][name] === "") {
                    this.deviceProperty[k][name] = def[k];
                }
            }
        } else {
            //Create layer
            for (k in def) {
                this.deviceProperty[k][name] = def[k];
            }
            for (k in this.deviceProperty) {
                defaultValue = this._getDefault(k.toLowerCase() + name.toLowerCase());
                if (defaultValue !== undefined) {
                    this.deviceProperty[k][name] = defaultValue;
                }
            }
        }
        this.property[name] = this.deviceProperty.desktopPortrait[name];
    };

    LayerDataStorage.prototype.removeProperty = function (name) {
        delete this.property[name];
        this.layer.removeData(name.toLowerCase())
            .removeAttr('data-' + name.toLowerCase());

        if (this.isDeviceProp[name]) {
            for (var k in this.deviceProperty) {
                delete this.deviceProperty[k][name];
                this.layer.removeData(k.toLowerCase() + name.toLowerCase())
                    .removeAttr('data-' + k.toLowerCase() + name.toLowerCase());
            }
        }
        delete this.isDeviceProp[name];
        delete this.propertyScope[name];
    };

    LayerDataStorage.prototype.removeProperties = function (properties) {
        for (var i = 0; i < properties.length; i++) {
            this.removeProperty(properties[i]);
        }
    };

    LayerDataStorage.prototype.getPropertiesData = function (properties) {
        var data = {};
        for (var i = 0; i < properties.length; i++) {
            var name = properties[i];
            if (this.property[name] !== undefined) {
                data[name] = this.property[name];
            }
            if (this.isDeviceProp[name]) {
                for (var k in this.deviceProperty) {
                    if (this.deviceProperty[k][name] !== undefined) {
                        data[k.toLowerCase() + name] = this.deviceProperty[k][name];
                    }
                }
            }
        }
        return data;
    };

    LayerDataStorage.prototype.setProperty = function (name, value, from) {

        if (this.advancedProperties[name] !== undefined) {
            name = this.advancedProperties[name].getName();
        }

        if (this.propertyScope[name] !== undefined) {
            if (typeof this.propertyScope[name]['setProperty' + name] == 'function') {
                this.propertyScope[name]['setProperty' + name](name, value, from);
            } else {
                this._setProperty(name, value, from);
            }
        } else if (typeof this['setProperty' + name] == 'function') {
            this['setProperty' + name](name, value, from);
        }
    };

    LayerDataStorage.prototype._setProperty = function (name, value, from) {

        this.store(undefined, name, value, true, from);

        if (from !== 'manager') {
            this.$.trigger('propertyChanged', [name, this.getProperty(name)]);
        }
    };

    LayerDataStorage.prototype.onSyncFields = function () {

    };

    LayerDataStorage.prototype.resetStyleMode = function (name) {
        for (var k in this.advancedProperties) {
            this.advancedProperties[k].resetMode(name);
        }
    };

    LayerDataStorage.prototype.outsideStoreProperty = function (mode, name, value, from) {

        this.store(mode, name, value, true, from);

        if (mode === undefined || mode === this.getMode()) {
            this.$.trigger('propertyChanged', [name, this.getProperty(name)]);
        }
    };

    return LayerDataStorage;
});_N2.d('FragmentEditorHotkey', '$', function () {
    var $ = _N2.$;
    "use strict";

    var keyCodeLShift = 'ShiftLeft';
    var keyCodeRShift = 'ShiftRight';

    var horizontalAlign = {
            'Numpad1': 'left',
            'Numpad2': 'center',
            'Numpad3': 'right',
            'Numpad4': 'left',
            'Numpad5': 'center',
            'Numpad6': 'right',
            'Numpad7': 'left',
            'Numpad8': 'center',
            'Numpad9': 'right'
        },
        verticalAlign = {
            'Numpad1': 'bottom',
            'Numpad2': 'bottom',
            'Numpad3': 'bottom',
            'Numpad4': 'middle',
            'Numpad5': 'middle',
            'Numpad6': 'middle',
            'Numpad7': 'top',
            'Numpad8': 'top',
            'Numpad9': 'top'
        };

    var numpadCodes = ['Numpad1', 'Numpad2', 'Numpad3', 'Numpad4', 'Numpad5', 'Numpad6', 'Numpad7', 'Numpad8', 'Numpad9']

    /**
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @constructor
     */
    function FragmentEditorHotkey(fragmentEditor) {
        /**
         *
         * @type {_N2.FragmentEditor}
         */
        this.fragmentEditor = fragmentEditor;

        this.keysPressed = {};
        this.keyIntervals = {};

        document.addEventListener('keydown', this.onKeyDown.bind(this), {
            capture: true
        });

        document.addEventListener('keyup', this.onKeyUp.bind(this), {
            capture: true
        });
    }

    FragmentEditorHotkey.prototype.onKeyDown = function (e) {
        if (!e.defaultPrevented) {
            var isTimelineActive = false;
            if (e.target.tagName !== 'TEXTAREA' && e.target.tagName !== 'INPUT' && !e.target.isContentEditable && !isTimelineActive && _N2.WindowManager.getCurrentWindow() === 'main') {
                var activeComponent = this.fragmentEditor.activeLayer,
                    keyCode = e.code,
                    keyKey = e.key,
                    handled = false;

                if (e.shiftKey && !e.ctrlKey && !e.metaKey) {
                    if (keyCode === 'KeyE') { // shift + [alt] + e
                        var devices = [];
                        if (e.altKey) {
                            devices = [
                                'desktopLandscape',
                                'tabletLandscape',
                                'tabletPortrait',
                                'mobileLandscape',
                                'mobilePortrait'
                            ];
                            _N2.Notification.success(n2_('Clear device specific settings') + ' - ' + n2_('Current layer, all devices'));
                        } else {
                            devices.push(this.fragmentEditor.getMode());
                            _N2.Notification.success(n2_('Clear device specific settings') + ' - ' + n2_('Current layer, current device'));
                        }
                        for (var i = 0; i < devices.length; i++) {
                            activeComponent.resetMode(devices[i]);
                        }
                        handled = true;
                    } else if (keyCode === 'KeyR') { // shift + [alt] + r
                        var devices = [], layers = this.fragmentEditor.mainContainer.container.getAllLayers();
                        layers.unshift(this.fragmentEditor.mainContainer);
                        if (e.altKey) {
                            devices = [
                                'desktopLandscape',
                                'tabletLandscape',
                                'tabletPortrait',
                                'mobileLandscape',
                                'mobilePortrait'
                            ];
                            _N2.Notification.success(n2_('Clear device specific settings') + ' - ' + n2_('All layers, all devices'));
                        } else {
                            devices.push(this.fragmentEditor.getMode());
                            _N2.Notification.success(n2_('Clear device specific settings') + ' - ' + n2_('All layers, current device'));
                        }
                        for (var j = 0; j < layers.length; j++) {
                            for (var i = 0; i < devices.length; i++) {
                                layers[j].resetMode(devices[i]);
                            }
                        }
                        handled = true;
                    }
                }

                if (e.ctrlKey || e.metaKey) {
                    if (keyKey === 'Z' || keyKey === 'z') { // ctrl + [shift] + z
                        if (e.shiftKey) {
                            if (_N2.History.get().redo()) {
                                handled = true;
                            }
                        } else {
                            if (_N2.History.get().undo()) {
                                handled = true;
                            }
                        }
                    } else if (keyCode === 'KeyC') { // ctrl + c
                        if (window.getSelection().toString().length === 0) {
                            this.fragmentEditor.clipboard.copy();
                            handled = true;
                        }
                    } else if (keyCode === 'KeyV') { // ctrl + v
                        this.fragmentEditor.clipboard.paste();
                        handled = true;
                    } else if (keyCode === 'Digit1') { // ctrl + 1
                        $('#n2-ss-preview').trigger('click');
                        handled = true;
                    } else if (keyCode === 'Digit2') { // ctrl + 2
                        var $desktopPortrait = $('.n2_device_tester_devices__device[data-device="desktopPortrait"]');
                        if (!$desktopPortrait.hasClass('n2_device_tester_devices__device--active')) {
                            $desktopPortrait.trigger('click');
                        } else {
                        }
                        handled = true;
                    } else if (keyCode === 'Digit3') { // ctrl + 3
                        var $tabletPortrait = $('.n2_device_tester_devices__device[data-device="tabletPortrait"]');
                        if (!$tabletPortrait.hasClass('n2_device_tester_devices__device--active')) {
                            $tabletPortrait.trigger('click');
                        } else {
                        }
                        handled = true;
                    } else if (keyCode === 'Digit4') { // ctrl + 4
                        var $mobilePortrait = $('.n2_device_tester_devices__device[data-device="mobilePortrait"]');
                        if (!$mobilePortrait.hasClass('n2_device_tester_devices__device--active')) {
                            $mobilePortrait.trigger('click');
                        } else {
                        }
                        handled = true;
                    }

                }

                if (!handled && activeComponent) {

                    if (e.ctrlKey || e.metaKey) {
                        if (keyCode === 'KeyD') { //ctrl + d
                            this.fragmentEditor.duplicate();
                            handled = true;
                        }
                    } else if (keyCode === 'Delete' || keyCode === 'Backspace') { // delete or backspace
                        this.fragmentEditor.delete();
                        handled = true;
                    } else if (keyCode === keyCodeRShift || keyCode === keyCodeLShift) {
                        this.keysPressed[keyCode] = 1;
                    } else if (keyCode === 'ArrowUp') { // arrow up
                        if (activeComponent.placement.getType() === 'absolute') {
                            if (!this.keyIntervals[keyCode]) {
                                this.layerMoveUp();
                                this.keyIntervals[keyCode] = setInterval(this.layerMoveUp.bind(this), 100);
                            }
                            handled = true;
                        }
                    } else if (keyCode === 'ArrowDown') { // arrow down
                        if (activeComponent.placement.getType() === 'absolute') {
                            if (!this.keyIntervals[keyCode]) {
                                this.layerMoveDown();
                                this.keyIntervals[keyCode] = setInterval(this.layerMoveDown.bind(this), 100);
                            }
                            handled = true;
                        }
                    } else if (keyCode === 'ArrowLeft') { // arrow left
                        if (activeComponent.placement.getType() === 'absolute') {
                            if (!this.keyIntervals[keyCode]) {
                                this.layerMoveLeft();
                                this.keyIntervals[keyCode] = setInterval(this.layerMoveLeft.bind(this), 100);
                            }
                            handled = true;
                        }
                    } else if (keyCode === 'ArrowRight') { // arrow right
                        if (activeComponent.placement.getType() === 'absolute') {
                            if (!this.keyIntervals[keyCode]) {
                                this.layerMoveRight();
                                this.keyIntervals[keyCode] = setInterval(this.layerMoveRight.bind(this), 100);
                            }
                            handled = true;
                        }
                    } else if (numpadCodes.indexOf(keyCode) !== -1) {  // numeric
                        if (activeComponent.placement.getType() === 'absolute') {
                            var hAlign = horizontalAlign[keyCode],
                                vAlign = verticalAlign[keyCode],
                                toZero = false;
                            if (this.fragmentEditor.layerOptions.forms.placement.absolute.align.val() === hAlign && this.fragmentEditor.layerOptions.forms.placement.absolute.valign.val() === vAlign) {
                                toZero = true;
                            }
                            this.fragmentEditor.layerOptions.layerFeatures.horizontalAlign(hAlign, toZero);
                            this.fragmentEditor.layerOptions.layerFeatures.verticalAlign(vAlign, toZero);
                            handled = true;
                        }
                    }
                }

                if (!handled) {
                    if (!e.ctrlKey && !e.metaKey) {
                        if (keyCode === 'KeyQ') { // q
                            if (!this.fragmentEditor.addLayer.visible) {
                                activeComponent.activate(true);
                                this.fragmentEditor.layerWindow.tab.displayTab('content');
                                handled = true;
                            }
                        } else if (keyCode === 'KeyW') { // w
                            if (!this.fragmentEditor.addLayer.visible) {
                                activeComponent.activate(true);
                                this.fragmentEditor.layerWindow.tab.displayTab('style');
                                handled = true;
                            }
                        } else if (keyCode === 'KeyE') { // e
                            if (!this.fragmentEditor.addLayer.visible) {
                                activeComponent.activate(true);
                                this.fragmentEditor.layerWindow.tab.displayTab('animations');
                                handled = true;
                            }
                        } else if (keyCode === 'KeyA') { // a
                            this.fragmentEditor.addLayer.toggleActive();
                            handled = true;
                        } else if (keyCode === 'KeyS') { // s
                            if (!this.fragmentEditor.addLayer.visible) {
                                activeComponent.activate(true);
                                this.fragmentEditor.layerWindow.toggleLayerList();
                                handled = true;
                            }
                        } else if (keyCode === 'KeyD') { // d
                        }
                    }
                }

                if (handled) {
                    e.preventDefault();
                }
            }
        }
    };

    FragmentEditorHotkey.prototype.onKeyUp = function (e) {
        if (this.keyIntervals[e.code] !== undefined) {
            clearInterval(this.keyIntervals[e.code]);
            delete this.keyIntervals[e.code];
        }

        if ((e.code === keyCodeRShift && this.keysPressed[keyCodeRShift] !== undefined)) {
            this.keysPressed[keyCodeRShift] = 0;
        } else if (e.code === keyCodeLShift && this.keysPressed[keyCodeLShift] !== undefined) {
            this.keysPressed[keyCodeLShift] = 0;
        }
    };

    FragmentEditorHotkey.prototype.layerMoveUp = function () {
        this.fragmentEditor.activeLayer.moveY(-1 * (this.keysPressed[keyCodeLShift] ? 10 : this.keysPressed[keyCodeRShift] ? 10 : 1));
    };

    FragmentEditorHotkey.prototype.layerMoveDown = function () {
        this.fragmentEditor.activeLayer.moveY((this.keysPressed[keyCodeLShift] ? 10 : this.keysPressed[keyCodeRShift] ? 10 : 1));
    };

    FragmentEditorHotkey.prototype.layerMoveLeft = function () {
        this.fragmentEditor.activeLayer.moveX(-1 * (this.keysPressed[keyCodeLShift] ? 10 : this.keysPressed[keyCodeRShift] ? 10 : 1));
    };

    FragmentEditorHotkey.prototype.layerMoveRight = function () {
        this.fragmentEditor.activeLayer.moveX(this.keysPressed[keyCodeLShift] ? 10 : this.keysPressed[keyCodeRShift] ? 10 : 1);
    };

    return FragmentEditorHotkey;
});_N2.d('EditorInteractionManager', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @constructor
     */
    function EditorInteractionManager(fragmentEditor) {
        this.fragmentEditor = fragmentEditor;

        this.isHover = false;

        this.state = {
            drag: false
        };

        this.$container = $('.n2_admin_editor__content')
            .on({
                click: this.handleMouseClick.bind(this)
            });

        this.$containerInner = this.$container.find(".n2_slide_editor_slider__editor_inner")
            .one('mouseover', this.mouseEnter.bind(this))
            .on({
                mouseenter: this.mouseEnter.bind(this),
                mouseleave: this.mouseLeave.bind(this),
                dblclick: this.handleDoubleClick.bind(this),
                contextmenu: this.handleContextMenu.bind(this)
            });

        this.storeInnerOffset();

        document.addEventListener('mousemove', this.onMouseMove.bind(this), {
            passive: true,
            capture: true
        });

        /**
         * @type {_N2.EditorContextualBarHoverSection}
         */
        this.contextualBarHoverSection = new _N2.EditorContextualBarHoverSection(this);

        /**
         * @type {_N2.EditorContextualBarHover}
         */
        this.contextualBarHover = new _N2.EditorContextualBarHover(this);

        /**
         * @type {_N2.EditorContextualBarActive}
         */
        this.contextualBarActive = new _N2.EditorContextualBarActive(this);

        window.addEventListener('resize', this.refreshPosition.bind(this), {
            passive: true,
            capture: true
        });

        document.addEventListener('scroll', this.refreshPosition.bind(this), {
            passive: true,
            capture: true
        });

        document.addEventListener('EditorInvalidated', this.refreshPosition.bind(this));

        _addEventListener(this.fragmentEditor.editor.frontend.sliderElement, 'SliderResize', this.refreshPosition.bind(this));
    }

    /**
     * @param {_N2.ComponentAbstract} component
     */
    EditorInteractionManager.prototype.changeActiveComponent = function (component) {
        /**
         * @type {_N2.ComponentAbstract}
         */
        this.activeComponent = component;

        this.contextualBarActive.changeActiveComponent(component);

        this.contextualBarHoverSection.refreshActiveState();
        this.contextualBarHover.refreshActiveState();

        if (this.lastMouseMoveEvent !== undefined) {
            this.contextualBarHoverSection.processMouseCoordinates(this.lastMouseMoveEvent.clientX, this.lastMouseMoveEvent.clientY);
            this.contextualBarHover.processMouseCoordinates(this.lastMouseMoveEvent.clientX, this.lastMouseMoveEvent.clientY);
        }
    };

    /**
     *
     * @param {MouseEvent} e
     */
    EditorInteractionManager.prototype.onMouseMove = function (e) {
        /**
         * @type {MouseEvent}
         */
        this.lastMouseMoveEvent = e;
    };

    EditorInteractionManager.prototype.refreshPosition = function () {
        this.storeInnerOffset();
        this.contextualBarActive.refreshPosition();
        this.contextualBarHover.refreshPosition();
        this.contextualBarHoverSection.refreshPosition();
    };

    EditorInteractionManager.prototype.mouseEnter = function () {
        if (!this.isHover) {
            this.isHover = true;
            this.enable();
            this.syncHover();
        }
    };

    EditorInteractionManager.prototype.mouseLeave = function () {
        if (this.isHover) {
            this.isHover = false;

            this.disable();
            this.syncHover();
        }
    };

    EditorInteractionManager.prototype.enable = function () {
        this.contextualBarHoverSection.enable();
        this.contextualBarHover.enable();
        this.contextualBarActive.enable();
    };

    EditorInteractionManager.prototype.disable = function () {
        this.contextualBarActive.disable();
        this.contextualBarHover.disable();
        this.contextualBarHoverSection.disable();
    };

    EditorInteractionManager.prototype.syncHover = function () {
        this.$container.toggleClass('n2_admin_editor__content--hover', this.isHover);
    };

    EditorInteractionManager.prototype.storeInnerOffset = function () {

        this.innerOffset = this.$containerInner[0].getBoundingClientRect();
    };

    EditorInteractionManager.prototype.handleMouseClick = function (e) {

        if (!_N2.WindowManager.isActionPrevented('ComponentActivate')) {
            var component = this.findRelatedComponent($(e.target));
            if (component) {
                component.activate(e);
            } else {
                this.fragmentEditor.layerWindow.requestHide();
            }
        }
    };

    EditorInteractionManager.prototype.handleDoubleClick = function (e) {
        var $target = $(e.target);
        if ($target.closest('.n2-ss-layer').length) {
            var component = this.findRelatedComponent($target);
            if (component && component === this.activeComponent) {
                this.activeComponent.doubleClick(e);
            }
        }
    };


    EditorInteractionManager.prototype.handleContextMenu = function (e) {

        var component = this.findRelatedComponent($(e.target));
        if (component) {
            e.preventDefault();

            component.onContextMenu(e);
        }
    };

    /**
     *
     * @param target
     * @returns {boolean|_N2.ComponentAbstract}
     */
    EditorInteractionManager.prototype.findRelatedComponent = function (target) {
        var $target = $(target),
            $component;
        if ($target.hasClass('n2-ss-layer')) {
            $component = $target;
        } else {
            $component = $target.closest('.n2-ss-layer');
            if (!$component.length) {
                if ($target.hasClass('n2_layer_contextual')) {
                    $component = $target;
                } else {
                    $component = $target.closest('.n2_layer_contextual');
                }
            }
        }

        if ($component !== undefined) {
            return $component.data('layerObject');
        }

        return false;
    };

    return EditorInteractionManager;
});_N2.d('FragmentClipboard', '$', function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @constructor
     */
    function FragmentClipboard(fragmentEditor) {
        this.fragmentEditor = fragmentEditor;
    }

    FragmentClipboard.prototype.copy = function () {
        var component = this.fragmentEditor.activeLayer;

        if (this.fragmentEditor.isSectionSlide(component)) {
            this.sectionCopy();
        } else {
            this.componentCopy(component);
        }
    };

    FragmentClipboard.prototype.paste = function () {
        var component = this.fragmentEditor.activeLayer;

        if (this.fragmentEditor.isSectionSlide(component)) {
            this.sectionPaste();
        } else {
            this.componentPaste(component);
        }
    };

    FragmentClipboard.prototype.sectionHasClipboard = function () {
        return !!localStorage.getItem('clipboardSection');
    };

    FragmentClipboard.prototype.sectionCopy = function () {
        var section = this.fragmentEditor.getSectionData();
        localStorage.setItem('clipboardSection', JSON.stringify(section));
    };

    FragmentClipboard.prototype.sectionPaste = function () {
        var slide = localStorage.getItem('clipboardSection');
        if (slide) {
            slide = JSON.parse(slide);
            this.fragmentEditor.manipulator.sectionReplace(this.fragmentEditor.mainContainer, slide);
        }
    };

    FragmentClipboard.prototype.componentHasClipboard = function () {
        return !!localStorage.getItem('clipboardLayers');
    };

    /**
     *
     * @param {_N2.ComponentAbstract} component
     */
    FragmentClipboard.prototype.componentCopy = function (component) {

        if (this.fragmentEditor.isSectionSlide(component)) {
            alert('copy section');
        } else {
            var componentsToCopy;
            if (this.fragmentEditor.isCol(component) || this.fragmentEditor.isContent(component)) {
                componentsToCopy = component.container.getSortedLayers()
            } else {
                componentsToCopy = [component];
            }

            var componentsData = this.getComponentsData(componentsToCopy);
            if (componentsData.length) {
                localStorage.setItem('clipboardLayers', JSON.stringify(componentsData))
            }
        }
    };

    /**
     *
     * @param {_N2.ComponentAbstract} targetComponent
     */
    FragmentClipboard.prototype.componentPaste = function (targetComponent) {
        var clipboard = localStorage.getItem('clipboardLayers');
        if (clipboard) {
            var layers = JSON.parse(clipboard);
            if (layers.length) {

                var placement = layers[0].pm,
                    targetGroup;

                if (placement === 'normal') {

                    if (targetComponent === undefined) {
                        targetGroup = this.fragmentEditor.mainContainer.getTargetGroupDefault();
                    } else if (this.fragmentEditor.isCol(targetComponent) || this.fragmentEditor.isContent(targetComponent)) {
                        targetGroup = targetComponent;
                    } else if (this.fragmentEditor.isRow(targetComponent)) {
                        /**
                         * First column in the group
                         */
                        targetGroup = targetComponent.container.getSortedLayers()[0];
                    } else if (targetComponent.placement.getType() === 'normal') {
                        targetGroup = targetComponent.group;
                    } else {
                        targetGroup = this.fragmentEditor.mainContainer.getTargetGroupDefault();
                    }

                    this.fragmentEditor.manipulator.addLayers(layers, targetGroup);

                } else if (placement === 'absolute') {

                    if (targetComponent === undefined) {
                        targetGroup = this.fragmentEditor.mainContainer.getTargetGroupAbsolute();
                    } else if (targetComponent.container) {
                        targetGroup = targetComponent;
                    } else {
                        targetGroup = targetComponent.group
                    }

                    this.fragmentEditor.manipulator.addLayers(layers, targetGroup);
                }
            }
        }
    };

    FragmentClipboard.prototype.getComponentsData = function (components) {
        if (components === undefined) {
            return [];
        }
        var layersData = [],
            layers = [];

        for (var i = 0; i < components.length; i++) {
            components[i].getDataWithChildren(layersData, layers);
        }
        return layersData;
    };

    return FragmentClipboard;
});_N2.d('FragmentEditor', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.EditorAbstract} editor
     * @param {jQuery} $editedElement
     * @param options
     * @constructor
     */
    function FragmentEditor(editor, $editedElement, options) {
        this.mode = 'desktopPortrait';
        /**
         * @type {_N2.EditorAbstract}
         */
        this.editor = editor;
        this.$editedElement = $editedElement;
        this.ready = $.Deferred();


        this.$ = $(this);

        editor.fragmentEditor = this;

        $('.n2_slide_editor_slider__editor').attr('dir', $('html').attr('dir'));

        this.___sheet = document.querySelector('style[data-related="n2-ss-0"]').sheet;

        /**
         * @type {_N2.AddLayer}
         */
        this.addLayer = new _N2.AddLayer(this);

        /**
         * @type {_N2.LayerWindow}
         */
        this.layerWindow = new _N2.LayerWindow(this);

        /**
         * @type {_N2.ComponentSettings}
         */
        this.layerOptions = new _N2.ComponentSettings(this);

        /**
         * @type {_N2.LayerNavigation}
         */
        this.layerNavigation = new _N2.LayerNavigation(this);

        /**
         * @type {_N2.FragmentManipulator}
         */
        this.manipulator = new _N2.FragmentManipulator(this);

        /**
         * @type {_N2.FragmentClipboard}
         */
        this.clipboard = new _N2.FragmentClipboard(this);

        /**
         * @type {_N2.SectionSlide}
         */
        this.mainContainer = new _N2.SectionSlide(this);


        this.canvasUIManager = new _N2.UICanvas(this.mainContainer.layer, this);

        /**
         * @type {_N2.ItemManager}
         */
        this.itemEditor = new _N2.ItemManager(this, options);

        this.mainContainer.lateInit();

        this._initDeviceModeChange();

        /**
         * @type {_N2.CanvasSettings}
         */
        this.canvasSettings = new _N2.CanvasSettings(this);

        this.layerOptions.startFeatures();

        this.pointerManager = new _N2.EditorInteractionManager(this);

        new _N2.FragmentEditorHotkey(this);

        this.mainContainer.activate(true);
    }

    FragmentEditor.prototype.getMode = function () {
        return this.mode;
    };

    FragmentEditor.prototype.getResponsiveRatioHorizontal = function () {
        return this.editor.getHorizontalRatio();
    };

    FragmentEditor.prototype.getResponsiveRatioVertical = function () {
        return this.editor.getVerticalRatio();
    };

    FragmentEditor.prototype.isRow = function (layer) {
        return layer instanceof _N2.Row;
    };

    FragmentEditor.prototype.isCol = function (layer) {
        return layer instanceof _N2.Col;
    };

    FragmentEditor.prototype.isLayer = function (layer) {
        return layer instanceof _N2.Layer;
    };

    FragmentEditor.prototype.isContent = function (layer) {
        return layer instanceof _N2.Content;
    };

    FragmentEditor.prototype.isSectionSlide = function (layer) {
        return layer instanceof _N2.SectionSlide;
    };

    FragmentEditor.prototype._initDeviceModeChange = function () {
        $('#layer-clear-device-specific-changes, #layer-slide-clear-device-specific-changes')
            .on('click', this.onClearDeviceSpecificChanges.bind(this));

        var deviceModes = this.editor.getAvailableDeviceModes();
        $('#layershow').data('field').setAvailableDevices(deviceModes);

        this.refreshMode();

        this.ready.resolve();

    };

    /**
     * Refresh the current responsive mode. Example: you are in tablet view and unpublish a layer for tablet, then you should need a refresh on the mode.
     */
    FragmentEditor.prototype.refreshMode = function () {
        this.mode = this.editor.getDeviceMode();

        this.mainContainer.changeEditorMode(this.mode);
    };

    FragmentEditor.prototype.onChangeDeviceOrientation = function () {
        this.refreshMode();
    };

    FragmentEditor.prototype.onResize = function (ratios) {
        this.mainContainer.onResize(ratios);

        if (this.canvasSettings.ruler) {
            this.canvasSettings.ruler.onResize();
        }
    };

    /**
     * Reset the custom values of the current mode on the current layer to the desktop values.
     * @private
     */
    FragmentEditor.prototype.onClearDeviceSpecificChanges = function () {
        if (this.activeLayer) {
            var mode = this.getMode();
            this.activeLayer.resetMode(mode);
        }
    };

    FragmentEditor.prototype.copyOrResetMode = function (mode) {
        var currentMode = this.getMode();
        if (currentMode === 'desktopPortrait') {
            if (mode !== 'desktopPortrait') {
                this.mainContainer.container.resetModes(mode);
            }
        } else {
            if (mode === currentMode) {
                this.mainContainer.container.resetModes(mode);
            } else {
                this.mainContainer.container.copyModes(currentMode, mode);
            }
        }
    };

    /**
     *
     * @param {_N2.ComponentAbstract} relativeToGroup
     * @param {_N2.ComponentAbstract} layer
     * @returns {jQuery}
     */
    FragmentEditor.prototype.getSnap = function (relativeToGroup, layer) {
        var $snapTo = $();

        if (this.canvasSettings.get("n2-ss-snap-to-enabled")) {
            if (this.mainContainer === relativeToGroup) {
                $snapTo = $snapTo.add('.n2-ruler-user-guide');
            }

            /**
             * Snap allowed to sibling absolute layers only
             *
             * @type {_N2.ComponentAbstract[]}
             */
            var layers = relativeToGroup.container.getSortedLayers();
            for (var i = 0; i < layers.length; i++) {
                if (layers[i].placement.getType() === 'absolute') {
                    $snapTo = $snapTo.add(layers[i].layer);
                }
            }
        }

        return $snapTo.filter(':visible').not(layer.layer);
    };

    /**
     * Get the HTML code of the whole slide
     * @returns {string} HTML
     */
    FragmentEditor.prototype.getHTML = function () {
        var node = $('<div></div>');

        var list = this.mainContainer.container.getAllLayers();
        for (var i = 0; i < list.length; i++) {
            node.append(list[i].getHTML(true));
        }

        return node.html();
    };

    FragmentEditor.prototype.getData = function () {
        return this.mainContainer.getData();
    };

    FragmentEditor.prototype.getSectionData = function () {
        return this.mainContainer.getSectionData();
    };

    FragmentEditor.prototype.loadComponentWithNode = function (group, $component, needHistory, refresh) {

        var component = group.container.append($component);

        if (refresh) {
            this.refreshMode();
        }

        return component;
    };

    FragmentEditor.prototype.insertComponentWithNode = function (group, $component, index, needHistory, refresh) {

        var component = group.container.insertAt($component, index);

        if (refresh) {
            this.refreshMode();
        }

        return component;
    };

    FragmentEditor.prototype.resetActiveLayer = function () {
        var layers = this.mainContainer.container.getSortedLayers();
        if (layers.length) {
            layers[layers.length - 1].activate();
        }
    };

    /**
     *
     * @param {_N2.ComponentAbstract} nextActiveLayer
     */
    FragmentEditor.prototype.changeActiveLayer = function (nextActiveLayer) {
        var activeLayer = this.activeLayer;
        if (activeLayer && !activeLayer.isDeleted) {
            // There is a chance that the layer already deleted
            activeLayer.$.off('propertyChanged.editor')
                .off('.active');

            activeLayer.deActivate();
        }
        /**
         * @type {_N2.ComponentAbstract}
         */
        this.activeLayer = nextActiveLayer;
        _N2._activeLayer = nextActiveLayer;

        if (nextActiveLayer) {

            this.pointerManager.changeActiveComponent(nextActiveLayer);

            this.layerOptions.changeActiveComponent(nextActiveLayer, nextActiveLayer.type, nextActiveLayer.getSubType(), nextActiveLayer.placement.getType(), nextActiveLayer.getProperties());

            nextActiveLayer.$.on({
                'propertyChanged.editor': this.layerOptions.onUpdateField.bind(this.layerOptions),
                'placementChanged.active': (function (e, current, last) {
                    this.layerOptions.changeActiveComponentPlacement(current, nextActiveLayer.property);
                }).bind(this)
            });
        }

        this.$.trigger('activeLayerChanged');
    };

    /**
     *
     * @returns {boolean|_N2.ComponentAbstract}
     */
    FragmentEditor.prototype.getActiveLayer = function () {
        return this.activeLayer;
    };

    FragmentEditor.prototype.highlight = function (layer) {
        this.pointerManager.contextualBarHover.enableActiveForced();
        this.pointerManager.contextualBarHover.processHoveredElement(layer);

        if (this.hoverZIndexInterval) {
            clearInterval(this.hoverZIndexInterval);
        }
        this.hoverZIndexInterval = setInterval((function () {
            this.toggleClass('n2_layer_layer_list_hover--force-zindex')
        }).bind(layer.layer), 800);
    };

    FragmentEditor.prototype.deHighlight = function (layer) {
        this.pointerManager.contextualBarHover.disableActiveForced();

        if (this.hoverZIndexInterval) {
            clearInterval(this.hoverZIndexInterval);
        }
        if (!layer.isDeleted) {
            layer.layer.removeClass('n2_layer_layer_list_hover--force-zindex');
        }
    };

    FragmentEditor.prototype.delete = function () {
        this.activeLayer.delete();
    };

    FragmentEditor.prototype.duplicate = function () {
        this.activeLayer.duplicate(true);
    };

    FragmentEditor.prototype.getSelf = function () {
        return this;
    };

    function removeRulesRecursive(sheet, className) {
        var rules = sheet.cssRules;
        for (var i = rules.length - 1; i >= 0; i--) {
            if (rules[i].selectorText && rules[i].selectorText.match(className)) {
                sheet.deleteRule(i);
            } else if (rules[i] instanceof CSSMediaRule) {
                removeRulesRecursive(rules[i], className);
            }
        }
    }

    FragmentEditor.prototype.removeRules = function (className) {

        removeRulesRecursive(this.___sheet, className);
    }

    return FragmentEditor;
});_N2.d('FragmentManipulator', '$', function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @constructor
     */
    function FragmentManipulator(fragmentEditor) {
        this.fragmentEditor = fragmentEditor;
    }

    FragmentManipulator.prototype.deleteLayers = function () {
        var layers = this.fragmentEditor.mainContainer.container.getSortedLayers();
        for (var i = 0; i < layers.length; i++) {
            layers[i].delete();
        }
    };

    /**
     *
     * @param {_N2.SectionSlide} section
     * @param {{}} _sectionData
     * @returns {[]}
     */
    FragmentManipulator.prototype.sectionReplace = function (section, _sectionData) {

        this._idTranslation = {};

        var sectionData = _AssignRecursive({}, _sectionData),
            sectionContainer = section.container,
            layerNodes = this.dataToLayers(_AssignRecursive([], sectionData.layers).reverse()),
            layers = [];

        this.deleteLayers();

        section.mainContent.remove();


        for (var i = 0; i < layerNodes.length; i++) {
            layers.push(sectionContainer.append(layerNodes[i]));
        }

        _N2.History.get().startBatch();
        /**
         * @type {{}}
         */
        var resetProperties = {};

        section.removeProperty('padding');
        section.createDeviceProperty('padding', {desktopPortrait: '10|*|10|*|10|*|10'});

        for (var device in section.deviceProperty) {
            for (var property in section.isDeviceProp) {
                if (section.isDeviceProp[property]) {
                    var name = device.toLowerCase() + property;
                    if (sectionData[name] !== undefined) {
                        resetProperties[property] = true;
                        section.outsideStoreProperty(device, property, sectionData[name], true, 'sectionReplace');
                        delete sectionData[name];
                    } else if (resetProperties[property] === true) {
                        section.outsideStoreProperty(device, property, undefined, true, 'sectionReplace');
                    }
                }
            }
        }

        for (var n in section.property) {
            if (sectionData[n] !== undefined) {
                section.outsideStoreProperty(undefined, n, sectionData[n], true, 'sectionReplace');
                delete sectionData[n];
            }
        }

        _N2.History.get().addSimple(this, this.historyDeleteAll, this.historyReplaceLayers, [section, sectionData, layers, sectionContainer.getAllLayers()]);

        _N2.History.get().endBatch();


        this.fragmentEditor.refreshMode();

        _N2.ImagesLoaded(sectionContainer.layerContainerElement[0])
            .finally(this.fragmentEditor.refreshMode.bind(this.fragmentEditor));

        if (!this.fragmentEditor.getActiveLayer()) {
            if (layers.length > 0) {
                layers[0].activate();
            }
        }

        return layers;
    };

    /**
     *
     * @param {_N2.SectionSlide} section
     * @param {{}} sectionData
     * @param {_N2.ComponentAbstract[]} historicalLayers
     */
    FragmentManipulator.prototype.historyDeleteAll = function (section, sectionData, historicalLayers) {
        for (var i = 0; i < historicalLayers.length; i++) {
            historicalLayers[i].getSelf().delete();
        }

        section.mainContent.getSelf().remove();
    };


    /**
     *
     * @param {_N2.SectionSlide} section
     * @param {{}} sectionData
     * @param {_N2.ComponentAbstract[]} historicalLayers
     * @param {_N2.ComponentAbstract[]} historicalAllLayers
     */
    FragmentManipulator.prototype.historyReplaceLayers = function (section, sectionData, historicalLayers, historicalAllLayers) {
        this.sectionReplace(section, sectionData);

        var layers = section.container.getAllLayers();
        for (var i = 0; i < historicalAllLayers.length; i++) {
            historicalAllLayers[i].setSelf(layers[i]);
        }
    };

    FragmentManipulator.prototype.addLayers = function (layersData, group) {

        this._idTranslation = {};
        var layerNodes = this.dataToLayers(_AssignRecursive([], layersData)),
            layers = [];

        for (var i = 0; i < layerNodes.length; i++) {
            layers.push(group.container.append(layerNodes[i]));
        }

        this.fragmentEditor.refreshMode();

        _N2.History.get().addSimple(this, this.historyDeleteLayers, this.historyAddLayers, [layersData, layers, group]);

        return layers;
    };

    FragmentManipulator.prototype.historyDeleteLayers = function (layersData, historicalLayers, historicalGroup) {
        for (var i = 0; i < historicalLayers.length; i++) {
            historicalLayers[i].getSelf().delete();
        }
    };

    FragmentManipulator.prototype.historyAddLayers = function (layersData, historicalLayers, historicalGroup) {
        var layers = this.addLayers(layersData, historicalGroup.getSelf());
        for (var i = 0; i < historicalLayers.length; i++) {
            historicalLayers[i].setSelf(layers[i]);
        }
    };

    FragmentManipulator.prototype.dataToLayers = function (layers, $targetGroupContent) {
        var nodes = [];
        for (var i = 0; i < layers.length; i++) {
            switch (layers[i].type) {
                case 'row':
                    nodes.push(this.buildRowNode(layers[i], $targetGroupContent));
                    break;
                case 'col':
                    nodes.push(this.buildColNode(layers[i], $targetGroupContent));
                    break;
                case 'content':
                    nodes.push(this.buildContentNode(layers[i], $targetGroupContent));
                    break;
                case 'layer':
                default:
                    nodes.push(this.buildLayerNode(layers[i], $targetGroupContent));
                    break;
            }
        }

        return nodes;
    };

    FragmentManipulator.prototype._buildNodePrepareID = function ($layer, layerData) {
        if (layerData.id) {
            var id = $.fn.uid();

            var deferred = false;
            if (typeof this._idTranslation[layerData.id] == 'object') {
                deferred = this._idTranslation[layerData.id];
            }

            this._idTranslation[layerData.id] = id;
            layerData.id = id;
            $layer.attr('id', id);

            if (deferred) {
                deferred.resolve(layerData.id, id);
            }
        }
        if (layerData.parentid) {
            switch (typeof this._idTranslation[layerData.parentid]) {
                case 'string':
                    layerData.parentid = this._idTranslation[layerData.parentid];
                    break;
                case 'undefined':
                    this._idTranslation[layerData.parentid] = $.Deferred();
                case 'object':
                    this._idTranslation[layerData.parentid].done((function ($_layer, originalID, newID) {
                        $_layer.data('parentid', newID);
                    }).bind(this, $layer));
                    break;
                default:
                    layerData.parentid = '';
            }
        }
    };


    FragmentManipulator.prototype.buildContentNode = function (layerData, $targetGroupContent) {

        var $layer = $("<div class='n2-ss-layer' data-sstype='content'></div>"),
            $content = $("<div class='n2-ss-section-main-content n2-ss-layer-with-background n2-ss-layer-content'></div>").appendTo($layer);
        for (var k in layerData) {
            $layer.data(k, layerData[k]);
        }

        if ($targetGroupContent !== undefined) {
            $layer.appendTo($targetGroupContent);
        }

        this.dataToLayers(layerData.layers, $content);

        return $layer;
    };

    FragmentManipulator.prototype.buildRowNode = function (layerData, $targetGroupContent) {

        var $layer = $("<div class='n2-ss-layer' data-sstype='row'></div>"),
            $content = $("<div class='n2-ss-layer-row n2-ss-layer-with-background'></div>").appendTo($layer);

        this._buildNodePrepareID($layer, layerData);
        for (var k in layerData) {
            $layer.data(k, layerData[k]);
        }

        if ($targetGroupContent !== undefined) {
            $layer.appendTo($targetGroupContent);
        }

        this.dataToLayers(layerData.cols, $content);

        return $layer;
    };

    FragmentManipulator.prototype.buildColNode = function (layerData, $targetGroupContent) {

        var $layer = $("<div class='n2-ss-layer' data-sstype='col'></div>"),
            $content = $("<div class='n2-ss-layer-col n2-ss-layer-with-background n2-ss-layer-content'></div>").appendTo($layer);
        for (var k in layerData) {
            $layer.data(k, layerData[k]);
        }

        if ($targetGroupContent !== undefined) {
            $layer.appendTo($targetGroupContent);
        }

        this.dataToLayers(layerData.layers, $content);

        return $layer;
    };

    FragmentManipulator.prototype.buildLayerNode = function (layerData, $targetGroupContent) {

        var $layer = $("<div class='n2-ss-layer' data-sstype='layer'></div>")
            .attr('style', layerData.style);

        this._buildNodePrepareID($layer, layerData);

        if (layerData.items !== undefined) {
            layerData.item = layerData.items[0];
            delete layerData.items;
        }

        $('<div class="n2-ss-item n2-ss-item-' + layerData.item.type + '"></div>')
            .data('item', layerData.item.type)
            .data('itemvalues', layerData.item.values)
            .appendTo($layer);

        delete layerData.style;
        delete layerData.item;
        for (var k in layerData) {
            $layer.data(k, layerData[k]);
        }

        if ($targetGroupContent !== undefined) {
            $layer.appendTo($targetGroupContent);
        }

        return $layer;
    };

    FragmentManipulator.prototype.getSelf = function () {
        return this;
    };

    return FragmentManipulator;
});_N2.d('AddLayer', '$', function () {
    var $ = _N2.$;

    /**
     *
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @constructor
     */
    function AddLayer(fragmentEditor) {
        this.fragmentEditor = fragmentEditor;

        this.sectionLibraryFree = fragmentEditor.editor.options.sectionLibraryFree;
        this.sectionLibraryUrl = fragmentEditor.editor.options.sectionLibraryUrl;
        this.sectionLibraryGoProUrl = fragmentEditor.editor.options.sectionLibraryGoProUrl;

        try {
            localStorage.removeItem('SectionFreeThemeorion_dark');
            localStorage.removeItem('SectionFreeThemeorion_darkMetaDate');
            localStorage.removeItem('SectionFreeThemeorion_light');
            localStorage.removeItem('SectionFreeThemeorion_lightMetaDate');
            localStorage.removeItem('SectionFreeThemes');
            localStorage.removeItem('SectionFreeThemesMetaDate');
            localStorage.removeItem('SectionProThemeorion_dark');
            localStorage.removeItem('SectionProThemeorion_darkMetaDate');
            localStorage.removeItem('SectionProThemeorion_light');
            localStorage.removeItem('SectionProThemeorion_lightMetaDate');
            localStorage.removeItem('SectionProThemes');
            localStorage.removeItem('SectionProThemesMetaDate');
        } catch (e) {
        }

        this.visible = false;

        this.$editor = $('.n2_admin_editor');

        this.$editorOverlay = $('.n2_admin_editor_overlay');

        this.$addLayer = $('.n2_add_layer');

        $('.n2_add_layer__bar_button_add')
            .on('click', (function (e) {
                e.preventDefault();
                this.toggleActive();
            }).bind(this));

        this.$addLayer.find('.n2_add_layer_box');

        this.hideInteractionCallback = this.hideInteraction.bind(this);
        this.onKeyDownCallback = this.onKeyDown.bind(this);

        this.initTabs();

        this.initPositionChanger();
    }

    AddLayer.prototype.hide = function () {
        this.visible = true;
        this.toggleActive();
    };

    AddLayer.prototype.hideInteraction = function (e) {
        if (!$.contains(this.$addLayer[0], e.target) && !this.$addLayer.is(e.target)) {
            this.hide();
        }
    };

    AddLayer.prototype.onKeyDown = function (e) {
        if (e.code === 'Escape') {
            e.stopImmediatePropagation();
            e.preventDefault();

            this.hide();
        }
    };

    AddLayer.prototype.toggleActive = function () {

        this.visible = !this.visible;

        $('body').toggleClass('n2_body--show-add-more', this.visible);
        this.$editorOverlay.toggleClass('n2_admin_editor_overlay--show-add-more', this.visible);

        if (this.visible) {
            this.$editor[0].addEventListener("click", this.hideInteractionCallback, {capture: true});
            document.addEventListener("keydown", this.onKeyDownCallback, {capture: true});
        } else {
            this.$editor[0].removeEventListener("click", this.hideInteractionCallback, {capture: true});
            document.removeEventListener("keydown", this.onKeyDownCallback, {capture: true});

            this.setPosition('default');
        }
    };

    AddLayer.prototype.initTabs = function () {
        this.$buttons = $('.n2_add_layer__more_tab_button')
            .on('click');
        this.$tabs = $('.n2_add_layer__more_tab');

        this.valuesDict = {};
        for (var i = 0; i < this.$buttons.length; i++) {
            var $button = this.$buttons.eq(i),
                tabName = $button.data('related-tab');

            this.valuesDict[tabName] = {
                index: i,
                $button: $button,
                $tab: this.$tabs.filter('[data-tab="' + tabName + '"]')
            };

            $button.on('click', this.displayTab.bind(this, tabName));
        }

        this.displayTab('layers');
    };

    AddLayer.prototype.displayTab = function (tabName, e) {
        if (e) {
            e.preventDefault();
        }

        if (this.activeTabName !== tabName) {
            this.activeTabName = tabName;

            this.$buttons.removeClass('n2_add_layer__more_tab_button--active');
            this.$tabs.removeClass('n2_add_layer__more_tab--active');

            this.valuesDict[tabName].$button.addClass('n2_add_layer__more_tab_button--active');
            this.valuesDict[tabName].$tab.addClass('n2_add_layer__more_tab--active');

            if (tabName === 'library') {
                this.initLibrary();
            }
        }
    };

    AddLayer.prototype.initPositionChanger = function () {

        this.currentPosition = 'default';

        this.$position = this.$addLayer.find('.n2_add_layer_position');

        this.$position.find('.n2_add_layer_position__default_label')
            .on('click', this.setPosition.bind(this, 'default'));

        this.$position.find('.n2_add_layer_position__absolute_label')
            .on('click', this.setPosition.bind(this, 'absolute'));

        this.$position.find('.n2_add_layer_position__switch')
            .on('click', this.togglePosition.bind(this));
    };

    AddLayer.prototype.setPosition = function (position) {
        this.currentPosition = position;
        this.$position.attr('data-position', position);
    };

    AddLayer.prototype.togglePosition = function () {
        this.setPosition(this.currentPosition === 'default' ? 'absolute' : 'default');
    };

    AddLayer.prototype.getPosition = function () {
        return this.currentPosition;
    };

    AddLayer.prototype.initLibrary = function () {
        this.initLibrary = function () {
        };

        this.activeThemeID = false;
        this.themes = {};

        this.$themesTitle = $('<div class="n2_add_layer_library__title"></div>').appendTo(this.valuesDict.library.$tab);

        $('<div class="n2_add_layer_library__title_label"></div>')
            .text(n2_('Theme'))
            .appendTo(this.$themesTitle);

        this.displayThemes();
    };

    AddLayer.prototype.displayThemes = function () {

        _N2.AjaxHelper.ajax({
            type: "GET",
            url: this.sectionLibraryUrl + '/themes/',
            dataType: 'json'
        }).done((function (response) {
            this.onThemesLoaded(response.themes);
        }).bind(this));
    };

    AddLayer.prototype.onThemesLoaded = function (themes) {

        var $selectContainer = $('<div class="n2_field_select"></div>')
                .appendTo(this.$themesTitle),
            $select = $('<select name="library-theme" autocomplete="off"></select>')
                .appendTo($selectContainer);
        for (var i = 0; i < themes.length; i++) {
            $select.append('<option value="' + themes[i].id + '">' + themes[i].label + '</option>');
        }

        this.displayTheme(themes[0].id);
        $select.on('change', (function () {
            this.displayTheme($select.val());
        }).bind(this))
    };

    AddLayer.prototype.displayTheme = function (themeID) {

        if (this.activeThemeID !== themeID) {

            if (this.activeThemeID) {
                this.themes[this.activeThemeID].removeClass('n2_add_layer_library__theme--active');
            }

            this.activeThemeID = themeID;

            if (this.themes[themeID] === undefined) {

                _N2.AjaxHelper.ajax({
                    type: "GET",
                    url: this.sectionLibraryUrl + '/theme/' + themeID + '',
                    dataType: 'json'
                }).done((function (themeID, response) {
                    this.onThemeLoaded(response.theme);
                }).bind(this, themeID));
            } else {
                this.themes[themeID].addClass('n2_add_layer_library__theme--active');
            }
        }
    };

    function isCompatible(version, requiredVersion) {
        version = version.split('.');
        requiredVersion = requiredVersion.split('.');
        var k = Math.min(version.length, requiredVersion.length);
        for (var i = 0; i < k; ++i) {
            version[i] = parseInt(version[i], 10);
            requiredVersion[i] = parseInt(requiredVersion[i], 10);
            if (version[i] > requiredVersion[i]) {
                return true;
            }

            if (version[i] < requiredVersion[i]) {
                return false;
            }
        }
        return version.length === requiredVersion.length ? true : (version.length >= requiredVersion.length);
    }

    AddLayer.prototype.onThemeLoaded = function (theme) {

        if (!isCompatible(N2SS3VERSION, theme.version)) {
            this.activeThemeID = false;
            _N2.Notification.error(n2_sprintf(n2_('%s or newer required for this feature.'), 'Smart Slider ' + theme.version));
        } else {
            var $themeContainer = $('<div class="n2_add_layer_library__theme n2_add_layer_library__theme--active"></div>').appendTo(this.valuesDict.library.$tab);
            this.themes[theme.id] = $themeContainer;

            for (var j = 0; j < theme.categories.length; j++) {
                var tagData = theme.categories[j],
                    $themeTagContainer = $('<div class="n2_add_layer_library_tag"></div>')
                        .appendTo($themeContainer),
                    $themeTagTitle = $('<div class="n2_add_layer_library_tag__title"></div>')
                        .on('click', (function ($themeTagContainer) {
                            $themeTagContainer.toggleClass('n2_add_layer_library_tag--active');
                        }).bind(this, $themeTagContainer))
                        .appendTo($themeTagContainer);

                $('<div class="n2_add_layer_library_tag__title_label"></div>').text(tagData.label)
                    .appendTo($themeTagTitle);

                $('<div class="n2_add_layer_library_tag__title_opener"><i class="ssi_16 ssi_16--buttonarrow"></i></div>')
                    .appendTo($themeTagTitle);

                var $sections = $('<div class="n2_add_layer_library_tag__sections"></div>')
                    .appendTo($themeTagContainer);
                for (var i = 0; i < tagData.sections.length; i++) {
                    var section = tagData.sections[i],
                        $section = $('<div class="n2_add_layer_library_tag__section"><img src="' + section.thumbnail + '" alt=""></div>')
                            .appendTo($sections);

                    if (section.isPro) {
                        $section.append('<div class="n2_add_layer_library_tag__section_pro">Pro</div>');
                    }

                    if (!section.isPro || section.isPro && !this.sectionLibraryFree) {
                        $section
                            .addClass('n2_add_layer_library_tag__section--available')
                            .on('click', this.insertSection.bind(this, section.data))
                    } else {
                        $section.append('<div class="n2_add_layer_library_tag__section_overlay"><div>' + n2_sprintf(n2_('This block is not available in the free version. %s'), '<a href="' + this.sectionLibraryGoProUrl + '" target="_blank">' + n2_('Go Pro') + '!</a>') + '</div></div>');
                    
                    }
                }
            }
        }
    };

    AddLayer.prototype.insertSection = function (sectionData) {
        this.fragmentEditor.manipulator.sectionReplace(this.fragmentEditor.mainContainer, sectionData);
    };

    return AddLayer;
});_N2.d('LayerFeatures', '$', function () {
    "use strict";

    var nameToIndex = {
        left: 0,
        center: 1,
        right: 2,
        top: 0,
        middle: 1,
        bottom: 2
    };

    /**
     * @memberOf _N2
     *
     * @param fields
     * @param fragmentEditor
     * @constructor
     */
    function LayerFeatures(fields, fragmentEditor) {
        this.fields = fields;
        this.fragmentEditor = fragmentEditor;


        this.layerDefault = {
            align: null,
            valign: null
        };
    }

    LayerFeatures.prototype.horizontalAlign = function (align, toZero) {
        if (this.fields.align.val() != align) {
            this.fields.align.data('field').options.eq(nameToIndex[align]).trigger('click');
        } else if (toZero) {
            this.fields.left.val(0).trigger('change');
        }

        _N2._activeLayer.layer.trigger('interaction');
    };

    LayerFeatures.prototype.verticalAlign = function (align, toZero) {
        if (this.fields.valign.val() != align) {
            this.fields.valign.data('field').options.eq(nameToIndex[align]).trigger('click');
        } else if (toZero) {
            this.fields.top.val(0).trigger('change');
        }

        _N2._activeLayer.layer.trigger('interaction');
    };

    return LayerFeatures;
});_N2.d('LayerNavigation', '$', function () {
    var $ = _N2.$;
    "use strict";

    var listTitleHeight = 48,
        rowHeight = 33;

    /**
     *
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @constructor
     */
    function LayerNavigation(fragmentEditor) {
        this.fragmentEditor = fragmentEditor;

        /**
         *
         * @type {_N2.ComponentAbstract[]}
         */
        this.forceOpened = [];

        this.scrollBarHeight = 0;

        this.visible = false;
        this.tlHeight = parseInt(localStorage.getItem('ssLayersHeight') || 200);

        this.$editorContent = $('.n2_admin_editor__content');

        this.$editorOverlay = $('.n2_admin_editor_overlay');

        this.$layerNavigation = this.$container = $('<div class="n2_layer_navigation" dir="ltr"></div>')
            .appendTo(this.$editorOverlay);

        this.$scrollPanes = $('<div class="n2_layer_navigation_list"></div>')
            .appendTo(this.$layerNavigation);

        this.$layerListContainer = $('<div class="n2_layer_navigation_list_layers"></div>')
            .appendTo(this.$scrollPanes);

        this.fixScroll();

        this.topBar = $('.n2_layer_navigation_list_title');

        $('.n2_layer_navigation_list_title__label')
            .on('mousedown', this.resizeStart.bind(this));

        /**
         * @type {_N2.LayerNavigationInteraction}
         */
        this.interaction = new _N2.LayerNavigationInteraction(this, this.fragmentEditor, this.$layerNavigation);
    }

    /**
     * @param {_N2.LayerWindow} layerWindow
     */
    LayerNavigation.prototype.onAttach = function (layerWindow) {
        /**
         * @type {_N2.LayerWindow}
         */
        this.attachedToLayerWindow = layerWindow;
        this.scrollToActiveLayer();
    };

    LayerNavigation.prototype.onDetach = function () {
        delete this.attachedToLayerWindow;
        this.scrollToActiveLayer();
    };

    LayerNavigation.prototype.addScrollPane = function ($scrollPane) {
        this.$scrollPanes = this.$scrollPanes.add($scrollPane);
    };

    LayerNavigation.prototype.setScrollTop = function (scrollTop) {

        this.$scrollPanes.each(function () {
            this.scrollTop = scrollTop;
        });
    };

    LayerNavigation.prototype.scrollToActiveLayer = function () {
        this.onActivateLayer(this.fragmentEditor.activeLayer);
    };

    /**
     *
     * @param {_N2.ComponentAbstract} layer
     */
    LayerNavigation.prototype.onActivateLayer = function (layer) {

        if (this.forceOpened.length) {
            for (var i = 0; i < this.forceOpened.length; i++) {
                this.forceOpened[i].removeForceOpen();
            }
        }

        this.forceOpened = [];
        var group = layer.group;
        while (group) {
            if (group.type === 'row' || group.type === 'col' || group.type === 'content') {
                this.forceOpened.push(group);
                group.forceOpen();
            }
            group = group.group;
        }

        var scrollTop = this.$scrollPanes.scrollTop(),
            top = 0,
            currentLayer = layer;

        do {
            top += currentLayer.layerRow.get(0).offsetTop;
            currentLayer = currentLayer.group;
        } while (currentLayer);

        if (top < scrollTop || top > scrollTop + this.$scrollPanes.height() - rowHeight * 2 - this.scrollBarHeight) {
            this.setScrollTop(top);
        }
    };

    LayerNavigation.prototype.fixScroll = function () {

        if (this.scrollBarHeight > 0) {
            this.$layerListContainer.css('padding-bottom', this.scrollBarHeight + 'px');
        }

        this.$scrollPanes.on({
            wheel: this.onWheel.bind(this),
            scroll: this.onScroll.bind(this)
        });
    };

    LayerNavigation.prototype.onScroll = function (e) {
        e.preventDefault();
        this.setScrollTop($(e.currentTarget).scrollTop());
    };

    LayerNavigation.prototype.onWheel = function (e) {
        /**
         * Catch only vertical scroll
         */
        if (!e.shiftKey) {
            e.preventDefault();

            var top = this.$scrollPanes.scrollTop();
            if (e.originalEvent.deltaY > 0) {
                top += rowHeight;
            } else {
                top -= rowHeight;
            }
            top = Math.round(top / rowHeight) * rowHeight;
            this.setScrollTop(top);
        }
    };

    LayerNavigation.prototype.resizeStart = function (e) {
        e.preventDefault();
        this.startY = e.clientY;
        this.height = this.$layerNavigation.height();
        $('body').on({
            'mousemove.n2-ss-tl-resize': this.resizeMove.bind(this),
            'mouseup.n2-ss-tl-resize': this.resizeStop.bind(this),
            'mouseleave.n2-ss-tl-resize': this.resizeStop.bind(this)
        });
    };

    LayerNavigation.prototype.resizeMove = function (e) {
        e.preventDefault();
        this.setLayerNavigationHeight(this._calculateDesiredHeight(e));
    };

    LayerNavigation.prototype.resizeStop = function (e) {
        e.preventDefault();
        $('body').off('.n2-ss-tl-resize');
        var h = this._calculateDesiredHeight(e);
        this.setLayerNavigationHeight(h);

        this.tlHeight = h;
        localStorage.setItem('ssLayersHeight', h + "");
        $('#n2-admin').triggerHandler('resize');
    };

    LayerNavigation.prototype._calculateDesiredHeight = function (e) {
        var h = this.startY - e.clientY + this.height - listTitleHeight / 2;
        return this.__calculateDesiredHeight(h);
    };

    LayerNavigation.prototype.__calculateDesiredHeight = function (h) {
        var rows = Math.max(1, Math.round(Math.min(h, window.innerHeight / 2) / rowHeight) - 1);
        return this.scrollBarHeight + rows * rowHeight + listTitleHeight;
    };

    LayerNavigation.prototype.setLayerNavigationHeight = function (h) {
        h = Math.max(listTitleHeight, h);
        this.$layerNavigation.height(h + 'px');
        this.$editorContent.css('paddingBottom', h + 'px');

        if (this.layerNavigationTimeline !== undefined) {
            this.layerNavigationTimeline.onResize();
        }

        _N2._triggerResize();
    };

    return LayerNavigation;
});_N2.d('LayerNavigationInteraction', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     *
     * @memberOf _N2
     *
     * @param {_N2.LayerNavigation} layerNavigation
     * @param {_N2.FragmentEditor} fragmentEditor
     * @param  $container
     * @constructor
     */
    function LayerNavigationInteraction(layerNavigation, fragmentEditor, $container) {
        /**
         * @type {boolean|_N2.ComponentAbstract}
         */
        this.hoveredComponent = false;

        this.layerNavigation = layerNavigation;
        this.fragmentEditor = fragmentEditor;

        this.$container = $container
            .on({
                click: this.handleMouseClick.bind(this),
                mouseleave: this.handleMouseLeave.bind(this),
                contextmenu: this.handleContextMenu.bind(this)
            });

        $container[0].addEventListener('mouseover', this.handleMouseOver.bind(this), {
            passive: true,
            capture: true
        });
    }

    LayerNavigationInteraction.prototype.handleMouseClick = function (e) {
        if (!_N2.WindowManager.isActionPrevented('ComponentRowActivate')) {
            var component = this.findRelatedComponent(e.target);
            if (component) {
                component.activate(e);

                this.detachFromLayerWindow();
            }
        }
    };

    LayerNavigationInteraction.prototype.detachFromLayerWindow = function () {
        if (this.layerNavigation.attachedToLayerWindow) {
            this.layerNavigation.attachedToLayerWindow.detachLayerList();
        }
    };

    LayerNavigationInteraction.prototype.handleMouseLeave = function () {

        if (this.hoveredComponent) {
            this.fragmentEditor.deHighlight(this.hoveredComponent);
            this.hoveredComponent = false;
        }
    };

    LayerNavigationInteraction.prototype.handleMouseOver = function (e) {

        if (this.hoveredComponent) {
            this.fragmentEditor.deHighlight(this.hoveredComponent);
        }

        this.hoveredComponent = this.findRelatedComponent(e.target);

        if (this.hoveredComponent) {
            this.fragmentEditor.highlight(this.hoveredComponent);
        }
    };

    LayerNavigationInteraction.prototype.handleContextMenu = function (e) {

        var contextMenuComponent = this.findRelatedComponent(e.target);
        if (contextMenuComponent) {
            e.preventDefault();
            contextMenuComponent.onContextMenu(e);
        }
    };

    /**
     *
     * @private
     * @param target
     * @returns {boolean|_N2.ComponentAbstract}
     */
    LayerNavigationInteraction.prototype.findRelatedComponent = function (target) {
        var $target = $(target),
            $layer;
        if ($target.hasClass('n2_layer_navigation_list_layer')) {
            $layer = $target;
        } else {
            $layer = $target.closest('.n2_layer_navigation_list_layer');
            if (!$layer.length) {
                if ($target.hasClass('n2_timeline_layer__animations')) {
                    $layer = $target;
                } else {
                    $layer = $target.closest('.n2_timeline_layer__animations');
                    if (!$layer.length) {
                        return false;
                    }
                }
            }
        }

        return $layer.data('layer');
    };

    return LayerNavigationInteraction;
});_N2.d('LayerWindow', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param fragmentEditor
     * @constructor
     */
    function LayerWindow(fragmentEditor) {

        this.admin = $('#n2-admin');

        this.$editorOverlay = $('.n2_admin_editor_overlay');

        this.sidebar = $('#n2-ss-layer-window');

        this.title = this.sidebar.find('.n2_ss_layer_window__title_inner');

        this.$inner = this.sidebar.find('.n2_ss_layer_window__crop');

        /**
         * @type {_N2.FragmentEditor}
         */
        this.fragmentEditor = fragmentEditor;

        /**
         * @type {_N2.LayerWindowTab}
         */
        this.tab = new _N2.LayerWindowTab(this);

        this.initLayerList();

        var right = this.sidebar.find('.n2_ss_layer_window__title_nav_right');

        $('<div class="n2_ss_layer_window__title_nav_button"><i class="ssi_16 ssi_16--remove"></i></div>').on('click', (function (e) {
            this.hide();
        }).bind(this)).appendTo(right);

        new _N2.UILayerWindow(this.sidebar);

        this.initDeviceSwitcherShortcut();
    }

    LayerWindow.prototype.show = function (layer) {
        this.cancelHideRequest();
        this.setTitle(layer);
        $('body').addClass('n2_body--layer-window-visible');
    };

    LayerWindow.prototype.requestHide = function () {
        this.cancelHideRequest();
        this.hideRequestID = requestAnimationFrame(this.hide.bind(this));
    };

    LayerWindow.prototype.cancelHideRequest = function () {
        if (this.hideRequestID !== undefined) {
            cancelAnimationFrame(this.hideRequestID);
            delete this.hideRequestID;
        }
    };

    LayerWindow.prototype.hide = function () {
        delete this.hideRequestID;
        $('body').removeClass('n2_body--layer-window-visible');
    };

    LayerWindow.prototype.setTitle = function (layer) {
        this.title.html(layer.getName());
    };

    LayerWindow.prototype.initLayerList = function () {

        this.isLayerListAttached = false;

        var left = this.sidebar.find('.n2_ss_layer_window__title_nav_left');

        $('<div class="n2_ss_layer_window__title_nav_button n2_ss_layer_window__title_nav_button_layer_list"><i class="ssi_16 ssi_16--layer"></i></div>')
            .attr('data-n2tip', n2_('Layer List'))
            .on('click', this.toggleLayerList.bind(this)).appendTo(left);

        _N2._tooltip.add(left);

        this.detachLayerListClickCallback = this.detachLayerListClick.bind(this);
    };

    LayerWindow.prototype.toggleLayerList = function (e) {

        if (!this.isLayerListAttached) {
            this.attachLayerList();
        } else {
            this.detachLayerList();
        }

    };

    LayerWindow.prototype.attachLayerList = function () {

        $('body').addClass('n2_body--attached-layer-list');
        this.$editorOverlay.addClass('n2_admin_editor_overlay--attached-layer-list');
        this.fragmentEditor.layerNavigation.$container.appendTo(this.$inner);
        this.fragmentEditor.layerNavigation.onAttach(this);

        document.addEventListener('click', this.detachLayerListClickCallback, {
            passive: true,
            capture: true
        });
        this.isLayerListAttached = true;
    };

    LayerWindow.prototype.detachLayerList = function () {
        /**
         * We must dispatch a mouseover event to the layer navigation element to properly remove hover highlight when layer activated
         */
        this.fragmentEditor.layerNavigation.$container[0].dispatchEvent(new MouseEvent('mouseover', {view: window}));

        this.$editorOverlay.removeClass('n2_admin_editor_overlay--attached-layer-list');
        $('body').removeClass('n2_body--attached-layer-list');
        this.fragmentEditor.layerNavigation.$container.insertAfter(this.sidebar);
        this.fragmentEditor.layerNavigation.onDetach();

        document.removeEventListener('click', this.detachLayerListClickCallback, {
            passive: true,
            capture: true
        });

        this.isLayerListAttached = false;
    };

    LayerWindow.prototype.detachLayerListClick = function (e) {
        if (!$.contains(this.sidebar[0], e.target) && !this.sidebar.is(e.target) && !$(e.target).closest('.n2_context_menu').length) {
            this.detachLayerList();
        }
    };


    LayerWindow.prototype.initDeviceSwitcherShortcut = function () {

        var $devicespecific = $('<div id="n2-ss-devicespecific-settings"></div>'),
            modes = this.fragmentEditor.editor.getAvailableDeviceModes(),
            icons = {
                'desktopLandscape': 'ssi_16--desktoplandscape',
                'desktopPortrait': 'ssi_16--desktopportrait',
                'tabletLandscape': 'ssi_16--tabletportraitlarge',
                'tabletPortrait': 'ssi_16--tabletportrait',
                'mobileLandscape': 'ssi_16--mobileportraitlarge',
                'mobilePortrait': 'ssi_16--mobileportrait',
            };

        for (var device in modes) {
            if (modes[device]) {
                $devicespecific.append('<i class="ssi_16 ' + icons[device] + '" data-device="' + device + '"></i>');
            }
        }
        var cb = {
            'mouseenter': (function (e) {
                $devicespecific.appendTo(e.currentTarget);
            }).bind(this),
            'mouseleave': (function (e) {
                $devicespecific.detach();
            }).bind(this)
        };

        var $label = this.sidebar.find('[data-devicespecific] label').parent();
        var $miniDesktopPortrait = $('<div class="n2_field__label_modifier ssi_16 ssi_16--minidesktopportrait"></div>').on(cb);

        $label
            .prepend($miniDesktopPortrait);


        $devicespecific.find('.ssi_16').on({
            'click': (function (e) {
                e.preventDefault();
                var $target = $(e.currentTarget);
                $('.n2_device_tester_devices__device[data-device="' + $target.data('device') + '"]').trigger('click')
            }).bind(this)
        });
    };

    return LayerWindow;
});_N2.d('LayerWindowTab', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.LayerWindow} layerWindow
     * @constructor
     */
    function LayerWindowTab(layerWindow) {

        this.layerWindow = layerWindow;

        this.activeTabName = '';
        this.activePanelNames = [];

        this.$buttons = $('.n2_ss_layer_window__tab_button');
        this.$buttonsContainer = this.$buttons.parent();

        this.$scrollable = $('.n2_ss_layer_window__tab_container');

        this.$tabs = $('.n2_ss_layer_window__tab');

        this.valuesDict = {};
        for (var i = 0; i < this.$buttons.length; i++) {
            var $button = this.$buttons.eq(i),
                tabName = $button.data('related-tab');

            this.valuesDict[tabName] = {
                index: i,
                $button: $button,
                $tab: this.$tabs.filter('[data-tab="' + tabName + '"]')
            };

            $button.on('click', this.displayTab.bind(this, tabName));
        }

        this.panels = {};
        var $panels = this.$tabs.find('.n2_ss_layer_window__tab_panel');
        for (var i = 0; i < $panels.length; i++) {
            var name = $panels.eq(i).data('panel');
            if (this.panels[name] === undefined) {
                this.panels[name] = $panels.eq(i);
            } else {
                this.panels[name] = this.panels[name].add($panels[i]);
            }
        }

    }

    LayerWindowTab.prototype.displayTab = function (tabName, e) {
        if (e) {
            e.preventDefault();
        }

        _N2._basicCSS.deActivate();

        if (this.activeTabName !== tabName && this.valuesDict[tabName] && this.valuesDict[tabName].$tab.hasClass('n2_ss_layer_window__tab--allowed')) {
            this.$buttons.removeClass('n2_ss_layer_window__tab_button--active');
            this.$tabs.removeClass('n2_ss_layer_window__tab--active');

            this.valuesDict[tabName].$button.addClass('n2_ss_layer_window__tab_button--active');
            this.valuesDict[tabName].$tab.addClass('n2_ss_layer_window__tab--active');

            this.activeTabName = tabName;

            this.$scrollable.scrollTop(0);
        }
    };

    LayerWindowTab.prototype.setPanel = function (panelNames, allowedTabs) {
        var i;

        for (i = 0; i < this.activePanelNames.length; i++) {
            this.panels[this.activePanelNames[i]].removeClass('n2_ss_layer_window__tab_panel--active');
        }

        this.activePanelNames = panelNames;
        for (i = 0; i < this.activePanelNames.length; i++) {
            this.panels[this.activePanelNames[i]].addClass('n2_ss_layer_window__tab_panel--active');
        }

        this.setAllowedTabs(allowedTabs)
    };

    LayerWindowTab.prototype.setAllowedTabs = function (allowedTabs) {

        this.$buttons.removeClass('n2_ss_layer_window__tab_button--allowed');

        for (var k in this.valuesDict) {
            this.valuesDict[k].$tab.removeClass('n2_ss_layer_window__tab--allowed');
        }

        var isActiveAllowed = false;
        for (var i = 0; i < allowedTabs.length; i++) {

            var tabName = allowedTabs[i];
            if (this.valuesDict[tabName] !== undefined) {

                if (tabName === this.activeTabName) {
                    isActiveAllowed = true;
                }
                this.valuesDict[tabName].$button.addClass('n2_ss_layer_window__tab_button--allowed');
                this.valuesDict[tabName].$tab.addClass('n2_ss_layer_window__tab--allowed');
            }
        }

        if (!isActiveAllowed) {
            this.valuesDict[allowedTabs[0]].$button.trigger('click');
        }

        this.$buttonsContainer.attr('data-visible-tabs', allowedTabs.length);
    };

    LayerWindowTab.prototype.getActiveTabName = function () {

        return this.activeTabName;
    };

    return LayerWindowTab;
});_N2.d('PositionDisplay', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function PositionDisplay() {
        this.currentSource = '';
        _ready(this._ready.bind(this));
    }

    /**
     * @private
     */
    PositionDisplay.prototype._ready = function () {
        this.$body = $('body');
        this.$el = $('<div class="n2_tooltip n2_tooltip_position"></div>')
            .appendTo('.n2_admin_editor_overlay,.n2_admin');
    };

    PositionDisplay.prototype.show = function (source) {
        if (this.currentSource === '') {
            this.currentSource = source;
            this.$el.addClass('n2_tooltip--active');
            this.$body.addClass('n2_body--tooltip-position-active');
        }
    };

    PositionDisplay.prototype.update = function (e, source, html) {
        if (this.currentSource === source) {

            if (html !== undefined) {
                this.$el.html(html);
            }

            this.$el
                .css({
                    left: (e.clientX + 10) + 'px',
                    top: (e.clientY + 10) + 'px'
                });
        }
    };

    PositionDisplay.prototype.hide = function (source) {
        if (this.currentSource === source || source === undefined) {
            this.$body.removeClass('n2_body--tooltip-position-active');
            this.$el.removeClass('n2_tooltip--active');
            this.currentSource = '';
        }

    };


    /**
     * @returns {_N2.PositionDisplay}
     */
    PositionDisplay.get = function () {
        var positionDisplay = new PositionDisplay();
        PositionDisplay.get = function () {
            return positionDisplay;
        };
        return positionDisplay;
    };

    return PositionDisplay;
});_N2.d('Ruler', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.EditorAbstract} editor
     * @param stored
     * @constructor
     */
    function Ruler(editor, stored) {

        this.editor = editor;

        this.showGuides = 1;
        /**
         *
         * @type Guide[]
         */
        this.guides = [];

        this.scale = 10;

        this.$scrollArea = $('.n2_admin_editor__content_inner');

        $('.n2_ruler_corner').on('click', (function () {
            this.editor.fragmentEditor.canvasSettings.toggle('n2-ss-ruler-enabled');
        }).bind(this));

        var scrollOptions,
            scrollOptionsTimeout,
            scrolledElement = this.$scrollArea[0],
            getScrollOptions = function () {
                if (scrollOptions === undefined) {
                    scrollOptions = {
                        top: scrolledElement.scrollTop,
                        left: scrolledElement.scrollLeft,
                        behavior: 'auto'
                    };
                } else {

                }
                if (scrollOptionsTimeout) {
                    clearTimeout(scrollOptionsTimeout);
                }
                scrollOptionsTimeout = setTimeout(function () {
                    scrollOptions = undefined;
                }, 500);

                return scrollOptions;
            };

        this.vertical = $('.n2_ruler--vertical')
            .on('wheel', (function (e) {

                e.preventDefault();

                var ratio = 1;
                if (e.originalEvent.deltaMode === 1 || e.originalEvent.deltaMode === 2) {
                    ratio = 40;
                }

                var options = getScrollOptions();

                if (e.originalEvent.deltaY) {
                    if (e.originalEvent.shiftKey) {
                        options.left += e.originalEvent.deltaY * ratio;
                    } else {
                        options.top += e.originalEvent.deltaY * ratio;
                    }
                }
                if (e.originalEvent.deltaX) {
                    options.left += e.originalEvent.deltaX * ratio;
                }

                options.left = Math.min(Math.max(0, options.left), scrolledElement.scrollWidth - scrolledElement.clientWidth);
                options.top = Math.min(Math.max(0, options.top), scrolledElement.scrollHeight - scrolledElement.clientHeight);

                scrolledElement.scrollTo(options);
            }).bind(this));
        this.$verticalInner = this.vertical.find('.n2_ruler__inner');

        this.horizontal = $('.n2_ruler--horizontal')
            .on('wheel', (function (e) {
                e.preventDefault();

                var ratio = 1;
                if (e.originalEvent.deltaMode === 1 || e.originalEvent.deltaMode === 2) {
                    ratio = 40;
                }

                var options = getScrollOptions();

                if (e.originalEvent.deltaY) {
                    if (e.originalEvent.shiftKey) {
                        options.left += e.originalEvent.deltaY * ratio;
                    } else {
                        options.top += e.originalEvent.deltaY * ratio;
                    }
                }
                if (e.originalEvent.deltaX) {
                    options.left += e.originalEvent.deltaX * ratio;
                }

                options.left = Math.min(Math.max(0, options.left), scrolledElement.scrollWidth - scrolledElement.clientWidth);
                options.top = Math.min(Math.max(0, options.top), scrolledElement.scrollHeight - scrolledElement.clientHeight);

                scrolledElement.scrollTo(options);
            }).bind(this))
            .appendTo('.n2_admin_editor_overlay__middle_center');

        this.$horizontalInner = this.horizontal.find('.n2_ruler__inner');

        this.verticalMarks = $();
        this.horizontalMarks = $();

        this.onResize();
        $(window).on('resize', this.onResize.bind(this));

        this.$scrollArea.on('scroll', this.syncScroll.bind(this));
        this.syncScroll();

        this.$horizontalInner.on('mousedown', (function (e) {
            if (this.showGuides) {
                new GuideHorizontal(this, this.horizontal, this.$horizontalInner, e);
            }
        }).bind(this));


        this.$verticalInner.on('mousedown', (function (e) {
            if (this.showGuides) {
                new GuideVertical(this, this.vertical, this.$verticalInner, e);
            }
        }).bind(this));

        try {
            stored = _Assign({vertical: [], horizontal: []}, JSON.parse(_N2.Base64.decode(stored)));
            for (var i = 0; i < stored.horizontal.length; i++) {
                var guide = new GuideHorizontal(this, this.horizontal, this.$horizontalInner);
                guide.setPosition(stored.horizontal[i]);
            }
            for (var i = 0; i < stored.vertical.length; i++) {
                var guide = new GuideVertical(this, this.vertical, this.$verticalInner);
                guide.setPosition(stored.vertical[i]);
            }
        } catch (e) {
        }

        this.measureToolVertical();
        this.measureToolHorizontal();
    }

    Ruler.prototype.addGuide = function (guide) {
        this.guides.push(guide);
    };

    Ruler.prototype.removeGuide = function (guide) {
        this.guides.splice(this.guides.indexOf(guide), 1);
    };

    Ruler.prototype.clearGuides = function () {
        for (var i = this.guides.length - 1; i >= 0; i--) {
            this.guides[i].delete();
        }
    };

    Ruler.prototype.syncScroll = function () {
        this.horizontal[0].scrollLeft = this.$scrollArea[0].scrollLeft;
        this.vertical[0].scrollTop = this.$scrollArea[0].scrollTop;
    };

    Ruler.prototype.onResize = function () {

        var width = this.$scrollArea[0].scrollWidth,
            height = this.$scrollArea[0].scrollHeight;

        var targetMarkCountHorizontal = Math.round(Math.max(width + 100, this.$scrollArea.width() - this.vertical.width() - 30) / this.scale);
        for (var i = this.horizontalMarks.length; i < targetMarkCountHorizontal; i++) {
            var mark = $('<div class="n2_ruler_mark"></div>')
                .appendTo(this.$horizontalInner);
            if (i % 10 === 0) {
                mark.addClass('n2_ruler_mark--large')
                    .append('<div class="n2_ruler_mark__label">' + ((i / 10) * 100) + '</div>');
            } else {
                mark.addClass('n2_ruler_mark--small');
            }
        }

        for (i = targetMarkCountHorizontal; i < this.horizontalMarks.length; i++) {
            this.horizontalMarks.eq(i).remove();
        }

        this.horizontalMarks = this.$horizontalInner.find('.n2_ruler_mark');


        var targetMarkCountVertical = Math.round((height + 100) / this.scale);
        for (var i = this.verticalMarks.length; i < targetMarkCountVertical; i++) {
            var mark = $('<div class="n2_ruler_mark"></div>')
                .appendTo(this.$verticalInner);
            if (i % 10 === 0) {
                mark.addClass('n2_ruler_mark--large')
                    .append('<div class="n2_ruler_mark__label">' + ((i / 10) * 100) + '</div>');
            } else {
                mark.addClass('n2_ruler_mark--small');
            }
        }

        for (i = targetMarkCountVertical; i < this.verticalMarks.length; i++) {
            this.verticalMarks.eq(i).remove();
        }


        this.verticalMarks = this.$verticalInner.find('.n2_ruler_mark');
    };

    Ruler.prototype.toArray = function () {
        var data = {
            horizontal: [],
            vertical: []
        };
        for (var i = 0; i < this.guides.length; i++) {
            if (this.guides[i] instanceof GuideHorizontal) {
                data.horizontal.push(this.guides[i].position);
            } else if (this.guides[i] instanceof GuideVertical) {
                data.vertical.push(this.guides[i].position);
            }
        }
        return data;
    };

    Ruler.prototype.measureToolVertical = function () {
        var guide = $('<div class="n2_ruler__guide n2_ruler__guide--measure"><div class="n2_ruler__guide_border"></div></div>')
            .css('display', 'none')
            .appendTo(this.vertical);

        var guideVisible = false,
            showGuide = (function () {
                if (!guideVisible) {
                    guideVisible = true;
                    guide.css('display', '');
                    _N2.PositionDisplay.get().show('Guide');
                }
            }).bind(this),
            hideGuide = (function () {
                if (guideVisible) {
                    guideVisible = false;
                    guide.css('display', 'none');
                    _N2.PositionDisplay.get().hide('Guide');
                }
            }).bind(this),
            lastE,
            mouseMove = (function (e) {
                lastE = e;

                var pos = Math.max(e.pageY - this.$verticalInner[0].getBoundingClientRect().top, 0);
                guide.css('top', pos + 'px');
                _N2.PositionDisplay.get().update(e, 'Guide', (pos) + 'px');
            }).bind(this);

        this.vertical.on({
            mouseenter: (function (e) {
                if (!this.showGuides) return;

                showGuide();

                this.vertical.on({
                    'mousemove.n2-ruler-measure-tool': mouseMove,
                    'scroll.n2-ruler-measure-tool': function () {
                        mouseMove(lastE);
                    }
                });
            }).bind(this),
            mouseleave: (function () {
                this.vertical.off('.n2-ruler-measure-tool');
                hideGuide();
            }).bind(this)
        });
    };

    Ruler.prototype.measureToolHorizontal = function () {
        var guide = $('<div class="n2_ruler__guide n2_ruler__guide--measure"><div class="n2_ruler__guide_border"></div></div>')
            .css('display', 'none')
            .appendTo(this.horizontal);

        var guideVisible = false,
            showGuide = (function () {
                if (!guideVisible) {
                    guideVisible = true;
                    guide.css('display', '');
                    _N2.PositionDisplay.get().show('Guide');
                }
            }).bind(this),
            hideGuide = (function () {
                if (guideVisible) {
                    guideVisible = false;
                    guide.css('display', 'none');
                    _N2.PositionDisplay.get().hide('Guide');
                }
            }).bind(this),
            lastE,
            mouseMove = (function (e) {
                lastE = e;

                var pos = Math.max(e.pageX - this.$horizontalInner[0].getBoundingClientRect().left, 0);
                guide.css('left', pos + 'px');
                _N2.PositionDisplay.get().update(e, 'Guide', pos + 'px');
            }).bind(this);

        this.horizontal.on({
            mouseenter: (function (e) {
                if (!this.showGuides) return;

                showGuide();

                this.horizontal.on({
                    'mousemove.n2-ruler-measure-tool': mouseMove,
                    'scroll.n2-ruler-measure-tool': function () {
                        mouseMove(lastE);
                    }
                });
            }).bind(this),
            mouseleave: (function () {
                this.horizontal.off('.n2-ruler-measure-tool');
                hideGuide();
            }).bind(this)
        });
    };

    /**
     *
     * @param {_N2.Ruler} ruler
     * @param {jQuery} container
     * @param {jQuery} containerInner
     * @param e
     * @constructor
     * @abstract
     */
    function Guide(ruler, container, containerInner, e) {
        this.ruler = ruler;
        this.container = container;
        this.containerInner = containerInner;
        this.position = 0;

        this.guide = $('<div class="n2_ruler__guide n2-ruler-user-guide"><div class="n2_ruler__guide_border"></div></div>')
            .appendTo(container);

        $('<div class="n2_ruler__guide_handle"><i class="ssi_missing ssi_16 ssi_16--remove"></i></div>')
            .on('mousedown', (function (e) {
                e.preventDefault();
                e.stopPropagation();

                this.delete();
            }).bind(this))
            .appendTo(this.guide);

        this.ruler.addGuide(this);

        if (e) {
            this.create(e);
        }
    }

    Guide.prototype._position = function (position, e) {
        return Math.max(0, position);
    };

    Guide.prototype.setPosition = function (position) {
        this.position = position;
        this.positionRender(this.position);
    };

    Guide.prototype.delete = function () {
        this.ruler.removeGuide(this);
        this.guide.remove();
    };

    /**
     * @abstract
     * @param e
     */
    Guide.prototype.create = function (e) {

    };

    /**
     *
     * @constructor
     * @augments Guide
     */
    function GuideHorizontal() {
        Guide.prototype.constructor.apply(this, arguments);
    }

    GuideHorizontal.prototype = Object.create(Guide.prototype);
    GuideHorizontal.prototype.constructor = GuideHorizontal;

    GuideHorizontal.prototype.create = function (e) {

        this.position = this._position((e.pageX - this.containerInner[0].getBoundingClientRect().left), e);
        this.positionRender(this.position);
    };

    GuideHorizontal.prototype.rawPositionRender = function (value) {
        this.guide.css('left', Math.max(0, value));
    };

    GuideHorizontal.prototype.positionRender = function (value) {
        this.guide.css('left', Math.max(0, value));
    };

    /**
     *
     * @constructor
     * @augments Guide
     */
    function GuideVertical() {
        Guide.prototype.constructor.apply(this, arguments);
    }

    GuideVertical.prototype = Object.create(Guide.prototype);
    GuideVertical.prototype.constructor = GuideVertical;

    GuideVertical.prototype.create = function (e) {
        this.position = this._position((e.pageY - this.containerInner[0].getBoundingClientRect().top), e);
        this.positionRender(this.position);
    };

    GuideVertical.prototype.rawPositionRender = function (value) {
        this.guide.css('top', Math.max(0, value));
    };

    GuideVertical.prototype.positionRender = function (value) {
        this.guide.css('top', Math.max(0, value));
    };

    return Ruler;
});_N2.d('CanvasSettings', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @constructor
     */
    function CanvasSettings(fragmentEditor) {


        this.$button = $('<a class="n2_button_plain_icon n2_button_plain_icon--medium" href="#"><i class="ssi_24 ssi_24--more"></i></a>')
            .on('click', this.onContextMenu.bind(this))
            .prependTo('.n2_add_layer__bar_bottom');

        _N2._tooltip.addElement(this.$button, n2_('Editor settings'), 10, -20);


        /**
         * @type {_N2.FragmentEditor}
         */
        this.fragmentEditor = fragmentEditor;

        this.settings = {};
        this.callbacks = {};

        this.guideSubItems = [];

        this.snapTo();
        this.roundTo();
        this.startRuler();
    }

    CanvasSettings.prototype.onContextMenu = function (e) {
        e.preventDefault();

        this.$button.addClass('n2_button_plain_icon--active');

        var items = [];
        items.push({
            priority: 10,
            label: n2_('Upgrade to Pro'),
            icon: 'ssi_16 ssi_16--smart',
            action: (function (settingsGoProUrl) {
                window.open(settingsGoProUrl, '_blank');
            }).bind(this, this.fragmentEditor.editor.options.settingsGoProUrl)
        });
    

        items.push({
            priority: 30,
            label: n2_('Guide settings'),
            icon: 'ssi_16 ssi_16--grid',
            items: this.guideSubItems
        });

        items.push({
            priority: 50,
            label: n2_('Keyboard shortcuts'),
            icon: 'ssi_16 ssi_16--keyboard',
            action: function () {
                _N2.ModalHotkey.show();
            }
        });

        _N2.ContextMenu(this.$button[0], items, {
            verticalAlign: 'bottom',
            x: 35,
            y: -30,
            onClose: (function () {
                this.$button.removeClass('n2_button_plain_icon--active');
            }).bind(this)
        });
    };

    CanvasSettings.prototype._addSettings = function (hash, label, _default, cb) {
        this.settings[hash] = parseInt(localStorage.getItem(hash) || _default);
        this.callbacks[hash] = cb;

        this.guideSubItems.push({
            type: 'onoff',
            label: label,
            isOn: (function (hash) {
                return this.settings[hash];
            }).bind(this, hash),
            action: (function (hash, isOn) {
                this.set(hash, isOn);
            }).bind(this, hash)
        });

        cb(this.settings[hash], true);
    };

    CanvasSettings.prototype._addAction = function (id, label, icon, action) {

        this.guideSubItems.push({
            label: label,
            icon: icon,
            action: action
        });
    };

    CanvasSettings.prototype.set = function (name, value) {
        value = value ? 1 : 0;

        this.settings[name] = value;
        localStorage.setItem(name, value);
        this.callbacks[name].call(this, value, false);
    };

    CanvasSettings.prototype.toggle = function (name) {
        this.set(name, !this.settings[name]);
    };

    CanvasSettings.prototype.get = function (name) {
        return this.settings[name];
    };

    CanvasSettings.prototype.snapTo = function () {

        this._addSettings("n2-ss-snap-to-enabled", n2_('Smart snap'), 1, function (value) {

        });
    };

    CanvasSettings.prototype.roundTo = function () {

        this._addSettings("n2-ss-round-to-enabled", n2_('Round to 5px'), 1, function (value) {
            if (value === 1) {
                _N2._roundTo = 5;
            } else {
                _N2._roundTo = 1;
            }
        });
    };

    CanvasSettings.prototype.startRuler = function () {
        this.ruler = new _N2.Ruler(this.fragmentEditor.editor, $('#slideguides').val());

        this._addSettings("n2-ss-ruler-enabled", n2_('Ruler'), 1, (function (value) {
            $('body').toggleClass('n2_body--ruler', value === 1);
        }).bind(this));

        this._addAction('n2-ss-clear-guides', n2_('Clear guides'), 'ssi_16 ssi_16--delete', (function () {
            this.ruler.clearGuides();
        }).bind(this))
    };

    return CanvasSettings;
});_N2.d('UICanvasItem', ['$', 'UIMouse'], function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @class
     * @constructor
     * @augments {_N2.UIMouse}
     */
    function UICanvasItem($element, options) {
        this.element = $element;

        this.widgetName = this.widgetName || 'UICanvasItem';
        this.widgetEventPrefix = "canvasItem";

        this.options = _Assign({
            canvasUIManager: null,
            layer: false,
            $layer: null,
            distance: 5
        }, this.options, options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UICanvasItem.prototype = Object.create(_N2.UIMouse.prototype);
    UICanvasItem.prototype.constructor = UICanvasItem;

    UICanvasItem.prototype.create = function () {

        if (typeof this.options.$layer === 'function') {
            this.options.$layer = this.options.$layer.call(this, this);
        }

        this._mouseInit();
    };

    UICanvasItem.prototype._mouseCapture = function (e, overrideHandle) {

        return this.options.canvasUIManager._mouseCapture(e, overrideHandle);
    };

    UICanvasItem.prototype._mouseStart = function (event, overrideHandle, noActivation) {
        var ret = this.options.canvasUIManager._mouseStart(event, this.options.$layer, this.options.layer);

        this._trigger('start');

        return ret;
    };

    UICanvasItem.prototype._mouseDrag = function (event) {
        return this.options.canvasUIManager._mouseDrag(event);
    };

    UICanvasItem.prototype._mouseStop = function (event, noPropagation) {
        this._trigger('end');
        var ret = this.options.canvasUIManager._mouseStop(event, noPropagation);

        _N2.WindowManager.actionPrevent('ComponentActivate');
        return ret;
    };

    UICanvasItem.prototype.destroy = function () {
        this._mouseDestroy();

        return this;
    };

    return UICanvasItem;
});_N2.d('UICanvas', ['$', 'UIWidgetBase'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @class
     * @constructor
     * @param  $element
     * @param {_N2.FragmentEditor} fragmentEditor
     * @augments {_N2.UIWidgetBase}
     */
    function UICanvas($element, fragmentEditor) {
        this.element = $element;
        this.fragmentEditor = fragmentEditor;

        this.widgetName = this.widgetName || 'UICanvas';

        $(this.element).data('UICanvas', this);

        this.widgetEventPrefix = "canvas";

        this._scrollCallback = this._scroll.bind(this);

        _N2.UIWidgetBase.prototype.constructor.apply(this, arguments);
    }

    UICanvas.prototype = Object.create(_N2.UIWidgetBase.prototype);
    UICanvas.prototype.constructor = UICanvas;

    UICanvas.prototype.getScrollOffset = function () {
        return this.getScrollOffsetRecursive(this.element[0], {
            top: 0,
            left: 0
        });
    };

    UICanvas.prototype.getScrollOffsetRecursive = function (el, offset) {
        if (el === null || el.scrollTop === undefined || el.scrollLeft === undefined) {
            return offset;
        }
        offset.top += el.scrollTop;
        offset.left += el.scrollLeft;

        return this.getScrollOffsetRecursive(el.parentNode, offset);
    };

    UICanvas.prototype._scroll = function () {
        var currentScrollOffset = this.getScrollOffset();
        this.interaction.setScrollOffset(currentScrollOffset.top, currentScrollOffset.left);
    };

    UICanvas.prototype._mouseCapture = function (e) {
        return $(e.target).closest(".nui_resize_absolute__handle, .nui_resize_normal__handle, .nui_spacing__handle").length === 0;
    };

    UICanvas.prototype._mouseStart = function (e, $layer, layer) {

        document.addEventListener('scroll', this._scrollCallback, {
            passive: true,
            capture: true
        });

        _N2.MW.___getUMW(document.body).addClass('n2_body--drag-layer');

        this.interaction = new Interaction(this, e, $layer, layer, this.fragmentEditor.mainContainer);

        this.interaction.drag(e);

    };

    UICanvas.prototype._mouseDrag = function (e) {

        if (this.interaction) this.interaction.drag(e);
    };

    UICanvas.prototype._mouseStop = function (e) {

        document.removeEventListener('scroll', this._scrollCallback, {
            passive: true,
            capture: true
        });

        if (this.interaction) this.interaction.stop(e);

        _N2.MW.___getUMW(document.body).removeClass('n2_body--drag-layer');

        delete this.interaction;
    };

    UICanvas.prototype.cancel = function () {
        delete this.interaction;
    };

    /**
     *
     * @param {_N2.UICanvas} uiCanvas
     * @param event
     * @param $layer
     * @param layer
     * @param mainContainer
     * @constructor
     */
    function Interaction(uiCanvas, event, $layer, layer, mainContainer) {

        this.uiCanvas = uiCanvas;
        /**
         * @type {_N2.FragmentEditor}
         */
        this.fragmentEditor = uiCanvas.fragmentEditor;

        this.onKeyDownCallback = this.onKeyDown.bind(this);

        this.fragmentEditor.pointerManager.disable();

        this.context = {
            action: 'move',
            placement: 'default',
            placeholder: $('<div class="n2-ss-layer-placeholder"></div>'),
            scroll: this.uiCanvas.getScrollOffset(),
            scrollOffsetModifier: {
                left: 0,
                top: 0
            },
            mouse: {
                offset: {
                    left: event.pageX,
                    top: event.pageY
                }
            },
            $layer: $layer,
            layer: layer
        };

        var css = {
            position: 'absolute',
            left: 0,
            top: 0,
            right: 'auto',
            bottom: 'auto'
        };


        if (!layer) {
            this.context.action = 'create';
            this.context.placement = event.ctrlKey || event.metaKey ? 'absolute' : this.fragmentEditor.addLayer.getPosition();

            this.context.relativeToGroup = mainContainer;

            this.context.$relativeToParent = this.context.relativeToGroup.layer;

            this.context.layerOffset = {
                left: 0,
                top: 0
            };
        } else {
            if (layer.placement.getType() === 'absolute') {
                this.context.placement = 'absolute';
                this.context.relativeToGroup = layer.group;

                this.context.$relativeToParent = $layer.parent();
            } else {
                this.context.relativeToGroup = mainContainer;

                this.context.$relativeToParent = this.context.relativeToGroup.layer;
            }


            this.context.layerOffset = $layer.offset();

            this.context.originalIndex = layer.getIndex();

            if (this.context.placement === 'default') {

                /**
                 * .width() is an integer and floating part has been cut. So increment the value with 1 to prevent unwanted wrap
                 */
                css.width = $layer[0].offsetWidth + 1;
            }
        }

        this.context.canvas = {
            offset: this.context.$relativeToParent.offset(),
            size: {
                width: this.context.$relativeToParent.outerWidth(),
                height: this.context.$relativeToParent.outerHeight()
            }
        };


        if (!layer) {
            $layer.appendTo('body');
        } else {

            _N2.MW.___getUMW($layer[0]).addClass('n2_layer_drag__dragging');

            if (this.context.placement === 'default') {

                this.context.$clone = $('<div class="n2-ss-layer-clone"></div>')
                    .height($layer.outerHeight(true))
                    .insertAfter($layer);

                $layer.appendTo(mainContainer.layer);
            }
        }

        _N2.MW.___getUMW($layer[0]).addClass('n2-ss-layer--drag');
        $layer.css(css);

        this.margins = {
            left: (+$layer.css("marginLeft") || 0),
            top: (+$layer.css("marginTop") || 0)
        };

        this.context.size = {
            width: $layer.outerWidth(),
            height: $layer.outerHeight()
        };

        this.context.droppables = mainContainer.getDroppables(this.context.placement, layer);

        if (this.context.placement === 'default') {
            for (var i = 0; i < this.context.droppables.length; i++) {
                _N2.MW.___getUMW(this.context.droppables[i].layer.layer[0]).addClass('n2_layer_drag_target_groups');
            }
        }

        this._cacheContainers();

        this.snapStart();

        document.addEventListener("keydown", this.onKeyDownCallback, {capture: true});
    }

    Interaction.prototype.drag = function (event) {

        this.lastEvent = event;

        var position,
            cursorPosition = {
                x: event.pageX - this.context.scrollOffsetModifier.left,
                y: event.pageY - this.context.scrollOffsetModifier.top
            };
        if (this.context.action === 'create') {
            position = {
                top: event.pageY - this.context.canvas.offset.top - 20,
                left: event.pageX - this.context.canvas.offset.left - 20
            };
        } else {
            position = {
                top: this.context.layerOffset.top - this.context.canvas.offset.top + event.pageY - this.context.mouse.offset.top - this.context.scrollOffsetModifier.top - this.margins.top,
                left: this.context.layerOffset.left - this.context.canvas.offset.left + event.pageX - this.context.mouse.offset.left - this.context.scrollOffsetModifier.left - this.margins.left
            };
        }

        var targetContainer = this._findInnerContainer(cursorPosition.x, cursorPosition.y);
        if (targetContainer === false && this.context.action === 'move') {
            targetContainer = this.context.droppables[0];
        }
        if (targetContainer) {
            if (targetContainer.placement === 'normal') {

                if (targetContainer.layers === undefined) {
                    targetContainer.layers = this._cacheContainerLayers(targetContainer);
                }

                var targetIndex = this._findNormalIndex(cursorPosition, targetContainer);
                if (targetIndex > 0) {
                    this.context.placeholder.css('order', targetContainer.layers[targetIndex - 1].layer.layer.css('order'));
                    this.context.placeholder.insertAfter(targetContainer.layers[targetIndex - 1].layer.layer);
                } else {
                    this.context.placeholder.css('order', 0);
                    this.context.placeholder.prependTo(targetContainer.$container);
                }

                this.context.targetIndex = targetIndex;
            } else {
                this.context.placeholder.detach();
            }
        } else {
            this.context.placeholder.detach();
        }

        if (this.context.targetContainer && this.context.targetContainer !== targetContainer) {

            _N2.MW.___getUMW(this.context.targetContainer.layer.layer[0]).removeClass('n2_layer_drag_target_group');
        }

        this.context.targetContainer = targetContainer;

        if (targetContainer) {
            _N2.MW.___getUMW(targetContainer.layer.layer[0]).addClass('n2_layer_drag_target_group');
        }

        if (this.context.layer && this.context.placement === 'absolute') {
            /**
             * Redraw linked child layers
             */
            this.context.layer.placement.current.triggerLayerResized();
        }

        if (this.context.action === 'create') {
            position.left += this.context.canvas.offset.left;
            position.top += this.context.canvas.offset.top;
        }

        this.snapDrag(event, {
            left: position.left + this.context.canvas.offset.left,
            top: position.top + this.context.canvas.offset.top
        }, position);

        position.left = Math.round(position.left);
        position.top = Math.round(position.top);

        var smw = _N2.MW.___getSMW(this.context.$layer[0]);
        smw.xAbs = position.left - smw.xPAbs / 100 * this.context.$layer[0].offsetWidth;
        smw.yAbs = position.top - smw.yPAbs / 100 * this.context.$layer[0].offsetHeight;

        this.displayPosition(event, position);

        this.___lastPosition = position;
    };

    Interaction.prototype.stop = function (e) {

        var doneDeferred = $.Deferred();

        this.ended(doneDeferred);

        var targetIndex = this.context.targetIndex,
            targetContainer = this.context.targetContainer;

        if (this.context.action === 'create') {
            if (targetContainer) {
                this.fragmentEditor.itemEditor.createWithDrag(e, doneDeferred, this.context.placement, this.context.$layer, targetContainer, targetIndex);
            } else {
                doneDeferred.resolve();
            }
            this.context.$layer.detach();

        } else if (this.context.action === 'move') {

            if (targetContainer === undefined) {
                targetContainer = this.fragmentEditor.mainContainer.layer;
            }

            if (this.context.placement === 'absolute') {

                this.context.layer.placement.current.setPosition(this.___lastPosition.left, this.___lastPosition.top);

            } else if (this.context.placement === 'default') {

                var smw = _N2.MW.___getSMW(this.context.$layer[0]);
                smw.xAbs = 0;
                smw.yAbs = 0;

                _NodeRemoveStyles(this.context.$layer[0], [
                    'position',
                    'width',
                    'left',
                    'top',
                    'bottom',
                    'right'
                ]);

                switch (targetContainer.layer.type) {

                    case 'content':
                    case 'col':
                        if (targetIndex > 0) {
                            this.context.$layer.insertAfter(targetContainer.layers[targetIndex - 1].layer.layer);
                        } else {
                            this.context.$layer.prependTo(targetContainer.$container);
                        }

                        this.context.layer.onCanvasUpdate(this.context.originalIndex, targetContainer.layer, targetIndex);
                        break;
                }

            }

            doneDeferred.resolve();
        }

        if (this.context.layer) {
            this.context.layer.placement.current.triggerLayerResized();
        }
    };

    Interaction.prototype.ended = function (deferred) {

        document.removeEventListener("keydown", this.onKeyDownCallback, {capture: true});

        this.context.placeholder.remove();

        if (this.context.placement === 'default') {
            for (var i = 0; i < this.context.droppables.length; i++) {
                _N2.MW.___getUMW(this.context.droppables[i].layer.layer[0]).removeClass('n2_layer_drag_target_groups');
            }

            if (this.context.action === 'move') {
                this.context.$clone.remove();
            }
        }

        if (this.context.targetContainer) {
            _N2.MW.___getUMW(this.context.targetContainer.layer.layer[0]).removeClass('n2_layer_drag_target_group');
        }

        _N2.MW.___getUMW(this.context.$layer[0]).removeClass('n2-ss-layer--drag');

        _N2.MW.___getUMW(this.context.$layer[0]).removeClass('n2_layer_drag__dragging');

        this.snapStop();

        this.hidePosition();

        if (deferred !== undefined) {
            deferred.done((function () {
                this.fragmentEditor.pointerManager.enable();
            }).bind(this));
        } else {
            this.fragmentEditor.pointerManager.enable();

            this.uiCanvas.cancel();
        }

    };

    Interaction.prototype.refresh = function () {

        this.drag(this.lastEvent);
    };

    Interaction.prototype.setScrollOffset = function (scrollOffsetTop, scrollOffsetLeft) {

        this.context.scrollOffsetModifier.top = this.context.scroll.top - scrollOffsetTop;
        this.context.scrollOffsetModifier.left = this.context.scroll.left - scrollOffsetLeft;

        this.refresh();
    };

    Interaction.prototype.displayPosition = function (e, position) {
        if (this.context.targetContainer && this.context.targetContainer.placement === 'absolute') {

            _N2.PositionDisplay.get().show('Canvas');

            if (this.context.action === 'create') {
                position.left -= this.context.canvas.offset.left;
                position.top -= this.context.canvas.offset.top;
            }

            _N2.PositionDisplay.get().update(e, 'Canvas', 'L: ' + (parseInt(position.left) || 0) + 'px<br>T: ' + (parseInt(position.top) || 0) + 'px');

        } else {
            this.hidePosition();
        }
    };

    Interaction.prototype.hidePosition = function () {
        _N2.PositionDisplay.get().hide('Canvas');
    };

    Interaction.prototype._cacheContainers = function () {
        for (var i = 0; i < this.context.droppables.length; i++) {
            var obj = this.context.droppables[i];
            obj.offset = obj.$container.offset();
            obj.size = {
                width: obj.$container.outerWidth(),
                height: obj.$container.outerHeight()
            };
            obj.offset.right = obj.offset.left + obj.size.width;
            obj.offset.bottom = obj.offset.top + obj.size.height;
        }
    };

    Interaction.prototype._findInnerContainer = function (offsetLeft, offsetTop) {
        for (var i = this.context.droppables.length - 1; i >= 0; i--) {
            var obj = this.context.droppables[i];
            if (obj.offset.left <= offsetLeft && obj.offset.right >= offsetLeft && obj.offset.top <= offsetTop && obj.offset.bottom >= offsetTop) {
                return obj;
            }
        }
        return false;
    };

    Interaction.prototype._cacheContainerLayers = function (droppable) {
        var layerObjects = [],
            layers = droppable.layer.container.getSortedLayers();

        for (var i = 0; i < layers.length; i++) {
            var obj = {
                isAbsolute: layers[i].placement.getType() === 'absolute',
                layer: layers[i]
            };
            obj.offset = obj.layer.layer.offset();
            obj.size = {
                width: obj.layer.layer.outerWidth(),
                height: obj.layer.layer.outerHeight()
            };
            obj.offset.right = obj.offset.left + obj.size.width / 2;
            obj.offset.bottom = obj.offset.top + obj.size.height / 2;
            layerObjects.push(obj);
        }

        return layerObjects;
    };

    Interaction.prototype._findNormalIndex = function (cursorPosition, targetContainer) {
        var index = -1,
            obj,
            i;

        switch (targetContainer.axis) {
            case 'y':
                for (i = 0; i < targetContainer.layers.length; i++) {
                    obj = targetContainer.layers[i];
                    if (!obj.isAbsolute && cursorPosition.y <= obj.offset.bottom) {
                        index = i;
                        break;
                    }
                }
                break;
            case 'x':
                for (i = 0; i < targetContainer.layers.length; i++) {
                    obj = targetContainer.layers[i];
                    if (!obj.isAbsolute && cursorPosition.x <= obj.offset.right) {
                        index = i;
                        break;
                    }
                }
                break;
        }

        if (index === -1) {
            index = targetContainer.layers.length;
        }

        return index;
    };

    Interaction.prototype.onKeyDown = function (e) {
        if (e.code === 'Escape') {
            e.stopImmediatePropagation();
            e.preventDefault();

            if (this.context.action === 'move') {
                if (this.context.placement === 'absolute') {
                    this.context.layer.placement.updatePosition();
                } else if (this.context.placement === 'default') {

                    var smw = _N2.MW.___getSMW(this.context.$layer[0]);
                    smw.xAbs = 0;
                    smw.yAbs = 0;

                    _NodeRemoveStyles(this.context.$layer[0], [
                        'position',
                        'width',
                        'left',
                        'top',
                        'bottom',
                        'right'
                    ]);

                    this.context.$layer.insertAfter(this.context.$clone);
                }
            }

            this.ended();
        }
    };

    Interaction.prototype.snapStart = function () {

        if (this.context.action === 'move' && this.context.placement === 'absolute') {

            this.$gridHorizontal = $('<div class="n2_smartguide n2_smartguide--vertical"></div>').appendTo(this.context.$relativeToParent);
            this.$gridVertical = $('<div class="n2_smartguide n2_smartguide--horizontal"></div>').appendTo(this.context.$relativeToParent);

            this.elements = [];
            this.snapLayers = [];

            var guides = this.fragmentEditor.getSnap(this.context.relativeToGroup, this.context.layer);
            if (guides && guides.length) {
                guides.each((function (i, el) {
                    var rect = el.getBoundingClientRect(),
                        $t = $(el);

                    this.elements.push({
                        item: el,
                        width: rect.width,
                        height: rect.height,
                        top: Math.round(rect.top),
                        left: Math.round(rect.left),
                        backgroundColor: ''
                    });

                    if ($t.hasClass('n2-ss-layer')) {
                        this.snapLayers.push($t[0]);
                    }
                }).bind(this));
            }

            _N2.MW.___getUMW(this.snapLayers).addClass('n2_layer_drag_snap_to');

            var rect = this.context.$relativeToParent[0].getBoundingClientRect();

            this.elements.push({
                width: rect.width,
                height: rect.height,
                top: Math.round(rect.top),
                left: Math.round(rect.left),
                backgroundColor: '#ff4aff'
            });
        }
    };

    var tolerance = 5;

    Interaction.prototype.snapDrag = function (e, offset, position) {


        if (this.context.action === 'move' && this.context.placement === 'absolute') {

            var horizontalElement = false,
                verticalElement = false,
                horizontalTolerance = tolerance,
                verticalTolerance = tolerance;

            this.$gridVertical.css({"display": "none"});
            this.$gridHorizontal.css({"display": "none"});

            if (this.context.targetContainer && this.context.targetContainer.placement === 'absolute') {

                var ctrlKey = e.ctrlKey || e.metaKey,
                    altKey = e.altKey;
                if (ctrlKey && altKey) {
                    return;
                } else if (ctrlKey) {
                    horizontalElement = true;
                } else if (altKey) {
                    verticalElement = true;
                }
                var x1 = offset.left, x2 = x1 + this.context.size.width,
                    y1 = offset.top, y2 = y1 + this.context.size.height,
                    xc = (x1 + x2) / 2,
                    yc = (y1 + y2) / 2;

                if (!horizontalElement) {
                    for (var i = this.elements.length - 1; i >= 0; i--) {
                        if (horizontalTolerance === 0) break;

                        var l = this.elements[i].left,
                            r = l + this.elements[i].width,
                            hc = (l + r) / 2;

                        var v = true,
                            c;
                        if ((c = Math.abs(l - x2)) < horizontalTolerance) {
                            position.left = l - this.context.size.width - this.context.canvas.offset.left;
                            this.setSnapHorizontalLine(position.left + this.context.size.width);
                        } else if ((c = Math.abs(l - x1)) < horizontalTolerance) {
                            position.left = l - this.context.canvas.offset.left;
                            this.setSnapHorizontalLine(position.left);
                        } else if ((c = Math.abs(r - x1)) < horizontalTolerance) {
                            position.left = r - this.context.canvas.offset.left;
                            this.setSnapHorizontalLine(position.left);
                        } else if ((c = Math.abs(r - x2)) < horizontalTolerance) {
                            position.left = r - this.context.size.width - this.context.canvas.offset.left;
                            this.setSnapHorizontalLine(position.left + this.context.size.width);
                        } else if ((c = Math.abs(hc - x2)) < horizontalTolerance) {
                            position.left = hc - this.context.size.width - this.context.canvas.offset.left;
                            this.setSnapHorizontalLine(position.left + this.context.size.width);
                        } else if ((c = Math.abs(hc - x1)) < horizontalTolerance) {
                            position.left = hc - this.context.canvas.offset.left;
                            this.setSnapHorizontalLine(position.left);
                        } else if ((c = Math.abs(hc - xc)) < horizontalTolerance) {
                            position.left = hc - this.context.size.width / 2 - this.context.canvas.offset.left;
                            this.setSnapHorizontalLine(position.left + this.context.size.width / 2);
                        } else {
                            v = false;
                        }

                        if (v) {
                            horizontalElement = this.elements[i];
                            horizontalTolerance = Math.min(c, horizontalTolerance);
                        }
                    }
                }

                if (!verticalElement) {
                    for (var i = this.elements.length - 1; i >= 0; i--) {
                        if (verticalTolerance === 0) {
                            break;
                        }

                        var t = this.elements[i].top,
                            b = t + this.elements[i].height,
                            vc = (t + b) / 2;

                        var h = true,
                            c;
                        if ((c = Math.abs(t - y2)) < verticalTolerance) {
                            position.top = t - this.context.size.height - this.context.canvas.offset.top;
                            this.setSnapVerticalLine(position.top + this.context.size.height);
                        } else if ((c = Math.abs(t - y1)) < verticalTolerance) {
                            position.top = t - this.context.canvas.offset.top;
                            this.setSnapVerticalLine(position.top);
                        } else if ((c = Math.abs(b - y1)) < verticalTolerance) {
                            position.top = b - this.context.canvas.offset.top;
                            this.setSnapVerticalLine(position.top);
                        } else if ((c = Math.abs(b - y2)) < verticalTolerance) {
                            position.top = b - this.context.size.height - this.context.canvas.offset.top;
                            this.setSnapVerticalLine(position.top + this.context.size.height);
                        } else if ((c = Math.abs(vc - y2)) < verticalTolerance) {
                            position.top = vc - this.context.size.height - this.context.canvas.offset.top;
                            this.setSnapVerticalLine(position.top + this.context.size.height);
                        } else if ((c = Math.abs(vc - y1)) < verticalTolerance) {
                            position.top = vc - this.context.canvas.offset.top;
                            this.setSnapVerticalLine(position.top);
                        } else if ((c = Math.abs(vc - yc)) < verticalTolerance) {
                            position.top = vc - this.context.size.height / 2 - this.context.canvas.offset.top;
                            this.setSnapVerticalLine(position.top + this.context.size.height / 2);
                        } else {
                            h = false;
                        }

                        if (h) {
                            verticalElement = this.elements[i];
                            verticalTolerance = Math.min(c, verticalTolerance);
                        }
                    }
                }

                if (horizontalElement && horizontalElement !== true) {
                    this.$gridHorizontal.css('backgroundColor', horizontalElement.backgroundColor);
                }
                if (verticalElement && verticalElement !== true) {
                    this.$gridVertical.css('backgroundColor', verticalElement.backgroundColor);
                }
            }
        }
    };

    Interaction.prototype.setSnapHorizontalLine = function (left) {
        this.$gridHorizontal.css({left: left, display: "block"});
    };

    Interaction.prototype.setSnapVerticalLine = function (top) {
        this.$gridVertical.css({top: top, display: "block"});
    };

    Interaction.prototype.snapStop = function () {

        if (this.context.action === 'move' && this.context.placement === 'absolute') {

            _N2.MW.___getUMW(this.snapLayers).removeClass('n2_layer_drag_snap_to');

            this.$gridVertical.remove();
            this.$gridHorizontal.remove();
        }
    };

    return UICanvas;
});_N2.d('UIColumns', ['$', 'UIMouse'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @class
     * @constructor
     * @param  $element
     * @param {_N2.FragmentEditor} fragmentEditor
     * @param {{}} options
     * @augments {_N2.UIMouse}
     */
    function UIColumns($element, fragmentEditor, options) {

        this.element = $element;
        this.fragmentEditor = fragmentEditor;

        this.active = 0;
        this.created = false;
        this.invalidated = false;
        this.isHandlesVisible = false;

        this.widgetName = this.widgetName || 'UIColumns';
        this.widgetEventPrefix = "columns";

        this.options = _Assign({
            columns: '1',
            gutter: 0,
            denominators: {
                1: 100,
                2: 100,
                3: 144,
                4: 100,
                5: 100,
                6: 144
            },
            // Callbacks
            drag: null,
            start: null,
            stop: null
        }, this.options, options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);
    }

    UIColumns.prototype = Object.create(_N2.UIMouse.prototype);
    UIColumns.prototype.constructor = UIColumns;

    UIColumns.prototype.showHandles = function () {
        this.isHandlesVisible = true;
        if (this.handles) {
            this.handles.addClass('ui-column-width-handle--visible');
        }
    };

    UIColumns.prototype.hideHandles = function () {
        this.isHandlesVisible = false;
        if (this.handles) {
            this.handles.removeClass('ui-column-width-handle--visible');
        }
    };

    UIColumns.prototype.create = function () {
        if (!this.created) {
            this.created = true;

            this._setupHandles();

            $(window).on('resize', this._resize.bind(this));

            this._mouseInit();
        }
    };

    UIColumns.prototype.destroy = function () {

        this._mouseDestroy();
        this.element
            .removeData("uiNextendColumns")
            .off(".columns")
            .find("> .ui-column-width-handle")
            .remove();

        return this;
    };

    UIColumns.prototype.getDenominator = function (i) {
        if (this.options.denominators[i] === undefined) {
            this.options.denominators[i] = i * 15;
        }
        return this.options.denominators[i];
    };

    UIColumns.prototype._setupHandles = function () {
        var o = this.options, handle, i, n, axis;

        this.fractions = [];

        var columnWidths = o.columns.split('+');
        for (var i = 0; i < columnWidths.length; i++) {
            this.fractions.push(new Fraction(columnWidths[i]));
        }
        this.currentDenominator = this.getDenominator(this.fractions.length);

        var currentPercent = 0;
        for (i = 0; i < this.fractions.length - 1; i++) {
            axis = $("<div class='ui-column-width-handle'>");

            currentPercent += this.fractions[i].valueOf() * 100;
            axis
                .data('i', i)
                .data('percent', currentPercent)
                .appendTo(this.element)
                .on('mousedown', this._mouseDown.bind(this));
        }

        this.handles = this.element.find('> .ui-column-width-handle');
        this.handles.toggleClass('ui-column-width-handle--visible', this.isHandlesVisible);

        this._resize();
    };

    UIColumns.prototype._resize = function () {
        if (this.active) {
            this.paddingLeft = parseInt(this.element.css('paddingLeft'));
            this.paddingRight = parseInt(this.element.css('paddingRight'));

            var containerWidth = this.element.width();

            this.outerWidth = containerWidth + this.paddingLeft + this.paddingRight;
            this.innerWidth = containerWidth - this.handles.length * this.options.gutter;

            for (var i = 0; i < this.handles.length; i++) {
                var currentPercent = this.handles.eq(i).data('percent');
                this._updateResizer(i, currentPercent);
            }
        } else {
            this.invalidated = true;
        }
    };

    UIColumns.prototype._updateResizer = function (i, currentPercent) {
        this.handles.eq(i).css({
            left: currentPercent + '%',
            marginLeft: (-2 + this.paddingLeft + (i + 0.5) * this.options.gutter + (this.innerWidth - this.outerWidth) * currentPercent / 100) + 'px'
        })
    };

    UIColumns.prototype._removeHandles = function () {
        this.handles.remove();
    };

    UIColumns.prototype.setOption = function (key, value) {
        _N2.UIWidgetBase.prototype.setOption.apply(this, arguments);

        switch (key) {
            case "active":
                this.active = value;
                if (this.active) {
                    this.create();
                    if (this.invalidated) {
                        this._resize();
                    }
                }
                break;
            case "columns":
                if (this.created) {
                    this._removeHandles();
                    this._setupHandles();
                }
                break;
            case "gutter":
                this._resize();
                break;
        }
    };

    UIColumns.prototype._mouseCapture = function (event) {
        var i, handle,
            capture = false;

        for (i = 0; i < this.handles.length; i++) {
            handle = this.handles[i];
            if (handle === event.target) {
                capture = true;
            }
        }

        return !this.options.disabled && capture;
    };

    UIColumns.prototype._mouseStart = function (event) {

        this.fragmentEditor.pointerManager.contextualBarHover.disable();

        var index = $(event.target).data('i'),
            cLeft = this.element.offset().left + 10,
            containerWidth = this.element.width() - 20;

        this.resizeContext = {
            index: index,
            cLeft: cLeft,
            containerWidth: containerWidth,
            startX: Math.max(0, Math.min(event.clientX - cLeft, containerWidth)),
        };

        this.currentFractions = [];
        this.currentPercent = [];
        for (var i = 0; i < this.fractions.length; i++) {
            this.currentFractions.push(this.fractions[i].clone());
            this.currentPercent.push(this.fractions[i].valueOf());
        }

        this.resizing = true;

        $("body").css("cursor", "ew-resize");

        this.element.addClass("ui-column-width-resizing");
        this._trigger("start", event, this.ui());
        return true;
    };

    UIColumns.prototype._mouseDrag = function (event) {

        var currentX = Math.max(0, Math.min(event.clientX - this.resizeContext.cLeft, this.resizeContext.containerWidth)),
            fractionDifference = new Fraction(Math.round((currentX - this.resizeContext.startX) / (this.resizeContext.containerWidth / this.currentDenominator)), this.currentDenominator);

        if (fractionDifference.compare(this.fractions[this.resizeContext.index].clone().mul(-1)) < 0) {
            fractionDifference = this.fractions[this.resizeContext.index].clone().mul(-1);
        }
        if (fractionDifference.compare(this.fractions[this.resizeContext.index + 1]) > 0) {
            fractionDifference = this.fractions[this.resizeContext.index + 1].clone();
        }

        this.currentFractions[this.resizeContext.index] = this.fractions[this.resizeContext.index].add(fractionDifference);
        this.currentFractions[this.resizeContext.index + 1] = this.fractions[this.resizeContext.index + 1].sub(fractionDifference);

        var currentPercent = 0;
        this.currentPercent = [];
        for (var i = 0; i < this.currentFractions.length; i++) {
            var width = this.currentFractions[i].valueOf();
            this.currentPercent.push(width);
            currentPercent += width * 100;
            this._updateResizer(i, currentPercent);
        }

        this._trigger("colwidth", event, this.ui());

        this._triggerInteraction();
    };

    UIColumns.prototype._mouseStop = function (event) {

        this.resizing = false;

        $("body").css("cursor", "auto");

        this._trigger("stop", event, this.ui());

        this.fractions = this.currentFractions;

        this._triggerInteraction();

        this.fragmentEditor.pointerManager.contextualBarHover.enable();

        _N2.WindowManager.actionPrevent('ComponentActivate');

        return false;
    };

    UIColumns.prototype.ui = function () {
        return {
            element: this.element,
            originalFractions: this.fractions,
            currentFractions: this.currentFractions,
            currentPercent: this.currentPercent,
            index: this.resizeContext.index
        };
    };

    return UIColumns;
});_N2.d('UILayerListItem', ['$', 'UIMouse'], function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @class
     * @constructor
     * @augments {_N2.UIMouse}
     */
    function UILayerListItem($element, options) {
        this.element = $element;

        this.widgetName = this.widgetName || 'UILayerListItem';
        this.widgetEventPrefix = "layerListItem";

        this.options = _Assign({
            UIManager: null,
            layer: false,
            $layer: null,
            distance: 2
        }, this.options, options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UILayerListItem.prototype = Object.create(_N2.UIMouse.prototype);
    UILayerListItem.prototype.constructor = UILayerListItem;

    UILayerListItem.prototype.create = function () {

        this._mouseInit();
    };

    UILayerListItem.prototype._mouseCapture = function (event, overrideHandle) {
        return this.options.UIManager._mouseCapture(this.options, event, overrideHandle);
    };

    UILayerListItem.prototype._mouseStart = function (event, overrideHandle, noActivation) {
        this._trigger('start');
        return this.options.UIManager._mouseStart(this.options, event, overrideHandle, noActivation);
    };

    UILayerListItem.prototype._mouseDrag = function (event) {
        return this.options.UIManager._mouseDrag(this.options, event);
    };

    UILayerListItem.prototype._mouseStop = function (event, noPropagation) {
        return this.options.UIManager._mouseStop(this.options, event, noPropagation);

    };

    UILayerListItem.prototype.destroy = function () {
        this._mouseDestroy();
        return this;
    };

    return UILayerListItem;
});_N2.d('UILayerList', ['$', 'UIWidgetBase'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @class
     * @constructor
     * @augments {_N2.UIWidgetBase}
     */
    function UILayerList($element, options) {
        this.element = $element;

        this.widgetName = this.widgetName || 'UILayerList';
        this.widgetEventPrefix = "layerList";

        this.options = _Assign({
            mainContainer: null,
            $scrolled: null
        }, this.options, options);

        this.interaction = false;

        this.onKeyDownCallback = this.onKeyDown.bind(this);

        _N2.UIWidgetBase.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UILayerList.prototype = Object.create(_N2.UIWidgetBase.prototype);
    UILayerList.prototype.constructor = UILayerList;

    UILayerList.prototype.create = function () {

        this.scrollTimeout = null;
    };


    UILayerList.prototype._mouseCapture = function (itemOptions, event, overrideHandle) {

        return itemOptions.$item[0] !== this.options.mainContainer.layerRow[0];
    };

    UILayerList.prototype._mouseStart = function (itemOptions, event, overrideHandle, noActivation) {


        this.interaction = true;

        this.scrolledTop = this.options.$scrolled.offset().top;
        this.scrolledHeight = this.options.$scrolled.height();
        this.scrolledScroll = this.options.$scrolled.scrollTop();
        this.scrolledMaxHeight = this.options.$scrolled[0].scrollHeight - this.scrolledHeight;

        this.context = {
            placeholder: $('<div class="n2_layer_navigation_list_layers__sortable_placeholder"></div>'),
            mouse: {
                y: event.pageY,
                topModifier: itemOptions.$item.offset().top - event.pageY
            },
            $item: itemOptions.$item,
            $clone: itemOptions.$item.clone()
        };

        this.context.$clone.addClass('n2_layer_navigation_list_layer--sorted').appendTo(this.options.$scrolled);

        this.context.droppables = this.options.mainContainer.getLLDroppables(itemOptions.layer);

        this._cacheContainers();

        document.addEventListener("keydown", this.onKeyDownCallback, {capture: true});

        this._trigger("start", event);

        this._mouseDrag(itemOptions, event);
    };

    UILayerList.prototype._scrollUp = function () {
        if (this.scrolledTop > 0) {
            if (this.scrollTimeout === null) {
                this.scrollTimeout = setInterval((function () {
                    this.scrolledScroll -= 33;
                    this.options.$scrolled.scrollTop(this.scrolledScroll);
                }).bind(this), 150);
                this.scrolledScroll -= 33;
                this.options.$scrolled.scrollTop(this.scrolledScroll);
            }
        }
    };

    UILayerList.prototype._scrollDown = function () {
        if (this.scrollTimeout === null) {
            this.scrollTimeout = setInterval((function () {
                this.scrolledScroll += 33;
                this.options.$scrolled.scrollTop(Math.min(this.scrolledScroll, this.scrolledMaxHeight));
            }).bind(this), 150);
            this.scrolledScroll += 33;
            this.options.$scrolled.scrollTop(Math.min(this.scrolledScroll, this.scrolledMaxHeight));
        }
    };

    UILayerList.prototype._mouseDrag = function (itemOptions, event) {

        if (this.interaction) {
            this.scrolledTop = this.options.$scrolled.offset().top;
            if (this.scrolledHeight > 140) {
                if (event.pageY < this.scrolledTop + 33 + 48) {
                    this._scrollUp();
                } else if (event.pageY > this.scrolledTop + this.scrolledHeight - 33) {
                    this._scrollDown();
                } else {
                    clearInterval(this.scrollTimeout);
                    this.scrollTimeout = null;
                }
            }


            this.scrolledScroll = this.options.$scrolled.scrollTop();

            var y = event.pageY - this.scrolledTop + this.scrolledScroll;

            var targetContainer = this._findInnerContainer(y);
            if (targetContainer === false) {
                targetContainer = this.context.droppables[0];
            }

            if (targetContainer.layers === undefined) {
                targetContainer.layers = this._cacheContainerLayers(targetContainer);
            }

            var targetIndex = this._findNormalIndex(y, targetContainer);

            if (targetIndex > 0) {
                this.context.placeholder.insertAfter(targetContainer.layers[targetIndex - 1].layer.layerRow);
            } else {
                this.context.placeholder.prependTo(targetContainer.$container);
            }

            this.context.targetIndex = targetIndex;
            if (this.context.targetContainer && this.context.targetContainer !== targetContainer) {
                this.context.targetContainer.layer.layerRow.removeClass('n2_layer_navigation_list_layer--drop');
            }

            this.context.targetContainer = targetContainer;
            this.context.targetContainer.layer.layerRow.addClass('n2_layer_navigation_list_layer--drop');

            this.context.$clone.css({
                top: (y + this.context.mouse.topModifier) + 'px'
            });
        }
    };

    UILayerList.prototype._mouseStop = function (itemOptions, event, noPropagation) {

        if (this.interaction) {
            this.ended(true);

            var targetIndex = this.context.targetIndex,
                targetContainer = this.context.targetContainer,
                newIndex = -1;


            if (this.context.targetContainer.layers.length === 0) {
                newIndex = 0;
            } else {
                var nextLayer = false,
                    prevLayer = false;

                if (this.context.targetContainer.layers[targetIndex]) {
                    nextLayer = this.context.targetContainer.layers[targetIndex].layer;
                }

                if (this.context.targetContainer.layers[targetIndex - 1]) {
                    prevLayer = this.context.targetContainer.layers[targetIndex - 1].layer;
                }

                if (nextLayer === itemOptions.layer || prevLayer === itemOptions.layer) {
                    newIndex = -1;
                } else {
                    if (targetContainer.layer.type === 'slide') {
                        if (nextLayer) {
                            newIndex = nextLayer.getIndex() + 1;
                        } else if (prevLayer) {
                            newIndex = prevLayer.getIndex();
                        }
                    } else {
                        if (prevLayer) {
                            newIndex = prevLayer.getIndex() + 1;
                        } else if (nextLayer) {
                            newIndex = nextLayer.getIndex();
                        }
                    }
                }
            }
            if (newIndex >= 0) {
                var originalIndex = itemOptions.layer.getIndex();

                if (targetContainer.layer === itemOptions.layer.group && newIndex > originalIndex) {
                    newIndex--;
                }
                if (itemOptions.layer.type === 'col') {
                    targetContainer.layer.moveCol(originalIndex, newIndex);
                } else {
                    targetContainer.layer.container.insertLayerAt(itemOptions.layer, newIndex);
                    itemOptions.layer.onCanvasUpdate(originalIndex, targetContainer.layer, newIndex);
                }
            }

            delete this.context;

            this._trigger("stop", event);
        }

        _N2.WindowManager.actionPrevent('ComponentRowActivate');

        this.interaction = false;
    };

    UILayerList.prototype.cancel = function () {

        this.interaction = false;

        delete this.context;
    };

    UILayerList.prototype.ended = function (mouseStop) {
        document.removeEventListener("keydown", this.onKeyDownCallback, {capture: true});

        if (this.scrollTimeout !== null) {
            clearInterval(this.scrollTimeout);
            this.scrollTimeout = null;
        }

        this.context.placeholder.remove();

        this.context.$clone.remove();

        this.context.targetContainer.layer.layerRow.removeClass('n2_layer_navigation_list_layer--drop');


        if (!mouseStop) {
            this.cancel();
        }
    };

    UILayerList.prototype.onKeyDown = function (e) {
        if (e.code === 'Escape') {
            e.stopImmediatePropagation();
            e.preventDefault();

            this.ended();
        }
    };

    UILayerList.prototype._cacheContainers = function () {
        for (var i = 0; i < this.context.droppables.length; i++) {
            var obj = this.context.droppables[i];
            obj.top = obj.$container.offset().top - this.scrolledTop + this.scrolledScroll - 15;
            obj.height = obj.$container.outerHeight();
            obj.bottom = obj.top + obj.height + 15;
        }
    };

    UILayerList.prototype._findInnerContainer = function (y) {
        for (var i = this.context.droppables.length - 1; i >= 0; i--) {
            var obj = this.context.droppables[i];
            if (obj.top <= y && obj.bottom >= y) {
                return obj;
            }
        }
        return false;
    };

    UILayerList.prototype._cacheContainerLayers = function (droppable) {
        var layerObjects = [],
            layers = droppable.layer.container.getSortedLayers();

        for (var i = 0; i < layers.length; i++) {
            var obj = {
                layer: layers[i]
            };
            obj.top = obj.layer.layerRow.offset().top - this.scrolledTop + this.scrolledScroll;
            obj.height = obj.layer.layerRow.outerHeight();
            obj.bottom = obj.top + obj.height / 2;
            obj.index = i;
            layerObjects.push(obj);
        }

        if (droppable.layer.type === 'slide') {
            layerObjects.reverse();
        }

        return layerObjects;
    };

    UILayerList.prototype._findNormalIndex = function (y, targetContainer) {
        for (var i = 0; i < targetContainer.layers.length; i++) {
            var obj = targetContainer.layers[i];
            if (y <= obj.bottom) {
                return i;
            }
        }
        return targetContainer.layers.length;
    };

    return UILayerList;
});_N2.d('UIDragTooltip', '$', function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * Drag tooltip has priority over the hover tooltip.
     *
     * @param {string} name
     * @constructor
     */
    function UIDragTooltip(name) {
        this.name = name;
        this.isShown = false;
        this.isShownForHover = false;
        this.isShownForDrag = false;
        this.e = null;
        this.hoverTipCallback = null;

        /**
         * @type {_N2.PositionDisplay}
         */
        this.tip = _N2.PositionDisplay.get();
    }

    UIDragTooltip.prototype.hoverShow = function (e, tipCallback) {
        this.e = e;
        this.hoverTipCallback = tipCallback;
        this.isShownForHover = true;
        this.show();
    };

    UIDragTooltip.prototype.hoverUpdatePosition = function (e) {
        if (this.isShownForHover && !this.isShownForDrag) {
            this.e = e;
            this.tip.update(this.e, this.name);
        }
    };

    UIDragTooltip.prototype.hoverHide = function () {
        this.isShownForHover = false;
        this.hide();
    };

    UIDragTooltip.prototype.dragShow = function () {
        this.isShownForDrag = true;
        this.show();
    };

    UIDragTooltip.prototype.dragUpdate = function (e, tip) {
        this.e = e;
        this.tip.update(e, this.name, tip);
    };

    UIDragTooltip.prototype.dragHide = function () {
        this.isShownForDrag = false;
        this.hide();

        if (this.isShownForHover) {
            this.tip.update(this.e, this.name, this.hoverTipCallback.call());
        }
    };

    UIDragTooltip.prototype.show = function () {
        if (!this.isShown) {
            this.tip.show(this.name);
            if (!this.isShownForDrag) {
                this.tip.update(this.e, this.name, this.hoverTipCallback.call());
            }
            this.isShown = true;
        }
    };

    UIDragTooltip.prototype.hide = function () {
        if (this.isShown && !this.isShownForHover && !this.isShownForDrag) {
            this.tip.hide(this.name);
            this.isShown = false;
        }
    };

    return UIDragTooltip;
});_N2.d('UIResizeAbsolute', ['$', 'UIMouse'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param  $element
     * @param {_N2.FragmentEditor} fragmentEditor
     * @param {{}} options
     */
    function UIResizeAbsolute($element, fragmentEditor, options) {
        this.element = $element;
        this.fragmentEditor = fragmentEditor;

        this.widgetName = this.widgetName || 'UIResizeAbsolute';

        $(this.element).data('UIResizeAbsolute', this);

        this.widgetEventPrefix = "resize";

        this.options = _Assign({
            // Callbacks
            resize: null,
            start: null,
            stop: null
        }, this.options, options);

        this.___parentNode = this.element[0].parentNode;

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UIResizeAbsolute.prototype = Object.create(_N2.UIMouse.prototype);
    UIResizeAbsolute.prototype.constructor = UIResizeAbsolute;

    UIResizeAbsolute.plugins = {};

    UIResizeAbsolute.prototype.create = function () {

        this.element.addClass("nui_resize_absolute");

        this._setupHandles();

        this._mouseInit();

        this._trigger('create', null, {});
    };

    UIResizeAbsolute.prototype._setupHandles = function () {
        var handle,
            handleNames = ["n", "e", "s", "w", "se", "sw", "ne", "nw"],
            hname,
            axis,
            that = this;

        this.$handles = $();
        this.handles = {};

        for (var i = 0; i < handleNames.length; i++) {

            handle = handleNames[i];
            hname = "nui_resize_absolute__handle--" + handle;
            axis = $("<div>")
                .addClass("nui_resize_absolute__handle " + hname)
                .attr('data-handle', handle)
                .on('mousedown', this._mouseDown.bind(this));

            this.handles[handle] = axis;
            this.$handles = this.$handles.add(axis);

            this.element.append(axis);
        }

        this.$handles.on("mouseover", function () {
            if (!that.resizing) {
                if (this.className) {
                    axis = this.className.match(/nui_resize_absolute__handle--(se|sw|ne|nw|n|e|s|w)/i);
                }
                that.axis = axis && axis[1] ? axis[1] : "se";
            }
        });
    };

    UIResizeAbsolute.prototype._mouseCapture = function (event) {
        var i, handle,
            capture = false;

        for (i in this.handles) {
            handle = this.handles[i][0];
            if (handle === event.target || $.contains(handle, event.target)) {
                capture = true;
            }
        }

        return !this.options.disabled && capture;
    };

    UIResizeAbsolute.prototype._removeHandles = function () {
        this.$handles.remove();
    };

    UIResizeAbsolute.prototype._mouseStart = function (event) {

        $('body')
            .addClass('n2_body--resize-absolute')
            .css('cursor', this.axis + '-resize')
            .addClass('n2_body--inherit-cursor');

        this.element.addClass('nui_resize_absolute--resizing');

        this.fragmentEditor.pointerManager.disable();

        var curleft, curtop, cursor;

        this.resizing = true;

        this.elementSMW = _N2.MW.___getSMW(this.element[0]);

        this.originalRect = this.element[0].getBoundingClientRect();

        curleft = this.elementSMW.xAbs + this.elementSMW.xPAbs / 100 * this.originalRect.width;
        curtop = this.elementSMW.yAbs + this.elementSMW.yPAbs / 100 * this.originalRect.height;

        this.elementSMW.xAbs = curleft;
        this.elementSMW.xPAbs = 0;
        this.elementSMW.yAbs = curtop;
        this.elementSMW.yPAbs = 0;

        this.position = {
            left: curleft,
            top: curtop
        };

        this.size = {
            width: this.originalRect.width,
            height: this.originalRect.height
        };

        this.originalPosition = {left: curleft, top: curtop};
        this.___originalMousePosition = {
            left: event.pageX,
            top: event.pageY
        };

        this.___currentTrigger = this._change[this.axis].bind(this);

        cursor = this.handles[this.axis].css("cursor");
        $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);

        this._propagate("start", event);
        return true;
    };

    UIResizeAbsolute.prototype._mouseDrag = function (event) {

        var data = this.___currentTrigger(event, event.pageX - this.___originalMousePosition.left, event.pageY - this.___originalMousePosition.top);

        if (data.left !== undefined) {
            this.position.left = data.left;
        }
        if (data.top !== undefined) {
            this.position.top = data.top;
        }
        if (data.height !== undefined) {
            this.size.height = Math.max(10, data.height);
        }
        if (data.width !== undefined) {
            this.size.width = Math.max(10, data.width);
        }

        this._propagate("resize", event);

        this.position.left = Math.round(this.position.left);
        this.position.top = Math.round(this.position.top);
        this.size.width = Math.round(this.size.width);
        this.size.height = Math.round(this.size.height);

        this.elementSMW.yAbs = this.position.top;
        this.elementSMW.xAbs = this.position.left;
        this.elementSMW.width = this.size.width;
        this.elementSMW.height = this.size.height;

        this._trigger("resize", event, this.ui());

        return false;
    };

    UIResizeAbsolute.prototype._mouseStop = function (event) {

        this.resizing = false;

        this._propagate("stop", event);

        this.fragmentEditor.pointerManager.enable();

        this.element.removeClass('nui_resize_absolute--resizing');


        $('body')
            .removeClass('n2_body--resize-absolute')
            .css('cursor', '')
            .removeClass('n2_body--inherit-cursor');

        _N2.WindowManager.actionPrevent('ComponentActivate');

        return false;

    };

    UIResizeAbsolute.prototype._change = {
        e: function (event, dx) {
            return {
                width: this.originalRect.width + dx
            };
        },
        w: function (event, dx) {
            return {
                left: this.originalPosition.left + dx,
                width: this.originalRect.width - dx
            };
        },
        n: function (event, dx, dy) {
            return {
                top: this.originalPosition.top + dy,
                height: this.originalRect.height - dy
            };
        },
        s: function (event, dx, dy) {
            return {
                height: this.originalRect.height + dy
            };
        },
        se: function (event, dx, dy) {
            return _Assign(this._change.s.apply(this, arguments), this._change.e.apply(this, arguments));
        },
        sw: function (event, dx, dy) {
            return _Assign(this._change.s.apply(this, arguments), this._change.w.apply(this, arguments));
        },
        ne: function (event, dx, dy) {
            return _Assign(this._change.n.apply(this, arguments), this._change.e.apply(this, arguments));
        },
        nw: function (event, dx, dy) {
            return _Assign(this._change.n.apply(this, arguments), this._change.w.apply(this, arguments));
        }
    };

    UIResizeAbsolute.prototype._propagate = function (n, event) {
        this.callPlugin(n, [event, this.ui()]);
        (n !== "resize" && this._trigger(n, event, this.ui()));
    };

    UIResizeAbsolute.prototype.ui = function () {

        return {
            originalRect: this.originalRect,
            rect: this.element[0].getBoundingClientRect(),
            element: this.element,
            position: this.position,
            size: this.size,
            axis: this.axis
        };
    };


    UIResizeAbsolute.prototype.destroy = function () {
        this._mouseDestroy();

        this.element
            .removeClass("nui_resize_absolute")
            .removeData(this.widgetName);

        for (var k in this.handles) {
            this.handles[k].remove();
        }

        return this;
    };

    _N2.UIWidgetBase.addPlugin(UIResizeAbsolute, "smartguides", {
        start: function (event, ui) {
            var i = $(this).data("UIResizeAbsolute"), o = i.options;
            i.gridH = $('<div class="n2_smartguide n2_smartguide--horizontal"></div>').appendTo(i.___parentNode);
            i.gridV = $('<div class="n2_smartguide n2_smartguide--vertical"></div>').appendTo(i.___parentNode);
            i.gridH2 = $('<div class="n2_smartguide n2_smartguide--horizontal"></div>').appendTo(i.___parentNode);
            i.gridV2 = $('<div class="n2_smartguide n2_smartguide--vertical"></div>').appendTo(i.___parentNode);
            i.elements = [];
            i.$snapLayers = $();
            if (typeof o.smartguides == 'function') {

                var guides = o.smartguides();
                if (guides) {
                    var parentRect = i.___parentNode.getBoundingClientRect();

                    guides.each(function () {
                        if (this !== i.element[0]) {
                            var $t = $(this),
                                rect = this.getBoundingClientRect();

                            i.elements.push({
                                item: this,
                                left: Math.round(rect.left - parentRect.left),
                                top: Math.round(rect.top - parentRect.top),
                                width: rect.width,
                                height: rect.height
                            });

                            if ($t.hasClass('n2-ss-layer')) {
                                i.$snapLayers = i.$snapLayers.add($t);
                            }
                        }
                    });

                    i.$snapLayers.addClass('n2_layer_resize_snap_to');

                    i.elements.push({
                        item: $(i.___parentNode),
                        width: parentRect.width,
                        height: parentRect.height,
                        top: 0, left: 0
                    });
                }
            }
        },
        stop: function (event, ui) {
            var i = $(this).data("UIResizeAbsolute");

            i.$snapLayers.removeClass('n2_layer_resize_snap_to');

            i.gridH.remove();
            i.gridV.remove();
            i.gridH2.remove();
            i.gridV2.remove();
        },
        resize: function (event, ui) {
            var inst = $(this).data("UIResizeAbsolute"),
                o = inst.options,
                d = o.tolerance,
                diff;
            inst.gridV.css({"display": "none"});
            inst.gridH.css({"display": "none"});
            inst.gridV2.css({"display": "none"});
            inst.gridH2.css({"display": "none"});


            var container = inst.elements[inst.elements.length - 1];

            function setGridV(left) {
                inst.gridV.css({left: Math.min(left, container.width - 1) + 'px', display: "block"});
            }

            function setGridV2(left) {
                inst.gridV2.css({left: Math.min(left, container.width - 1) + 'px', display: "block"});
            }

            function setGridH(top) {
                inst.gridH.css({top: Math.min(top, container.height - 1) + 'px', display: "block"});
            }

            function setGridH2(top) {
                inst.gridH2.css({top: Math.min(top, container.height - 1) + 'px', display: "block"});
            }

            var ctrlKey = event.ctrlKey || event.metaKey,
                altKey = event.altKey;
            if (ctrlKey && altKey) {
                return;
            }

            var x1 = ui.position.left, x2 = x1 + ui.size.width,
                y1 = ui.position.top, y2 = y1 + ui.size.height;
            for (var i = inst.elements.length - 1; i >= 0; i--) {
                var l = inst.elements[i].left, r = l + inst.elements[i].width,
                    t = inst.elements[i].top, b = t + inst.elements[i].height;

                if (!ctrlKey) {
                    var hc = (l + r) / 2;

                    if (Math.abs(l - x2) <= d) {
                        ui.size.width = l - ui.position.left;
                        setGridV(ui.position.left + ui.size.width);
                    } else if (Math.abs(l - x1) <= d) {
                        diff = ui.position.left - l;
                        ui.position.left = l;
                        ui.size.width += diff;
                        setGridV(ui.position.left);
                    } else if (Math.abs(hc - x1) <= d) {
                        diff = ui.position.left - hc;
                        ui.position.left = hc;
                        ui.size.width += diff;
                        setGridV(ui.position.left);
                    }

                    if (Math.abs(r - x1) <= d) {
                        diff = ui.position.left - r;
                        ui.position.left = r;
                        ui.size.width += diff;
                        setGridV2(ui.position.left);
                    } else if (Math.abs(r - x2) <= d) {
                        ui.size.width = r - ui.position.left;
                        setGridV2(ui.position.left + ui.size.width);
                    } else if (Math.abs(hc - x2) <= d) {
                        ui.size.width = hc - ui.position.left;
                        setGridV2(ui.position.left + ui.size.width);
                    }
                }

                if (!altKey) {
                    var vc = (t + b) / 2;

                    if (Math.abs(t - y2) <= d) {
                        ui.size.height = t - ui.position.top;
                        setGridH(t);
                    } else if (Math.abs(t - y1) <= d) {
                        diff = ui.position.top - t;
                        ui.position.top = t;
                        ui.size.height += diff;
                        setGridH(ui.position.top);
                    } else if (Math.abs(vc - y1) <= d) {
                        diff = ui.position.top - vc;
                        ui.position.top = vc;
                        ui.size.height += diff;
                        setGridH(ui.position.top);
                    }

                    if (Math.abs(b - y1) <= d) {
                        diff = ui.position.top - b;
                        ui.position.top = b;
                        ui.size.height += diff;
                        setGridH2(ui.position.top);
                    } else if (Math.abs(b - y2) <= d) {
                        ui.size.height = b - ui.position.top;
                        setGridH2(ui.position.top + ui.size.height);
                    } else if (Math.abs(vc - y2) <= d) {
                        ui.size.height = vc - ui.position.top;
                        setGridH2(ui.position.top + ui.size.height);
                    }
                }
            }
        }
    });

    return UIResizeAbsolute;
});_N2.d('UIResizeNormal', ['$', 'UIMouse', 'UIDragTooltip'], function () {
    var $ = _N2.$;
    "use strict";

    var tooltip = new _N2.UIDragTooltip('NormalSizing');

    /**
     * @memberOf _N2
     *
     * @constructor
     * @param  $element
     * @param {_N2.FragmentEditor} fragmentEditor
     * @param {{}} options
     * @augments {_N2.UIMouse}
     */
    function UIResizeNormal($element, fragmentEditor, options) {
        this.element = $element;
        this.fragmentEditor = fragmentEditor;

        this.widgetName = this.widgetName || 'UIResizeNormal';
        this.widgetEventPrefix = "normalsizing";
        this.multiplier = 1;

        this.options = _Assign({
            maxWidth: true,
            height: false,
            syncWidth: false,
            start: null,
            resizeMaxWidth: null,
            resizeHeight: null,
            stopMaxWidth: null,
            stopHeight: null
        }, this.options, options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UIResizeNormal.prototype = Object.create(_N2.UIMouse.prototype);
    UIResizeNormal.prototype.constructor = UIResizeNormal;

    UIResizeNormal.prototype.setMaxWidth = function (maxWidth) {

        this.handles.e.toggleClass('nui_resize_normal__handle--none', maxWidth < 0);
        this.handles.w.toggleClass('nui_resize_normal__handle--none', maxWidth < 0);
    };

    UIResizeNormal.prototype.create = function () {

        this._setupHandles();

        this._mouseInit();
    };

    UIResizeNormal.prototype.destroy = function () {

        this._mouseDestroy();

        this.element
            .removeData(this.widgetName);

        for (var k in this.handles) {
            this.handles[k].remove();
        }

        return this;
    };

    UIResizeNormal.prototype._setupHandles = function () {
        var o = this.options, i, n = [], hname, axis;

        if (o.maxWidth) {
            n.push('w');
            n.push('e');
        }

        if (o.height) {
            n.push('s');
        }
        /**
         * @type {{}}
         */
        this.handles = {};
        this.$handles = $();

        for (i = 0; i < n.length; i++) {
            var handle = n[i];
            axis = $('<div class="nui_resize_normal__handle nui_resize_normal__handle--' + handle + '">')
                .on('mouseenter', this.onMouseEnter.bind(this, handle));
            this.handles[handle] = axis;
            this.$handles = this.$handles.add(axis);
            this.element.append(axis);
        }
    };

    UIResizeNormal.prototype.onMouseEnter = function (handle, e) {

        var lastE = e;
        this.timeout = setTimeout((function () {
            delete this.timeout;

            tooltip.hoverShow(lastE, (function () {
                if (handle === 'e' || handle === 'w') {
                    var maxWidth = this.element.css('maxWidth');
                    return n2_('Max width') + ': ' + (maxWidth.substr(-2) === 'px' ? maxWidth : n2_('None'));
                } else if (handle === 's') {
                    return n2_('Height') + ': ' + this.element.height() + 'px';
                }
            }).bind(this));
        }).bind(this), 500);

        this.handles[handle]
            .on({
                'mousemove.tooltip': function (e) {
                    lastE = e;
                    tooltip.hoverUpdatePosition(e);
                },
                'mouseleave.tooltip': (function () {
                    if (this.timeout) {
                        clearTimeout(this.timeout);
                        delete this.timeout;
                    }
                    this.handles[handle].off('.tooltip');
                    tooltip.hoverHide();
                }).bind(this)
            });
    };

    UIResizeNormal.prototype._removeHandles = function () {
        this.$handles.remove();
    };

    UIResizeNormal.prototype._mouseCapture = function (event) {
        for (var handle in this.handles) {
            if (this.handles[handle][0] === event.target) {
                this.currentHandle = handle;
                return !this.options.disabled;
            }
        }

        return false;
    };

    UIResizeNormal.prototype._mouseStart = function (event) {

        this.fragmentEditor.pointerManager.contextualBarHover.disable();

        tooltip.dragShow();

        this.originalMousePosition = {left: event.pageX, top: event.pageY};

        switch (this.currentHandle) {
            case 'w':
            case 'e':
                this.originalValue = this.element.width();
                this.maxWidth = this.element.parent().width();

                this._trigger("start", event, 'maxwidth');

                if (this.element.css('align-self') === 'center') {
                    this.multiplier = 2;
                } else {
                    this.multiplier = 1;
                }

                $('body')
                    .css('cursor', this.currentHandle + '-resize')
                    .addClass('n2_body--inherit-cursor');

                break;
            case 's':
                this.originalValue = this.element.height();

                this._trigger("start", event, 'height');

                $('body')
                    .css('cursor', 's-resize')
                    .addClass('n2_body--inherit-cursor');
                break;
        }

        return true;
    };

    UIResizeNormal.prototype._parse_movement_s = function (e) {
        return e.pageY - this.originalMousePosition.top;
    };

    UIResizeNormal.prototype._parse_movement_e = function (e) {
        return (e.pageX - this.originalMousePosition.left) * this.multiplier;
    };

    UIResizeNormal.prototype._parse_movement_w = function (e) {
        return (this.originalMousePosition.left - e.pageX) * this.multiplier;
    };

    UIResizeNormal.prototype._mouseDrag = function (event) {
        var o = this.options;

        this.currentValue = _N2._roundHelper(this.originalValue + this['_parse_movement_' + this.currentHandle].call(this, event));

        switch (this.currentHandle) {
            case 'w':
            case 'e':
                if (this.currentValue <= this.maxWidth) {
                    this.element.css('maxWidth', this.currentValue + 'px');
                    if (o.syncWidth) {
                        this.element.css('width', this.currentValue + 'px');
                    }

                    this.setMaxWidth(this.currentValue);
                } else {
                    this.element.css('maxWidth', 'none');
                    if (o.syncWidth) {
                        this.element.css('width', '');
                    }
                    this.currentValue = 0;

                    this.setMaxWidth(-1);
                }

                tooltip.dragUpdate(event, n2_('Max width') + ": " + (this.currentValue === 0 ? n2_('None') : (this.currentValue + 'px')));


                break;
            case 's':
                this.currentValue = Math.max(1, this.currentValue);
                this.element.height(this.currentValue + 'px');

                tooltip.dragUpdate(event, n2_('Height') + ": " + this.currentValue + 'px');

                break;
        }

        this._triggerInteraction();
    };

    UIResizeNormal.prototype._mouseStop = function (event) {
        var o = this.options;

        this.currentValue = _N2._roundHelper(this.originalValue + this['_parse_movement_' + this.currentHandle].call(this, event));

        tooltip.dragHide();

        switch (this.currentHandle) {
            case 'w':
            case 'e':
                if (this.currentValue <= this.maxWidth) {
                    this.element.css('maxWidth', this.currentValue + 'px');
                    if (o.syncWidth) {
                        this.element.css('width', '');
                    }

                    this.setMaxWidth(this.currentValue);
                } else {
                    this.element.css('maxWidth', 'none');
                    if (o.syncWidth) {
                        this.element.css('width', '');
                    }
                    this.currentValue = 0;

                    this.setMaxWidth(-1);
                }

                this._trigger("stopMaxWidth", event, {value: this.currentValue});

                break;
            case 's':
                this.currentValue = Math.max(1, this.currentValue);
                this.element.height(this.currentValue + 'px');

                this._trigger("stopHeight", event, {value: this.currentValue});
                break;
        }

        $('body')
            .css('cursor', '')
            .removeClass('n2_body--inherit-cursor');

        this._triggerInteraction();

        this.fragmentEditor.pointerManager.contextualBarHover.enable();

        _N2.WindowManager.actionPrevent('ComponentActivate');

        return false;
    };

    return UIResizeNormal;
});_N2.d('UISpacingMargin', ['$', 'UISpacing'], function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @param  $element
     * @param {_N2.FragmentEditor} fragmentEditor
     * @param {{}} options
     * @augments {_N2.UISpacing}
     */
    function UISpacingMargin($element, fragmentEditor, options) {

        this.options = _Assign({
            mode: 'margin',
            label: n2_('Margin'),
            sync: {
                n: 'margin-top',
                e: 'margin-right',
                s: 'margin-bottom',
                w: 'margin-left',
            }
        }, options);

        _N2.UISpacing.prototype.constructor.call(this, $element, fragmentEditor);
    }

    UISpacingMargin.prototype = Object.create(_N2.UISpacing.prototype);
    UISpacingMargin.prototype.constructor = UISpacingMargin;

    UISpacingMargin.prototype.updateElementProp = function (property, value) {

        _NodeSetStyle(this.element[0], '--' + property, value + 'px');
    };

    return UISpacingMargin;
});_N2.d('UISpacingPadding', ['$', 'UISpacing'], function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @param  $element
     * @param {_N2.FragmentEditor} fragmentEditor
     * @param {{}} options
     * @augments {_N2.UISpacing}
     */
    function UISpacingPadding($element, fragmentEditor, options) {

        this.options = _Assign({
            mode: 'padding',
            label: n2_('Padding'),
            sync: {
                n: 'padding-top',
                e: 'padding-right',
                s: 'padding-bottom',
                w: 'padding-left'
            }
        }, options);

        _N2.UISpacing.prototype.constructor.call(this, $element, fragmentEditor);
    }

    UISpacingPadding.prototype = Object.create(_N2.UISpacing.prototype);
    UISpacingPadding.prototype.constructor = UISpacingPadding;

    UISpacingPadding.prototype.limitValue = function (value) {
        return Math.max(0, value);
    };

    return UISpacingPadding;
});_N2.d('UISpacing', ['$', 'UIMouse', 'UIDragTooltip'], function () {
    "use strict";

    var $ = _N2.$,
        tooltip = new _N2.UIDragTooltip('Spacing'),
        handleSizing = {
            n: 'height',
            e: 'width',
            s: 'height',
            w: 'width'
        };

    /**
     * @memberOf _N2
     *
     * @abstract
     * @constructor
     * @param  $element
     * @param {_N2.FragmentEditor} fragmentEditor
     * @augments {_N2.UIMouse}
     */
    function UISpacing($element, fragmentEditor) {
        this.element = $element;
        this.fragmentEditor = fragmentEditor;

        this.widgetName = this.widgetName || 'UISpacing';
        this.widgetEventPrefix = "spacing";

        this.options = _Assign({
            syncInv: {
                n: 's',
                e: 'w',
                s: 'n',
                w: 'e'
            },
            side: {
                n: {
                    label: n2_('top'),
                    value: 'top'
                },
                e: {
                    label: n2_('right'),
                    value: 'right'
                },
                s: {
                    label: n2_('bottom'),
                    value: 'bottom'
                },
                w: {
                    label: n2_('left'),
                    value: 'left'
                },
            },
            // Callbacks
            drag: null,
            start: null,
            stop: null
        }, this.options);

        _N2.UIMouse.prototype.constructor.apply(this, arguments);

        this.create();
    }

    UISpacing.prototype = Object.create(_N2.UIMouse.prototype);
    UISpacing.prototype.constructor = UISpacing;


    UISpacing.prototype.create = function () {

        this._setupHandles();

        this._mouseInit();
    };


    UISpacing.prototype.destroy = function () {

        this._mouseDestroy();

        this.element
            .removeData(this.widgetName);

        for (var k in this.handles) {
            this.handles[k].remove();
        }

        return this;
    };

    UISpacing.prototype._setupHandles = function () {
        var handleNames = ["n", "e", "s", "w"];

        /**
         * @type {{}}
         */
        this.handles = {};
        this.$handles = $();

        for (var i = 0; i < handleNames.length; i++) {

            var handle = handleNames[i];
            var $handle = $("<div>")
                .addClass("nui_spacing__handle nui_spacing__handle--" + this.options.mode + " nui_spacing__handle--" + handle)
                .on('mouseenter', this.onMouseEnter.bind(this, handle))
                .on('mousedown', this._mouseDown.bind(this))
                .appendTo(this.element);
            this.handles[handle] = $handle;
            this.$handles = this.$handles.add($handle);
        }
    };

    UISpacing.prototype.onMouseEnter = function (handle, e) {

        var lastE = e;
        this.timeout = setTimeout((function () {
            delete this.timeout;

            tooltip.hoverShow(lastE, (function () {
                return this.options.label + ' ' + this.options.side[handle].label + ': ' + this.element.css(this.options.sync[handle]);
            }).bind(this));
        }).bind(this), 500);

        this.handles[handle]
            .on({
                'mousemove.tooltip': function (e) {
                    lastE = e;
                    tooltip.hoverUpdatePosition(e);
                },
                'mouseleave.tooltip': (function () {
                    if (this.timeout) {
                        clearTimeout(this.timeout);
                        delete this.timeout;
                    }
                    this.handles[handle].off('.tooltip');
                    tooltip.hoverHide();
                }).bind(this)
            });
    };

    UISpacing.prototype._removeHandles = function () {
        this.$handles.remove();
    };

    UISpacing.prototype._parse_movement_n = function (e) {
        return e.pageY - this.originalMousePosition.top;
    };

    UISpacing.prototype._parse_movement_w = function (e) {
        return e.pageX - this.originalMousePosition.left;
    };

    UISpacing.prototype._parse_movement_s = function (e) {
        return e.pageY - this.originalMousePosition.top;
    };

    UISpacing.prototype._parse_movement_e = function (e) {
        return this.originalMousePosition.left - e.pageX;
    };

    UISpacing.prototype._mouseCapture = function (e) {
        var handle,
            capture = false;

        for (var k in this.handles) {
            handle = this.handles[k][0];
            if ((handle === e.target || $.contains(handle, e.target)) && $(handle).hasClass('nui-enabled')) {
                capture = true;
            }
        }

        return !this.options.disabled && capture;
    };

    UISpacing.prototype.updatePositionDisplay = function (e) {
        var html = this.options.label + ' ' + this.options.side[this.direction].label + ': ' + this.currentValue + 'px<br>';
        if (this.wasShiftPressed) {
            html += this.options.label + ' ' + this.options.side[this.options.syncInv[this.direction]].label + ': ' + this.currentValue + 'px<br>';
        }

        tooltip.dragUpdate(e, html);
    };

    UISpacing.prototype._mouseStart = function (e) {

        this.fragmentEditor.pointerManager.contextualBarActive.enableActiveForced();
        this.fragmentEditor.pointerManager.contextualBarHover.disable();

        tooltip.dragShow();

        this.wasShiftPressed = false;
        var handle;
        for (var d in this.handles) {
            handle = this.handles[d][0];
            if (handle === e.target || $.contains(handle, e.target)) {
                this.direction = d;
                break;
            }
        }
        this.syncProperty = this.options.sync[this.direction];
        this.originalValue = parseInt(this.element.css(this.syncProperty));

        this.invSyncProperty = this.options.sync[this.options.syncInv[this.direction]];
        this.invOriginalValue = parseInt(this.element.css(this.invSyncProperty));

        this.resizing = true;

        this.originalMousePosition = {left: e.pageX, top: e.pageY};
        this.currentValue = this.originalValue;

        this.handles[this.direction].addClass('nui_spacing__handle--spacing');

        $('body')
            .css('cursor', this.direction + '-resize')
            .addClass('n2_body--inherit-cursor');

        $("body")
            .on('keydown.' + this.widgetEventPrefix, this._keyDown.bind(this))
            .on('keyup.' + this.widgetEventPrefix, this._keyUp.bind(this));

        this._trigger("start", e, this.ui());

        return true;
    };

    UISpacing.prototype._keyDown = function (e) {
        if (e.shiftKey && !this.wasShiftPressed) {
            this.wasShiftPressed = true;
            this.updateElementProp(this.invSyncProperty, this.currentValue);

            this.setSize(this.options.syncInv[this.direction], this.currentValue);
            this._trigger("spacing", e, this.ui());

            this.updatePositionDisplay(e);

            this._triggerInteraction();
        }
    };

    UISpacing.prototype._keyUp = function (e) {
        if (!e.shiftKey && this.wasShiftPressed) {
            this.wasShiftPressed = false;
            this.updateElementProp(this.invSyncProperty, this.invOriginalValue);

            this.setSize(this.options.syncInv[this.direction], this.invOriginalValue);
            this._trigger("spacing", e, this.ui());

            this.updatePositionDisplay(e);

            this._triggerInteraction();
        }
    };

    UISpacing.prototype._mouseDrag = function (e) {

        this.movement = this['_parse_movement_' + this.direction].call(this, e);

        this.currentValue = this.limitValue(_N2._roundHelper(this.originalValue + this.movement));

        this.updateElementProp(this.syncProperty, this.currentValue);
        this.setSize(this.direction, this.currentValue);
        if (e.shiftKey) {
            this.wasShiftPressed = true;
            this.updateElementProp(this.invSyncProperty, this.currentValue);

            this.setSize(this.options.syncInv[this.direction], this.currentValue);
        } else if (this.wasShiftPressed) {
            this.wasShiftPressed = false;
            this.updateElementProp(this.invSyncProperty, this.invOriginalValue);
            this.setSize(this.options.syncInv[this.direction], this.invOriginalValue);
        }
        this._trigger("spacing", e, this.ui());

        this._triggerInteraction();

        this.updatePositionDisplay(e);
    };

    UISpacing.prototype._mouseStop = function (e) {
        this.movement = this['_parse_movement_' + this.direction].call(this, e);

        this.currentValue = this.limitValue(_N2._roundHelper(this.originalValue + this.movement));

        this.updateElementProp(this.syncProperty, this.currentValue);

        if (e.shiftKey) {
            this.updateElementProp(this.invSyncProperty, this.currentValue);
        } else if (this.wasShiftPressed) {
            this.updateElementProp(this.invSyncProperty, this.invOriginalValue);
        }

        this.resizing = false;

        $("body").off('.' + this.widgetEventPrefix);

        this.handles[this.direction].removeClass('nui_spacing__handle--spacing');

        $('body')
            .css('cursor', '')
            .removeClass('n2_body--inherit-cursor');

        this._trigger("stop", e, this.ui());

        tooltip.dragHide();

        this._triggerInteraction();

        this.fragmentEditor.pointerManager.contextualBarActive.disableActiveForced();
        this.fragmentEditor.pointerManager.contextualBarHover.enable();

        _N2.WindowManager.actionPrevent('ComponentActivate');
        return false;
    };

    UISpacing.prototype.limitValue = function (value) {
        return value;
    };

    UISpacing.prototype.ui = function () {
        var changed = {};
        changed[this.options.side[this.direction].value] = this.currentValue;
        if (this.wasShiftPressed) {
            changed[this.options.side[this.options.syncInv[this.direction]].value] = this.currentValue;
        }
        return {
            element: this.element,
            changed: changed
        };
    };

    UISpacing.prototype.setOption = function (key, value) {
        _N2.UIWidgetBase.prototype.setOption.apply(this, arguments);

        if (key === "current") {
            var values = value.split(' ');
            this.setSize('n', values[0]);
            this.setSize('e', values[1]);
            this.setSize('s', values[2]);
            this.setSize('w', values[3]);
        }
    };

    UISpacing.prototype.updateElementProp = function (property, value) {
        this.element.css(property, value + 'px');
    };

    UISpacing.prototype.setSize = function (handle, value) {
        var negative = false,
            valueWithUnit;
        if (typeof value === 'number') {
            if (value < 0) {
                negative = true;
                value *= -1;
            }
            valueWithUnit = value + 'px';
        } else {
            if (value.substr(0, 1) === '-') {
                negative = true;
                value = value.substr(1);
            }
            valueWithUnit = value;
        }

        this.handles[handle].css(handleSizing[handle], valueWithUnit);

        this.handles[handle].toggleClass('nui_spacing__handle--negative', negative);
        this.handles[handle].toggleClass('nui_spacing__handle--zero', parseFloat(value) === 0);
    };

    return UISpacing;
});_N2.d('PlacementAbsolute', ['$', 'PlacementAbstract'], function () {
    var $ = _N2.$;
    "use strict";

    var resizeCollection = {
            raf: false,
            ratios: null,
            isThrottled: false,
            layers: []
        },
        requestRender = function () {
            if (resizeCollection.raf === false) {
                resizeCollection.raf = true;
                requestAnimationFrame(function () {

                    var localResizeCollection = resizeCollection;

                    resizeCollection = {
                        raf: false,
                        ratios: null,
                        isThrottled: false,
                        layers: []
                    };

                    for (var i = 0; i < localResizeCollection.layers.length; i++) {
                        if (!localResizeCollection.layers[i].isDeleted) {
                            localResizeCollection.layers[i].doTheResize(localResizeCollection.ratios, true, localResizeCollection.isThrottled);
                        }
                    }
                });
            }
        };

    /**
     * @memberOf _N2
     *
     * @param placement
     * @param layer
     * @param fragmentEditor
     * @constructor
     * @augments _N2.PlacementAbstract
     */
    function PlacementAbsolute(placement, layer, fragmentEditor) {
        this.type = 'absolute';

        this.transferredProperties = {};

        _N2.PlacementAbstract.prototype.constructor.apply(this, arguments);

        this.doThrottledTheResize = this.doTheResize;

        this.parentIsVisible = true; // Related to parent child layer picker
    }

    PlacementAbsolute.prototype = Object.create(_N2.PlacementAbstract.prototype);
    PlacementAbsolute.prototype.constructor = PlacementAbsolute;

    /**
     *
     * @param {_N2.ComponentSettings} componentSettings
     * @returns {{}}
     */
    PlacementAbsolute.getRegistryDataToFields = function (componentSettings) {

        return {
            parentid: $('#layerparentid'),
            parentalign: $('#layerparentalign'),
            parentvalign: $('#layerparentvalign'),
            left: $('#layerleft'),
            top: $('#layertop'),
            responsiveposition: $('#layerresponsive-position'),
            width: $('#layerwidth'),
            height: $('#layerheight'),
            responsivesize: $('#layerresponsive-size'),
            align: $('#layeralign'),
            valign: $('#layervalign'),
            adaptivefont: $('#layeradaptive-font')
        };
    };

    PlacementAbsolute.prototype.start = function () {
        this.$layer = this.layer.layer;
        this.$layerRow = this.layer.layerRow;
    };

    PlacementAbsolute.prototype.preActivation = function (lastPlacement) {
        if (lastPlacement.type === 'normal') {
            var height = this.layer.getProperty('height');
            if (height > 0) {
                this.transferredProperties.height = height;
            }
        }
    };

    PlacementAbsolute.prototype.activated = function (properties) {

        this.___layerSMW = _N2.MW.___getSMW(this.layer.layer[0]);

        var parentid = this.$layer.data('parentid');
        if (parentid) {
            var $parent = $('#' + parentid);
            if ($parent.length > 0) {
                this.activatedAfterParentReady(properties);
            } else {
                setTimeout((function () {
                    this.activatedAfterParentReady(properties);
                    this.sync();
                }).bind(this), 300);
            }
        } else {
            this._activated(properties);
        }
    };

    PlacementAbsolute.prototype.activatedAfterParentReady = function (properties) {
        var parentid = this.$layer.data('parentid');
        if (parentid) {
            var $parent = $('#' + parentid);
            if ($parent.length > 0) {
                var layerObject = $parent.data('layerObject');
                if (layerObject) {
                    layerObject.readyDeferred.done(this._activated.bind(this, properties));
                } else {
                    $parent.on('layerStarted', (function (e, layerObject) {
                        layerObject.readyDeferred.done(this._activated.bind(this, properties));
                    }).bind(this));
                }
            } else {
                this.$layer.data('parentid', '');
                this._activated(properties);
            }
        } else {
            this._activated(properties);
        }
    };

    PlacementAbsolute.prototype._activated = function (properties) {
        this.loadProperties(_Assign(properties, this.transferredProperties));
        this.transferredProperties = {};

        this.___makeLayerAlign();
        this.___makeLayerResizeable();

        this.layer.layerRow.addClass('n2_layer_navigation_list_layer--absolute');
        $('<div class="n2_layer_navigation_list_layer__title_action"><div class="n2_layer_navigation_list_layer__title_absolute_marker" data-n2tip="Absolute">A</div></div>')
            .prependTo(this.layer.layerTitle.find('.n2_layer_navigation_list_layer__title_actions'));
    };

    PlacementAbsolute.cleanLayer = function ($layer) {
        var devices = [
            'desktopPortrait',
            'desktopLandscape',
            'tabletPortrait',
            'tabletLandscape',
            'mobilePortrait',
            'mobileLandscape'
        ];

        this.___layerSMW.xAbs = 0;
        this.___layerSMW.xPAbs = 0;
        this.___layerSMW.yAbs = 0;
        this.___layerSMW.yPAbs = 0;

        $layer
            .removeAttr('data-align')
            .removeAttr('data-valign')
            .css({
                width: '',
                height: '',
                'text-align': ''
            });
        var properties = ['parentid', 'responsiveposition', 'responsivesize', 'parentalign', 'parentvalign',
            'align', 'valign', 'left', 'top', 'width', 'height'];

        var data = {};
        for (var i = 0; i < properties.length; i++) {
            var prop = properties[i].toLowerCase();
            data[prop] = $layer.data(prop);
            $layer.removeAttr(prop);
            $layer.removeData(prop);
            for (var j = 0; j < devices.length; j++) {
                var device = devices[j].toLowerCase();
                data[device + prop] = $layer.data(device + prop);
                $layer.removeAttr(device + prop);
                $layer.removeData(device + prop);
            }
        }
        return data;
    };

    PlacementAbsolute.prototype.deActivated = function (newMode) {

        this.layer.layerRow.removeClass('n2_layer_navigation_list_layer--absolute');
        this.layer.layerTitle.find('.n2_layer_navigation_list_layer__title_absolute_marker').remove();

        var value = this.layer.getProperty('parentid');
        if (value && value != '') {
            this.$layer.removeAttr('data-parentid');
            this.unSubscribeParent();
        }

        this.$layer
            .removeAttr('data-align')
            .removeAttr('data-valign')
            .css({
                left: '',
                top: '',
                right: '',
                bottom: '',
                width: '',
                height: '',
                'text-align': ''
            });

        this.___layerSMW.xAbs = 0;
        this.___layerSMW.xPAbs = 0;
        this.___layerSMW.yAbs = 0;
        this.___layerSMW.yPAbs = 0;

        this.alignMarker.remove();
        this.uiResizeAbsolute.destroy();
        this.$layer.off('.n2-ss-absolute');

        this.$layer.triggerHandler('LayerAbsoluteUnavailable');

        var properties = ['parentid', 'responsiveposition', 'responsivesize', 'parentalign', 'parentvalign',
                'align', 'valign', 'left', 'top', 'width', 'height'],
            historicalData = this.layer.getPropertiesData(properties);

        this.layer.removeProperties(properties);

        return historicalData;
    };

    PlacementAbsolute.prototype.loadProperties = function (options) {
        this.layer.createProperty('parentid', null, this.layer.layer, this);

        this.layer.createProperty('responsiveposition', 1, this.layer.layer, this);
        this.layer.createProperty('responsivesize', 1, this.layer.layer, this);

        this.layer.createDeviceProperty('parentalign', {desktopPortrait: 'center'}, this.layer.layer, this);
        this.layer.createDeviceProperty('parentvalign', {desktopPortrait: 'middle'}, this.layer.layer, this);

        this.layer.createDeviceProperty('align', {desktopPortrait: options.align || 'center'}, this.layer.layer, this);
        this.layer.createDeviceProperty('valign', {desktopPortrait: options.valign || 'middle'}, this.layer.layer, this);

        this.layer.createDeviceProperty('left', {desktopPortrait: options.left || 0}, this.layer.layer, this);
        this.layer.createDeviceProperty('top', {desktopPortrait: options.top || 0}, this.layer.layer, this);

        this.layer.createDeviceProperty('width', {desktopPortrait: options.width || 'auto'}, this.layer.layer, this);
        this.layer.createDeviceProperty('height', {desktopPortrait: options.height || 'auto'}, this.layer.layer, this);

        this.layer.createProperty('adaptivefont', 1, this.layer.layer, this);

        var $layer = this.layer.layer;

        this.subscribeParentCallbacks = {};
        if (this.layer.getProperty('parentid')) {
            this.subscribeParent();
        }

        $layer.attr({
            'data-align': this.layer.getProperty('align'),
            'data-valign': this.layer.getProperty('valign')
        });
    };

    PlacementAbsolute.prototype.___makeLayerAlign = function () {
        this.alignMarker = $('<div class="n2-ss-layer-cc"></div>')
            .attr('data-handle', 'cc')
            .appendTo(this.$layer);

        this.layer.editorUI.absoluteCC = this.alignMarker;
    };

    /**
     * Add resize handles to the specified layer
     * @private
     */
    PlacementAbsolute.prototype.___makeLayerResizeable = function () {
        this._resizableJustClick = false;

        /**
         * @type {_N2.UIResizeAbsolute}
         */
        this.uiResizeAbsolute = new _N2.UIResizeAbsolute(this.$layer, this.layer.fragmentEditor, {
            _containment: this.$layer[0].parentNode,
            start: this.____makeLayerResizeableStart.bind(this),
            resize: this.____makeLayerResizeableResize.bind(this),
            stop: this.____makeLayerResizeableStop.bind(this),
            create: (function () {
                this.$layer.find('.nui_resize_absolute__handle, .n2-ss-layer-cc').on({
                    mousedown: (function (e) {
                        this._resizableJustClick = [e.clientX, e.clientY];
                    }).bind(this),
                    mouseup: (function (e) {
                        if (this._resizableJustClick && Math.abs(Math.sqrt(Math.pow(this._resizableJustClick[0] - e.clientX, 2) + Math.pow(this._resizableJustClick[1] - e.clientY, 2))) < 1) {
                            var $target = $(e.currentTarget),
                                layerFeatures = this.fragmentEditor.layerOptions.layerFeatures;

                            switch ($target.data('handle')) {
                                case 'nw':
                                    layerFeatures.horizontalAlign('left', false);
                                    layerFeatures.verticalAlign('top', false);
                                    break;
                                case 'w':
                                    layerFeatures.horizontalAlign('left', false);
                                    layerFeatures.verticalAlign('middle', false);
                                    break;
                                case 'sw':
                                    layerFeatures.horizontalAlign('left', false);
                                    layerFeatures.verticalAlign('bottom', false);
                                    break;
                                case 'n':
                                    layerFeatures.horizontalAlign('center', false);
                                    layerFeatures.verticalAlign('top', false);
                                    break;
                                case 'cc':
                                    layerFeatures.horizontalAlign('center', false);
                                    layerFeatures.verticalAlign('middle', false);
                                    break;
                                case 's':
                                    layerFeatures.horizontalAlign('center', false);
                                    layerFeatures.verticalAlign('bottom', false);
                                    break;
                                case 'ne':
                                    layerFeatures.horizontalAlign('right', false);
                                    layerFeatures.verticalAlign('top', false);
                                    break;
                                case 'e':
                                    layerFeatures.horizontalAlign('right', false);
                                    layerFeatures.verticalAlign('middle', false);
                                    break;
                                case 'se':
                                    layerFeatures.horizontalAlign('right', false);
                                    layerFeatures.verticalAlign('bottom', false);
                                    break;
                            }
                        }
                        this._resizableJustClick = false;
                    }).bind(this)
                });
            }).bind(this),
            smartguides: (function () {
                return this.fragmentEditor.getSnap(this.layer.group, this.layer);
            }).bind(this),
            tolerance: 5
        });

        this.layer.editorUI.resize = this.uiResizeAbsolute.$handles;

        this.$layer.on({
            'mousedown.n2-ss-absolute': (function (e) {
                if (e.button === 0) {
                    if ($(e.target).closest(".nui_resize_absolute__handle, .nui_resize_normal__handle, .nui_spacing__handle").length === 0) {
                        _N2.PositionDisplay.get().show('Canvas');

                        _N2.PositionDisplay.get().update(e, 'Canvas', 'W: ' + parseInt(this.$layer.width()) + 'px<br>H: ' + parseInt(this.$layer.height()) + 'px');

                        if (document.activeElement) {
                            document.activeElement.blur();
                        }

                        document.addEventListener('mouseup', function () {
                            _N2.PositionDisplay.get().hide('Canvas');
                        }, {
                            capture: true,
                            once: true
                        });
                    }
                }
            }).bind(this)
        });
    };

    PlacementAbsolute.prototype.____makeLayerResizeableStart = function (event, ui) {
        if (this._resizableJustClick) {
            this._resizableJustClick = false;
        }
        this.____makeLayerResizeableResize(event, ui);
        _N2.PositionDisplay.get().show('Canvas');
    };

    PlacementAbsolute.prototype.____makeLayerResizeableResize = function (e, ui) {

        _N2.PositionDisplay.get().update(e, 'Canvas', 'W: ' + ui.rect.width + 'px<br>H: ' + ui.rect.height + 'px');

        this.triggerLayerResized();
    };

    PlacementAbsolute.prototype.____makeLayerResizeableStop = function (event, ui) {

        var isAutoWidth = false;
        if (ui.axis === "n" || ui.axis === "s" || ui.originalRect.width === ui.rect.width) {
            var currentValue = this.layer.getProperty('width');
            if (this.layer.isDimensionPropertyAccepted(currentValue)) {
                isAutoWidth = true;
                this._syncwidth();
            }
        }

        var isAutoHeight = false;
        if (ui.axis === "e" || ui.axis === "w" || ui.originalRect.height === ui.rect.height) {
            var currentValue = this.layer.getProperty('height');
            if (this.layer.isDimensionPropertyAccepted(currentValue)) {
                isAutoHeight = true;
                this._syncheight();
            }
        }

        var ratioSizeH = this.fragmentEditor.getResponsiveRatioHorizontal(),
            ratioSizeV = this.isSingleAxis() ? this.fragmentEditor.getResponsiveRatioHorizontal() : this.fragmentEditor.getResponsiveRatioVertical();

        if (!parseInt(this.layer.getProperty('responsivesize'))) {
            ratioSizeH = ratioSizeV = 1;
        }
        var width = null;
        if (!isAutoWidth) {
            width = Math.round(ui.rect.width * (1 / ratioSizeH));
        }
        var height = null;
        if (!isAutoHeight) {
            height = Math.round(ui.rect.height * (1 / ratioSizeV));
        }

        this._setPosition(null, null, ui.position.left, ui.position.top, width, height, true);

        this.triggerLayerResized();

        _N2.PositionDisplay.get().hide('Canvas');
    };

    PlacementAbsolute.prototype._setPosition = function (align, valign, left, top, width, height, isPositionAbsolute) {
        var mode = this.layer.getMode();
        if (align === null) {
            align = this.layer.getProperty('align');
        }
        if (valign === null) {
            valign = this.layer.getProperty('valign');
        }

        if (left === null) {
            left = this.layer.getProperty('left');
        } else if (isPositionAbsolute) {
            left = this.calculatePositionLeft(align, left);
        }

        if (top === null) {
            top = this.layer.getProperty('top');
        } else if (isPositionAbsolute) {
            top = this.calculatePositionTop(valign, top);
        }

        if (width === null) {
            width = this.layer.getProperty('width');
        }

        if (height === null) {
            height = this.layer.getProperty('height');
        }

        var task = _N2.History.get().addValue(this.layer, this.layer.historyStoreOnPlacement, ['historyStorePosition', mode]);
        if (task) {
            task.setValues({
                align: this.layer.getRawProperty('align'),
                valign: this.layer.getRawProperty('valign'),
                left: this.layer.getRawProperty('left'),
                top: this.layer.getRawProperty('top'),
                width: this.layer.getRawProperty('width'),
                height: this.layer.getRawProperty('height')
            }, {
                align: align,
                valign: valign,
                left: left,
                top: top,
                width: width,
                height: height
            });
        }

        _N2.History.get().off();

        this.layer.store(undefined, 'width', width, true, 'layer');
        this.layer.$.trigger('propertyChanged', ['width', width]);

        this.layer.store(undefined, 'height', height, true, 'layer');
        this.layer.$.trigger('propertyChanged', ['height', height]);

        this.layer.store(undefined, 'align', align, true, 'layer');
        this.layer.$.trigger('propertyChanged', ['align', align]);

        this.layer.store(undefined, 'valign', valign, true, 'layer');
        this.layer.$.trigger('propertyChanged', ['valign', valign]);

        this.layer.store(undefined, 'left', left, true, 'layer');
        this.layer.$.trigger('propertyChanged', ['left', left]);

        this.layer.store(undefined, 'top', top, true, 'layer');
        this.layer.$.trigger('propertyChanged', ['top', top]);

        _N2.History.get().on();

    };

    PlacementAbsolute.prototype.historyStorePosition = function (values, mode) {

        this.layer.historyStore(values.align, 'align', mode);
        this.layer.historyStore(values.valign, 'valign', mode);

        this.layer.historyStore(values.width, 'width', mode);
        this.layer.historyStore(values.height, 'height', mode);

        this.layer.historyStore(values.left, 'left', mode);
        this.layer.historyStore(values.top, 'top', mode);

        this.triggerLayerResized();
    };

    PlacementAbsolute.prototype.calculatePositionLeft = function (align, left) {
        var ratioH = this.fragmentEditor.getResponsiveRatioHorizontal();

        if (!parseInt(this.layer.getProperty('responsiveposition'))) {
            ratioH = 1;
        }
        var p = {
            left: 0,
            leftMultiplier: 1
        };

        if (this.parentLayerPlacement && this.parentLayerPlacement.isVisible()) {
            var parentPositionSize = this.parentLayerPlacement.getPositionSize();

            if (this.parentLayerPlacement.current instanceof _N2.PlacementAbsolute) {
                var parentLayer = this.parentLayerPlacement.layer.layer[0],
                    parentSMW = _N2.MW.___getSMW(parentLayer);

                parentPositionSize.width = parentLayer.offsetWidth;
                parentPositionSize.height = parentLayer.offsetHeight;

                parentPositionSize.left += parentSMW.xAbs + parentSMW.xPAbs / 100 * parentPositionSize.width;
                parentPositionSize.top += parentSMW.yAbs + parentSMW.yPAbs / 100 * parentPositionSize.height;
            }

            switch (this.layer.getProperty('parentalign')) {
                case 'right':
                    p.left = parentPositionSize.left + this.parent.layer[0].offsetWidth;
                    break;
                case 'center':
                    p.left = parentPositionSize.left + Math.round(this.parent.layer[0].offsetWidth / 2);
                    break;
                default:
                    p.left = parentPositionSize.left;
            }
        } else {
            var $parent = this.$layer.parent();

            switch (align) {
                case 'center':
                    p.left += Math.round($parent[0].offsetWidth / 2);
                    break;
                case 'right':
                    p.left += $parent[0].offsetWidth;
                    break;
            }
        }

        switch (align) {
            case 'left':
                left = -Math.round((p.left - left) * (1 / ratioH));
                break;
            case 'center':
                left = -Math.round((p.left - left - this.$layer.width() / 2) * (1 / ratioH));
                break;
            case 'right':
                left = -Math.round((p.left - left - this.$layer.width()) * (1 / ratioH));
                break;
        }

        return left;
    };


    PlacementAbsolute.prototype.calculatePositionTop = function (valign, top) {

        var ratioV = this.isSingleAxis() ? this.fragmentEditor.getResponsiveRatioHorizontal() : this.fragmentEditor.getResponsiveRatioVertical();

        if (!parseInt(this.layer.getProperty('responsiveposition'))) {
            ratioV = 1;
        }


        var p = {
            top: 0,
            topMultiplier: 1
        };

        if (this.parentLayerPlacement && this.parentLayerPlacement.isVisible()) {
            var parentPositionSize = this.parentLayerPlacement.getPositionSize();

            if (this.parentLayerPlacement.current instanceof _N2.PlacementAbsolute) {
                var parentLayer = this.parentLayerPlacement.layer.layer[0],
                    parentSMW = _N2.MW.___getSMW(parentLayer);

                parentPositionSize.width = parentLayer.offsetWidth;
                parentPositionSize.height = parentLayer.offsetHeight;

                parentPositionSize.left += parentSMW.xAbs + parentSMW.xPAbs / 100 * parentPositionSize.width;
                parentPositionSize.top += parentSMW.yAbs + parentSMW.yPAbs / 100 * parentPositionSize.height;
            }

            switch (this.layer.getProperty('parentvalign')) {
                case 'bottom':
                    p.top = parentPositionSize.top + this.parent.layer[0].offsetHeight;
                    break;
                case 'middle':
                    p.top = parentPositionSize.top + Math.round(this.parent.layer[0].offsetHeight / 2);
                    break;
                default:
                    p.top = parentPositionSize.top;
            }
        } else {
            var $parent = this.$layer.parent();

            switch (valign) {
                case 'middle':
                    p.top += Math.round($parent[0].offsetHeight / 2);
                    break;
                case 'bottom':
                    p.top += $parent[0].offsetHeight;
                    break;
            }
        }

        switch (valign) {
            case 'top':
                top = -Math.round((p.top - top) * (1 / ratioV));
                break;
            case 'middle':
                top = -Math.round((p.top - top - this.$layer.height() / 2) * (1 / ratioV));
                break;
            case 'bottom':
                top = -Math.round((p.top - top - this.$layer.height()) * (1 / ratioV));
                break;
        }

        return top;
    };

    PlacementAbsolute.prototype.moveX = function (x) {

        this._setPosition(null, null, this.layer.getProperty('left') + x, null, null, null, false);
    };

    PlacementAbsolute.prototype.moveY = function (y) {

        this._setPosition(null, null, null, this.layer.getProperty('top') + y, null, null, false);
    };

    PlacementAbsolute.prototype.setPositionLeft = function (left) {

        left = this.calculatePositionLeft(this.layer.getProperty('align'), left);

        this.layer.store(undefined, 'left', left, true);
        this.layer.$.trigger('propertyChanged', ['left', left]);

    };

    PlacementAbsolute.prototype.setPositionTop = function (top) {

        top = this.calculatePositionTop(this.layer.getProperty('valign'), top);

        this.layer.store(undefined, 'top', top, true);
        this.layer.$.trigger('propertyChanged', ['top', top]);
    };

    PlacementAbsolute.prototype.setPosition = function (left, top) {
        this.setPositionLeft(left);
        this.setPositionTop(top);
    };

    PlacementAbsolute.prototype.setDeviceBasedAlign = function () {
        var mode = this.layer.getMode();
        if (this.layer.deviceProperty[mode]['align'] === undefined) {
            this.layer.setProperty('align', this.layer.getProperty('align'), 'layer');
        }
        if (this.layer.deviceProperty[mode]['valign'] === undefined) {
            this.layer.setProperty('valign', this.layer.getProperty('valign'), 'layer');
        }
    };


    PlacementAbsolute.prototype.setPropertyresponsiveposition =
        PlacementAbsolute.prototype.setPropertyresponsivesize = function (name, value, from) {
            this.layer._setProperty(name, parseInt(value), from);
        };


    PlacementAbsolute.prototype.setPropertywidth =
        PlacementAbsolute.prototype.setPropertyheight = function (name, value, from) {
            var v = value;
            if (!this.layer.isDimensionPropertyAccepted(value)) {
                v = ~~value;
                if (v != value) {
                    this.layer.$.trigger('propertyChanged', [name, v]);
                }
            }
            setTimeout((function () {
                this.onResize(false);
            }).bind(this), 50);

            this.layer._setProperty(name, v, from);
        };

    PlacementAbsolute.prototype.setPropertyleft =
        PlacementAbsolute.prototype.setPropertytop = function (name, value, from) {
            var v = ~~value;
            if (v != value) {
                this.layer.$.trigger('propertyChanged', [name, v]);
            }
            this.layer._setProperty(name, v, from);
        };

    PlacementAbsolute.prototype.render = function (name) {
        this['_sync' + name]();
    };

    PlacementAbsolute.prototype.renderWithModifier = function (name, value, modifier) {
        try {
            if ((name == 'width' || name == 'height') && this.layer.isDimensionPropertyAccepted(value)) {
                this['_sync' + name](value);
            } else {
                this['_sync' + name](Math.round(value * modifier));
            }
        } catch (e) {
            console.error('_sync' + name);
        }
    };

    PlacementAbsolute.prototype.startResize = function () {
        this.doTheResize({
            slideW: this.fragmentEditor.getResponsiveRatioHorizontal(),
            slideH: this.isSingleAxis() ? this.fragmentEditor.getResponsiveRatioHorizontal() : this.fragmentEditor.getResponsiveRatioVertical()
        }, true, true);
    };

    PlacementAbsolute.prototype.onResize = function (isForced) {
        this.resize({
            slideW: this.fragmentEditor.getResponsiveRatioHorizontal(),
            slideH: this.isSingleAxis() ? this.fragmentEditor.getResponsiveRatioHorizontal() : this.fragmentEditor.getResponsiveRatioVertical()
        }, isForced);
    };

    PlacementAbsolute.prototype.resize = function (ratios, isForced) {

        if (!this.parent || isForced) {
            this.addToResizeCollection(this, ratios, false);
        }
    };

    PlacementAbsolute.prototype.addToResizeCollection = function (layer, ratios, isThrottled) {
        resizeCollection.ratios = ratios;
        resizeCollection.isThrottled = isThrottled;
        for (var i = 0; i < resizeCollection.layers.length; i++) {
            if (resizeCollection.layers[i] === this) {
                resizeCollection.layers.splice(i, 1);
                break;
            }
        }
        resizeCollection.layers.push(layer);

        requestRender();
        this.triggerLayerResized(isThrottled, ratios);
    };

    PlacementAbsolute.prototype._syncresponsiveposition = function () {
        this.onResize(true);
    };

    PlacementAbsolute.prototype._syncwidth = function () {
        var value = this.layer.getProperty('width');

        if (!this.layer.isDimensionPropertyAccepted(value)) {
            if (parseInt(this.layer.getProperty('responsivesize'))) {
                var ratio = this.fragmentEditor.getResponsiveRatioHorizontal();
                value = (value * ratio);
            }
            value += 'px';
        }

        this.$layer.css('width', value);
    };

    PlacementAbsolute.prototype._syncheight = function () {
        var value = this.layer.getProperty('height');
        if (!this.layer.isDimensionPropertyAccepted(value)) {
            if (parseInt(this.layer.getProperty('responsivesize'))) {
                var ratio = this.isSingleAxis() ? this.fragmentEditor.getResponsiveRatioHorizontal() : this.fragmentEditor.getResponsiveRatioVertical();
                value = (value * ratio);
            }
            value += 'px';
        }

        this.$layer.css('height', value);
    };

    PlacementAbsolute.prototype._syncparentalign = function () {
        var value = this.layer.getProperty('parentalign');
        this.$layer.data('parentalign', value);
        var parent = this.getParent();
        if (parent) {
            parent.placement.current.onResize(false);
        }
    };

    PlacementAbsolute.prototype._syncparentvalign = function () {
        var value = this.layer.getProperty('parentvalign');
        this.$layer.data('parentvalign', value);
        var parent = this.getParent();
        if (parent) {
            parent.placement.current.onResize(false);
        }
    };


    PlacementAbsolute.prototype._syncleft = function () {

        var layerSMW = this.___layerSMW,
            align = this.layer.getProperty('align'),
            left = this.layer.getProperty('left');

        if (parseInt(this.layer.getProperty('responsiveposition'))) {
            left *= this.fragmentEditor.getResponsiveRatioHorizontal();
        }

        if (this.parentLayerPlacement && this.parentLayerPlacement.isVisible()) {
            var parentPositionSize = this.parentLayerPlacement.getPositionSize(),
                parentAlignPosition = {left: 0};

            if (this.parentLayerPlacement.current instanceof _N2.PlacementAbsolute) {
                var parentLayer = this.parentLayerPlacement.layer.layer[0],
                    parentSMW = _N2.MW.___getSMW(parentLayer);

                parentPositionSize.width = parentLayer.offsetWidth;
                parentPositionSize.height = parentLayer.offsetHeight;

                parentPositionSize.left += parentSMW.xAbs + parentSMW.xPAbs / 100 * parentPositionSize.width;
                parentPositionSize.top += parentSMW.yAbs + parentSMW.yPAbs / 100 * parentPositionSize.height;
            }

            switch (this.layer.getProperty('parentalign')) {
                case 'right':
                    parentAlignPosition.left = parentPositionSize.left + parentPositionSize.width;
                    break;
                case 'center':
                    parentAlignPosition.left = parentPositionSize.left + parentPositionSize.width / 2;
                    break;
                default:
                    parentAlignPosition.left = parentPositionSize.left;
            }

            switch (align) {
                case 'right':
                    layerSMW.xAbs = Math.round(parentAlignPosition.left + left);
                    layerSMW.xPAbs = -100;
                    break;
                case 'center':
                    layerSMW.xAbs = Math.round(parentAlignPosition.left + left);
                    layerSMW.xPAbs = -50;
                    break;
                default:
                    layerSMW.xAbs = Math.round(parentAlignPosition.left + left);
                    layerSMW.xPAbs = 0;
                    break;
            }
        } else {
            var parentWidth = this.layer.layer[0].parentNode.offsetWidth;

            switch (align) {
                case 'right':
                    layerSMW.xAbs = Math.round(parentWidth + left);
                    layerSMW.xPAbs = -100;
                    break;
                case 'center':
                    layerSMW.xAbs = Math.round(parentWidth / 2 + left);
                    layerSMW.xPAbs = -50;
                    break;
                default:
                    layerSMW.xAbs = Math.round(left);
                    layerSMW.xPAbs = 0;
                    break;
            }
        }

        this.triggerLayerResized();
    };

    PlacementAbsolute.prototype._synctop = function () {

        var layerSMW = this.___layerSMW,
            valign = this.layer.getProperty('valign'),
            top = this.layer.getProperty('top');

        if (parseInt(this.layer.getProperty('responsiveposition'))) {
            top *= this.isSingleAxis() ? this.fragmentEditor.getResponsiveRatioHorizontal() : this.fragmentEditor.getResponsiveRatioVertical();
        }

        if (this.parentLayerPlacement && this.parentLayerPlacement.isVisible()) {
            var parentPositionSize = this.parentLayerPlacement.getPositionSize(),
                parentAlignPosition = {height: 0};

            if (this.parentLayerPlacement.current instanceof _N2.PlacementAbsolute) {
                var parentLayer = this.parentLayerPlacement.layer.layer[0],
                    parentSMW = _N2.MW.___getSMW(parentLayer);

                parentPositionSize.width = parentLayer.offsetWidth;
                parentPositionSize.height = parentLayer.offsetHeight;

                parentPositionSize.left += parentSMW.xAbs + parentSMW.xPAbs / 100 * parentPositionSize.width;
                parentPositionSize.top += parentSMW.yAbs + parentSMW.yPAbs / 100 * parentPositionSize.height;
            }

            switch (this.layer.getProperty('parentvalign')) {
                case 'bottom':
                    parentAlignPosition.top = parentPositionSize.top + parentPositionSize.height;
                    break;
                case 'middle':
                    parentAlignPosition.top = parentPositionSize.top + parentPositionSize.height / 2;
                    break;
                default:
                    parentAlignPosition.top = parentPositionSize.top;
            }

            switch (valign) {
                case 'bottom':
                    layerSMW.yAbs = Math.round(parentAlignPosition.top + top);
                    layerSMW.yPAbs = -100;
                    break;
                case 'middle':
                    layerSMW.yAbs = Math.round(parentAlignPosition.top + top);
                    layerSMW.yPAbs = -50;
                    break;
                default:
                    layerSMW.yAbs = Math.round(parentAlignPosition.top + top);
                    layerSMW.yPAbs = 0;
                    break;
            }
        } else {
            var parentHeight = this.layer.layer[0].parentNode.offsetHeight;

            switch (valign) {
                case 'bottom':
                    layerSMW.yAbs = Math.round(parentHeight + top);
                    layerSMW.yPAbs = -100;
                    break;
                case 'middle':
                    layerSMW.yAbs = Math.round(parentHeight / 2 + top);
                    layerSMW.yPAbs = -50;
                    break;
                default:
                    layerSMW.yAbs = Math.round(top);
                    layerSMW.yPAbs = 0;
                    break;
            }
        }

        this.triggerLayerResized();
    };

    PlacementAbsolute.prototype._syncresponsivesize = function () {
        this.onResize(true);
    };

    PlacementAbsolute.prototype.historyStoreDoubleProp = function (data, mode, prop, prop2) {
        var currentMode = this.layer.getMode();
        if (mode == currentMode) {
            this.layer._setProperty(prop, data.value, 'history');
            this.layer._setProperty(prop2, data.value2, 'history');
        } else {
            this.layer.deviceProperty[mode][prop] = data.value;
            this.layer.deviceProperty[mode][prop2] = data.value2;
            this.layer.$.trigger('propertyChanged', [prop, this.layer.getProperty(prop)]);
            this.layer.$.trigger('propertyChanged', [prop2, this.layer.getProperty(prop2)]);
            this.layer.render(prop, null, 'history');
            this.layer.render(prop2, null, 'history');
        }
    };

    PlacementAbsolute.prototype.setPropertyalign = function (name, value, from) {
        var oldValue = this.layer.getProperty(name),
            oldLeft = this.layer.getRawProperty('left');

        _N2.History.get().off();
        this.layer._setProperty(name, value, from);
        _N2.History.get().on();

        var task = _N2.History.get().addValue(this.layer, this.layer.historyStoreOnPlacement, ['historyStoreDoubleProp', this.layer.getMode(), 'align', 'left']);
        if (task) {
            task.setValues({
                value: oldValue,
                value2: oldLeft
            }, {
                value: value,
                value2: this.layer.getRawProperty('left')
            });
        }
    };

    PlacementAbsolute.prototype.setPropertyvalign = function (name, value, from) {
        var oldValue = this.layer.getProperty(name),
            oldTop = this.layer.getRawProperty('top');

        _N2.History.get().off();
        this.layer._setProperty(name, value, from);
        _N2.History.get().on();

        var task = _N2.History.get().addValue(this.layer, this.layer.historyStoreOnPlacement, ['historyStoreDoubleProp', this.layer.getMode(), 'valign', 'top']);
        if (task) {
            task.setValues({
                value: oldValue,
                value2: oldTop
            }, {
                value: value,
                value2: this.layer.getRawProperty('top')
            });
        }
    };

    PlacementAbsolute.prototype._syncalign = function (oldValue, from) {
        var value = this.layer.getProperty('align');
        this.$layer.attr('data-align', value);

        if (from !== 'history' && value != oldValue) {
            this.setPositionLeft(Math.round(this.___layerSMW.xAbs + this.___layerSMW.xPAbs / 100 * this.$layer[0].offsetWidth));
        }
    };
    PlacementAbsolute.prototype._syncvalign = function (oldValue, from) {
        var value = this.layer.getProperty('valign');
        this.$layer.attr('data-valign', value);

        if (from !== 'history' && value != oldValue) {
            this.setPositionTop(Math.round(this.___layerSMW.yAbs + this.___layerSMW.yPAbs / 100 * this.$layer[0].offsetHeight));
        }
    };

    PlacementAbsolute.prototype.fit = function () {
        var layer = this.$layer[0];

        if (layer.scrollWidth > 0 && layer.scrollHeight > 0) {
            var resized = false;
            if (this.layer.item) {
                resized = this.layer.item.fitLayer();
            }
            if (!resized) {
                this.layer.setProperty('width', 'auto', 'layer');
                this.layer.setProperty('height', 'auto', 'layer');

                var layerWidth = this.$layer.width();
                if (Math.abs(this.fragmentEditor.mainContainer.layer.width() - this.$layer.position().left - layerWidth) < 2) {
                    this.layer.setProperty('width', layerWidth, 'layer');
                }
            }
        }
    };

    PlacementAbsolute.prototype.hide = function (targetMode) {
        this.layer.store(undefined, (targetMode ? targetMode : this.layer.getMode()), 0, true);
    };

    PlacementAbsolute.prototype.show = function (targetMode) {
        this.layer.store(undefined, (targetMode ? targetMode : this.layer.getMode()), 1, true);
    };


    PlacementAbsolute.prototype.changeStatus = function (oldStatus, newStatus) {
    };

    PlacementAbsolute.prototype.getParent = function () {
        var parentID = this.layer.getProperty('parentid');
        if (parentID) {
            return $('#' + this.layer.getProperty('parentid')).data('layerObject');
        }

        return false;
    };

    PlacementAbsolute.prototype.subscribeParent = function () {
        var $newParent = $('#' + this.layer.property.parentid);
        if (this.parent && !$newParent.is(this.parent.layer)) {
            this.parent.layer.off(this.subscribeParentCallbacks);
            this.parent = false;
        }
        if (!this.parent) {
            this.subscribeParentCallbacks = {
                LayerResized: (function () {
                    this.resizeParent.apply(this, arguments);
                }).bind(this),
                LayerDeleted: (function (e) {
                    this.layer.setProperty('parentid', '', 'layer');
                }).bind(this),
                LayerAbsoluteUnavailable: (function () {
                    this.onLayerAbsoluteUnavailable();
                }).bind(this),
                LayerShowChange: (function (e, mode, value) {
                    if (this.layer.getMode() === mode) {
                        this.parentIsVisible = value;
                    }
                }).bind(this),
                'n2-ss-activate': (function () {
                    this.$layerRow.addClass('n2-parent-active');
                }).bind(this),
                'n2-ss-deactivate': (function () {
                    this.$layerRow.removeClass('n2-parent-active');
                }).bind(this),
                'LayerGetDataWithChildren': (function (e, layersData, layers) {
                    this.layer.getDataWithChildren(layersData, layers);
                }).bind(this)
            };

            /**
             * @type {_N2.ComponentAbstract}
             */
            this.parent = $newParent.data('layerObject');
            this.parentLayerPlacement = this.parent.placement;

            this.parent.layer.on(this.subscribeParentCallbacks);

            this.$layer.on('LayerAbsoluteUnavailable.absolute', this.onLayerAbsoluteUnavailable.bind(this));

            this.$layer.addClass('n2-ss-layer-has-parent');
        }
    };

    PlacementAbsolute.prototype.onLayerAbsoluteUnavailable = function () {
        this.layer.setProperty('parentid', '', 'layer');
    };

    PlacementAbsolute.prototype.unSubscribeParent = function (context) {
        this.$layerRow.removeClass('n2-parent-active');
        this.$layer.removeClass('n2-ss-layer-has-parent');
        if (this.parent) {
            this.$layer.off('LayerAbsoluteUnavailable.absolute');
            this.parent.layer.off(this.subscribeParentCallbacks);
            this.parent = false;
            delete this.parentLayerPlacement;
            this.subscribeParentCallbacks = {};
            if (context !== 'delete') {
                var position = this.$layer.position();
                this._setPosition(null, null, position.left, position.top, null, null, true);
            }
        }

    };

    PlacementAbsolute.prototype.unlink = function (e) {
        if (e) e.preventDefault();
        this.layer.setProperty('parentid', '', 'layer');
    };

    PlacementAbsolute.prototype.parentPicked = function (parentObject, parentAlign, parentValign, align, valign) {
        this.layer.setProperty('parentid', '', 'layer');

        this.layer.setProperty('align', align, 'layer');
        this.layer.setProperty('valign', valign, 'layer');
        this.layer.setProperty('parentalign', parentAlign, 'layer');
        this.layer.setProperty('parentvalign', parentValign, 'layer');

        this.layer.setProperty('parentid', parentObject.requestID(), 'layer');

        var undef;
        for (var device in this.layer.deviceProperty) {
            if (device === 'desktopPortrait') continue;
            this.layer.deviceProperty[device].left = undef;
            this.layer.deviceProperty[device].top = undef;
            this.layer.deviceProperty[device].valign = undef;
            this.layer.deviceProperty[device].align = undef;
        }
    };

    PlacementAbsolute.prototype._syncparentid = function () {
        var value = this.layer.getProperty('parentid');
        if (!value || value === '') {
            this.$layer.removeAttr('data-parentid');
            this.unSubscribeParent();
        } else {
            if (!_N2.History.get().actionInProgress()) {
                this._linkToParentID(value, false);
            } else {
                setTimeout(this._linkToParentID.bind(this, value, true), 100);
            }
        }
    };

    PlacementAbsolute.prototype._linkToParentID = function (value, historyAction) {
        if ($('#' + value).length === 0) {
            this.layer.setProperty('parentid', '', 'layer');
        } else if (!this.parent) {
            this.$layer.attr('data-parentid', value).addClass('n2-ss-layer-has-parent');
            this.subscribeParent();

            if (!historyAction) {
                var position = this.$layer.position();
                this._setPosition(null, null, position.left, position.top, null, null, true);
            } else {
                _N2.History.get().off();
                this._setPosition(null, null, null, null, null, null, true);
                _N2.History.get().on();
            }
        }
    };

    PlacementAbsolute.prototype._renderModeProperties = function (isReset) {

        this.$layer.attr('data-align', this.layer.property.align);
        this.$layer.attr('data-valign', this.layer.property.valign);
        if (isReset) {
            this.onResize(true);
        }
    };

    PlacementAbsolute.prototype.doLinearResize = function (ratios) {
        this.doThrottledTheResize(ratios, true);
    };

    PlacementAbsolute.prototype.doTheResize = function (ratios, isLinear, isThrottled) {

        if (!this.layer.isDeleteStarted) {
            this.render('width');
            this.render('height');

            this.render('left');
            this.render('top');

            if (!isLinear) {
                this.triggerLayerResized(isThrottled, ratios);
            }
        }
    };

    PlacementAbsolute.prototype.resizeParent = function (e, ratios, isThrottled) {
        this.addToResizeCollection(this, ratios, isThrottled);
    };

    PlacementAbsolute.prototype.resizeParentLocal = function () {
        this.resizeParent(undefined, {
            slideW: this.fragmentEditor.getResponsiveRatioHorizontal(),
            slideH: this.isSingleAxis() ? this.fragmentEditor.getResponsiveRatioHorizontal() : this.fragmentEditor.getResponsiveRatioVertical()
        }, false);
    };

    PlacementAbsolute.prototype.sync = function () {

        this._syncalign(null, 'history');
        this._syncvalign(null, 'history');

        this._syncwidth();
        this._syncheight();
        this._synctop();
        this._syncleft();

        this._syncparentid();

    };

    PlacementAbsolute.prototype.delete = function () {

        var parentId = this.layer.getProperty('parentid');
        if (parentId) {
            this.unSubscribeParent('delete');
        }
    };

    return PlacementAbsolute;
});_N2.d('PlacementContent', ['$', 'PlacementAbstract'], function () {

    "use strict";

    /**
     * @memberOf _N2
     *
     * @param placement
     * @param layer
     * @param fragmentEditor
     * @constructor
     */
    function PlacementContent(placement, layer, fragmentEditor) {
        this.type = 'content';

        _N2.PlacementAbstract.prototype.constructor.apply(this, arguments);
    }

    PlacementContent.prototype = Object.create(_N2.PlacementAbstract.prototype);
    PlacementContent.prototype.constructor = PlacementContent;

    return PlacementContent;
});_N2.d('PlacementDefault', ['$', 'PlacementAbstract'], function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param placement
     * @param layer
     * @param fragmentEditor
     * @constructor
     */
    function PlacementDefault(placement, layer, fragmentEditor) {
        this.type = 'default';

        _N2.PlacementAbstract.prototype.constructor.apply(this, arguments);
    }

    PlacementDefault.prototype = Object.create(_N2.PlacementAbstract.prototype);
    PlacementDefault.prototype.constructor = PlacementDefault;

    return PlacementDefault;
});_N2.d('PlacementNormal', ['$', 'PlacementAbstract'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param placement
     * @param layer
     * @param fragmentEditor
     * @constructor
     * @augments _N2.PlacementAbstract

     */
    function PlacementNormal(placement, layer, fragmentEditor) {
        this.type = 'normal';

        this.transferredProperties = {};

        _N2.PlacementAbstract.prototype.constructor.apply(this, arguments);
    }

    PlacementNormal.prototype = Object.create(_N2.PlacementAbstract.prototype);
    PlacementNormal.prototype.constructor = PlacementNormal;

    /**
     *
     * @param {_N2.ComponentSettings} componentSettings
     * @returns {{}}
     */
    PlacementNormal.getRegistryDataToFields = function (componentSettings) {

        return {
            margin: $('#layernormal-margin'),
            height: $('#layernormal-height'),
            maxwidth: $('#layernormal-maxwidth'),
            selfalign: $('#layernormal-selfalign')
        };
    };

    PlacementNormal.prototype.preActivation = function (lastPlacement) {
        if (lastPlacement.type === 'absolute' && this.layer.item && this.layer.item.needHeight) {
            var height = this.layer.getProperty('height');
            if (height.match && height.match(/[0-9]+%$/)) {
                this.transferredProperties.height = Math.max(100, parseInt(this.$layer.parent()[0].offsetHeight * parseInt(height) / 100));
            } else if (height > 0) {
                this.transferredProperties.height = height;
            }
        }
    };

    PlacementNormal.prototype.activated = function (properties) {
        this.loadProperties(_Assign(properties, this.transferredProperties));
        this.transferredProperties = {};

        this.startUISpacing();

        this.startUISizing();

        this.$layer.on({
            mousedown: (function (e) {
                e.stopPropagation();
            })
        });
    };

    PlacementNormal.prototype.loadProperties = function (options) {
        this.layer.createDeviceProperty('margin', {desktopPortrait: '0|*|0|*|0|*|0'}, this.layer.layer, this);
        this.layer.createDeviceProperty('height', {desktopPortrait: (options.height || 0)}, this.layer.layer, this);
        this.layer.createDeviceProperty('maxwidth', {desktopPortrait: 0}, this.layer.layer, this);
        this.layer.createDeviceProperty('selfalign', {desktopPortrait: 'inherit'}, this.layer.layer, this);
    };

    PlacementNormal.cleanLayer = function ($layer) {
        var devices = [
            'desktopPortrait',
            'desktopLandscape',
            'tabletPortrait',
            'tabletLandscape',
            'mobilePortrait',
            'mobileLandscape'
        ];

        $layer
            .css({
                position: '',
                margin: '',
                height: '',
                maxWidth: ''
            });

        var properties = ['margin', 'height', 'maxwidth', 'selfalign'];

        var data = {};
        for (var i = 0; i < properties.length; i++) {
            var prop = properties[i].toLowerCase();
            data[prop] = $layer.data(prop);
            $layer.removeAttr(prop);
            $layer.removeData(prop);
            for (var j = 0; j < devices.length; j++) {
                var device = devices[j].toLowerCase();
                data[prop] = $layer.data(device + prop);
                $layer.removeAttr(device + prop);
                $layer.removeData(device + prop);
            }
        }
        return data;
    };

    PlacementNormal.prototype.deActivated = function (newMode) {
        this.layer.$.off('.placementnormal');
        this.uiSpacing.destroy();
        delete this.layer.editorUI.normalSizing;
        this.uiResizeNormal.destroy();

        var properties = ['margin', 'height', 'maxwidth', 'selfalign'],
            historicalData = this.layer.getPropertiesData(properties);
        this.layer.removeProperties(properties);


        this.layer.layer.css({
            position: '',
            margin: '',
            height: '',
            maxWidth: ''
        });
        return historicalData;
    };

    PlacementNormal.prototype._renderModeProperties = function (isReset) {

        this._syncmargin();
        this._syncheight();
        this._syncmaxwidth();
        this._syncselfalign();
    };

    PlacementNormal.prototype._syncmargin = function () {
        var margin = this.layer.getProperty('margin').split('|*|'),
            unit = 'px';

        margin.splice(4); // drop legacy unit

        _NodeSetStyles(this.layer.layer[0], {
            '--margin-top': margin[0] + 'px',
            '--margin-right': margin[1] + 'px',
            '--margin-bottom': margin[2] + 'px',
            '--margin-left': margin[3] + 'px'
        });
        this.layer.update();

        this.uiSpacing.setOption('current', margin.join(unit + ' ') + unit);
    };

    PlacementNormal.prototype.startUISpacing = function () {
        this.uiSpacing = new _N2.UISpacingMargin(this.$layer, this.layer.fragmentEditor, {
            stop: this.onSpacingStop.bind(this),
        });

        this.layer.editorUI.margin = this.uiSpacing.$handles;
    };

    PlacementNormal.prototype.onSpacingStop = function (event, ui) {
        var margin = this.layer.getProperty('margin').split('|*|');

        for (var k in ui.changed) {
            var value = ui.changed[k];
            switch (k) {
                case 'top':
                    margin[0] = Math.round(value);
                    break;
                case 'right':
                    margin[1] = Math.round(value);
                    break;
                case 'bottom':
                    margin[2] = Math.round(value);
                    break;
                case 'left':
                    margin[3] = Math.round(value);
                    break;
            }
        }

        this.layer.setProperty('margin', margin.join('|*|'));
        $('#layernormal-margin').data('field').insideChange(margin.join('|*|'));
    };

    PlacementNormal.prototype.startUISizing = function () {
        var needHeight = false;
        if (this.layer.item && this.layer.item.needHeight) {
            needHeight = true;
        }
        this.uiResizeNormal = new _N2.UIResizeNormal(this.$layer, this.layer.fragmentEditor, {
            height: needHeight,
            syncWidth: true,
            stopMaxWidth: (function (e, ui) {
                this.layer.setProperty('maxwidth', ui.value);
            }).bind(this),
            stopHeight: (function (e, ui) {
                this.layer.setProperty('height', ui.value);
            }).bind(this)
        });

        this.layer.editorUI.normalSizing = this.uiResizeNormal.$handles;
    };

    PlacementNormal.prototype._syncheight = function () {
        var height = parseInt(this.layer.getProperty('height'));

        if (height > 0) {
            this.layer.layer
                .css('height', height + 'px');
        } else {

            this.layer.layer
                .css('height', '');
        }

        this.layer.update();
    };

    PlacementNormal.prototype._syncmaxwidth = function () {
        var value = parseInt(this.layer.getProperty('maxwidth'));
        if (value <= 0 || isNaN(value)) {
            this.layer.layer.css('maxWidth', '');

            this.uiResizeNormal.setMaxWidth(-1);
        } else {
            this.layer.layer.css('maxWidth', value + 'px');

            this.uiResizeNormal.setMaxWidth(value);
        }

        this.layer.update();
    };

    PlacementNormal.prototype._syncselfalign = function () {

        _N2.ComponentAbstract.selfAlignToStyle(this.layer.layer, this.layer.getProperty('selfalign'));

        this.layer.update();
    };

    PlacementNormal.prototype.sync = function () {
        this._syncmargin();
        this._syncheight();
        this._syncmaxwidth();
    };

    return PlacementNormal;
});_N2.d('Placement', '$', function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param layer
     * @constructor
     */
    function Placement(layer) {
        this.layer = layer;
        this.fragmentEditor = layer.fragmentEditor;
        this.modes = {};
        /**
         *
         * @type {_N2.PlacementAbstract}
         */
        this.current = undefined;
    }

    Placement.prototype.allow = function (mode) {
        switch (mode) {
            case 'default':
                this.modes['default'] = new _N2.PlacementDefault(this, this.layer, this.fragmentEditor);
                break;
            case 'absolute':
                this.modes.absolute = new _N2.PlacementAbsolute(this, this.layer, this.fragmentEditor);
                break;
            case 'normal':
                this.modes.normal = new _N2.PlacementNormal(this, this.layer, this.fragmentEditor);
                break;
        }
    };

    Placement.prototype.start = function () {
        for (var k in this.modes) {
            this.modes[k].start();
        }
    };

    Placement.prototype.setMode = function (mode, properties) {
        var historicalData = false;
        properties = properties || {};
        if (this.modes[mode] !== undefined) {
            if (this.current !== this.modes[mode]) {
                var lastType;
                if (this.current) {

                    this.modes[mode].preActivation(this.current);
                    lastType = this.current.type;
                    historicalData = this.current.deActivated(this.modes[mode]);
                }
                this.current = this.modes[mode];

                this.layer.layer.attr('data-pm', this.current.type);
                this.current.activated(properties);

                this.layer.$.triggerHandler('placementChanged', [this.current.type, lastType]);
            }
        } else {
            console.error('Layer placement(' + mode + ') not allowed for this component', this.layer);
        }
        return historicalData;
    };

    Placement.prototype.getType = function () {
        return this.current.type;
    };

    Placement.prototype.startResize = function () {
        this.current.startResize();
    };

    Placement.prototype.onResize = function (isForced) {
        if (typeof this.current.onResize == 'function') {
            this.current.onResize(isForced);
        }
    };

    Placement.prototype.updatePosition = function () {
        this.layer.group.update();
        this.layer.layer.trigger('interaction');
    };

    Placement.prototype.doLinearResize = function (ratios) {
        if (this.current) {
            this.current.doLinearResize(ratios);
        }
    };

    Placement.prototype.sync = function () {
        this.current.sync();
    };

    Placement.prototype.renderModeProperties = function (isReset) {

        this.layer.adjustFontSize(false);

        this.current._renderModeProperties(isReset);
    };

    Placement.prototype.delete = function () {
        this.current.delete();
    };

    Placement.prototype.isVisible = function () {

        return +_NodeGetData(this.layer.layer[0], 'hide' + this.layer.getMode().toLowerCase()) !== 1;
    };

    Placement.prototype.getPositionSize = function () {

        var element = this.layer.layer[0];

        return {
            left: element.offsetLeft,
            top: element.offsetTop,
            width: element.offsetWidth,
            height: element.offsetHeight
        };
    };

    return Placement;
});_N2.d('PlacementAbstract', ['$', 'Placement'], function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param placement
     * @param {_N2.ComponentAbstract} layer
     * @param fragmentEditor
     * @constructor
     */
    function PlacementAbstract(placement, layer, fragmentEditor) {
        this.placement = placement;
        /**
         * @type {_N2.ComponentAbstract}
         */
        this.layer = layer;
        this.fragmentEditor = fragmentEditor;

        this._triggerLayerResizedThrottled = NextendThrottle(this._triggerLayerResized, 30);
    }

    PlacementAbstract.prototype.start = function () {
        this.$layer = this.layer.layer;
    };

    PlacementAbstract.prototype.startResize = function () {

    };

    PlacementAbstract.prototype.preActivation = function (lastPlacement) {

    };

    PlacementAbstract.prototype.activated = function () {

    };

    PlacementAbstract.prototype.deActivated = function (newMode) {

        return false;
    };

    PlacementAbstract.prototype._renderModeProperties = function (isReset) {

    };

    PlacementAbstract.prototype._hide = function () {
    };

    PlacementAbstract.prototype._show = function () {
    };

    PlacementAbstract.prototype.doLinearResize = function (ratios) {
    };

    PlacementAbstract.prototype.sync = function () {

    };

    PlacementAbstract.prototype.delete = function () {
    };

    PlacementAbstract.prototype.changeStatus = function (oldStatus, newStatus) {

    };

    PlacementAbstract.prototype.onResize = function (isForced) {

        this.triggerLayerResized(isForced);
    };

    PlacementAbstract.prototype.isSingleAxis = function () {
        if (this.layer.group.type === 'slide') {
            if (this.parent) {
                if (this.parent.placement.getType() === 'absolute') {
                    return false;
                }
            } else {
                return false;
            }
        }

        return true;
    };

    PlacementAbstract.prototype.triggerLayerResized = function (isThrottled, ratios) {
        if (isThrottled) {
            this._triggerLayerResized(isThrottled, ratios);
        } else {
            this._triggerLayerResizedThrottled(true, ratios);
        }
    };

    PlacementAbstract.prototype._triggerLayerResized = function (isThrottled, ratios) {
        if (!this.layer.isDeleted) {
            this.$layer.triggerHandler('LayerResized', [ratios || {
                slideW: this.fragmentEditor.getResponsiveRatioHorizontal(),
                slideH: this.isSingleAxis() ? this.fragmentEditor.getResponsiveRatioHorizontal() : this.fragmentEditor.getResponsiveRatioVertical()
            }, isThrottled || false]);
        }
    };

    return PlacementAbstract;
});_N2.d('Item', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     * @augments _N2.Historical
     *
     * @constructor
     */
    function Item($item, layer, itemEditor) {
        if (this.type === undefined) {
            this.type = $item.data('item');
        }

        this.needHeight = this.constructor.needHeight || false;

        this.fragmentEditor = itemEditor.fragmentEditor;
        /**
         * @type {_N2.Generator}
         */
        this.generator = this.fragmentEditor.editor.generator;

        this.self = this;
        this.$item = $item;
        this.layer = layer;
        this.itemEditor = itemEditor;

        /**
         *
         * @type {_N2.CssInjection[]}
         */
        this.css = [];
        this.fonts = [];
        this.styles = [];

        this.needFill = [];

        this.values = this.$item.data('itemvalues');
        if (typeof this.values !== 'object') {
            this.values = JSON.parse(this.values);
        }

        this.pre = 'div#' + this.fragmentEditor.editor.frontend.elementID + ' ';
        this.defaultValues = itemEditor.getItemForm(this.type).values;

        this.added();

        this.$item.data('item', this);

        this.$item.appendTo(this.layer.getContent());

        this.layer.item = this;

        if (this.$item.children().length === 0) {
            // It's create, so render the item
            this.reRender();
        }


        $('<div></div>')
            .addClass('n2-ss-item-overlay')
            .css('zIndex', '89')
            .appendTo(this.$item);

        this.$item.find('a').on('click', function (e) {
            e.preventDefault();
        });


        $(window).trigger('ItemCreated');
    }

    Item.needHeight = false;

    _N2.Historical(Item);

    Item.prototype.getLabel = function () {

        return this.type;
    };

    Item.prototype.hasTextScale = function () {

        return true;
    };

    Item.prototype.changeValue = function (property, value) {
        if (this === this.itemEditor.activeItem) {
            $('#item_' + this.type + property).data('field')
                .insideChange(value);
        } else {
            this.values[property] = value;
        }
    };

    Item.prototype.activate = function (focusFirstField, force) {
        if (this.itemEditor.setActiveItem(this, focusFirstField, force)) {
            _N2._basicCSS.activate('ss3item' + this.type, this.values, {
                font: this.fonts,
                style: this.styles
            });
            this.itemEditor.lastValues[this.type] = this.values;
        }
    };

    Item.prototype.deActivate = function () {
        _N2._basicCSS.deActivate();
    };

    Item.prototype.render = function (data, originalData) {
        this.layer.layer.triggerHandler('itemRender');
        this.$item.html('');

        this.parseAll(data);
        this._render(data);

        // These will be available on the backend render
        this.itemEditor.lastValues[this.type] = this.values = originalData;

        $('<div></div>')
            .addClass('n2-ss-item-overlay')
            .css('zIndex', '89')
            .appendTo(this.$item);

        _N2.ImagesLoaded(this.$item[0])
            .finally(this.layer.update.bind(this.layer));

        this.$item.find('a').on('click', function (e) {
            e.preventDefault();
        });
    };

    Item.prototype._render = function (data) {
    };

    Item.prototype.reRender = function (newData) {

        this.values = _Assign({}, this.getDefault(), this.values, newData);

        this.render(_Assign({}, this.values), this.values);
    };

    Item.prototype.delete = function () {
        this.$item.remove();

        if (this.itemEditor.activeItem == this) {
            this.itemEditor.activeItem = null;
        }
    };

    Item.prototype.getHTML = function (base64) {
        var item = '';
        if (base64) {

            item = '[' + this.type + ' values="' + _N2.Base64.encode(JSON.stringify(this.values)) + '"]';
        } else {
            item = $('<div class="n2-ss-item n2-ss-item-' + this.type + '"></div>')
                .attr('data-item', this.type)
                .attr('data-itemvalues', JSON.stringify(this.values));
        }
        return item;
    };

    Item.prototype.getData = function () {
        return {
            type: this.type,
            values: this.values
        };
    };


    Item.prototype.getDefault = function () {
        return {};
    };

    Item.prototype.added = function () {

    };

    Item.prototype.addedFont = function (mode, name) {
        var $input = $('#item_' + this.type + name);
        if ($input.length) {
            this.fonts.push({
                mode: mode,
                name: name,
                field: $input.data('field'),
                def: this.defaultValues[name]
            });
        }
    };

    Item.prototype.addedStyle = function (mode, name) {
        var $input = $('#item_' + this.type + name);
        if ($input.length) {
            this.styles.push({
                mode: mode,
                name: name,
                field: $input.data('field'),
                def: this.defaultValues[name]
            });
        }

    };

    Item.prototype.parseAll = function (data) {

        for (var i = 0; i < this.css.length; i++) {
            this.css[i].destroy();
        }
        this.css = [];
        for (var i = 0; i < this.fonts.length; i++) {
            if (data[this.fonts[i].name] === '') {
                data[this.fonts[i].name + 'class'] = this.fonts[i].className = '';
            } else {
                data[this.fonts[i].name + 'class'] = this.fonts[i].className = _N2.CSSRendererFont.getClass();

                this.css.push(_N2.CSSRendererFont.addCSS(_N2._pre, this.fonts[i].className, data[this.fonts[i].name], _N2.CSSRendererFont.rendererModes[this.fonts[i].mode]));
            }
        }

        for (var i = 0; i < this.styles.length; i++) {
            if (data[this.styles[i].name] === '') {
                data[this.styles[i].name + 'class'] = this.styles[i].className = '';
            } else {
                data[this.styles[i].name + 'class'] = this.styles[i].className = _N2.CSSRendererStyle.getClass();

                this.css.push(_N2.CSSRendererStyle.addCSS(_N2._pre, this.styles[i].className, data[this.styles[i].name], _N2.CSSRendererStyle.rendererModes[this.styles[i].mode]));
            }
        }

        for (var i = 0; i < this.needFill.length; i++) {
            if (data[this.needFill[i]] !== undefined) {
                data[this.needFill[i]] = this.generator.fill(data[this.needFill[i]] + '');
            }
        }
    };

    Item.prototype.resizeLayerToImage = function (image) {
        var layer = this.layer,
            $image = $("<img>")
                .attr("src", image)
                .on('load', (function () {
                    var width = $image[0].width,
                        height = $image[0].height;

                    if (width > 0 && height > 0) {
                        var $containerElement = this.fragmentEditor.editor.getMainContainerElement(),
                            maxWidth = $containerElement.width(),
                            maxHeight = $containerElement.height();

                        if (width > maxWidth) {
                            height = height * maxWidth / width;
                            width = maxWidth;
                        }
                        if (height > maxHeight) {
                            width = width * maxHeight / height;
                            //height = maxHeight;
                        }
                        _N2.History.get().off();
                        layer.setProperty('width', width);
                        layer.setProperty('height', 'auto');
                        _N2.History.get().on();
                    }
                }).bind(this));
    };

    Item.prototype.fitLayer = function (item) {
        return false;
    };

    return Item;
});_N2.d('ItemManager', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @param options
     * @constructor
     */
    function ItemManager(fragmentEditor, options) {
        this.suppressChange = false;

        this.activeItemOriginalData = null;

        this.fragmentEditor = fragmentEditor;

        this.lastValues = {};

        this.startItems();

        this.forms = {};
        this.activeForm = false;

        if (!options.isUploadDisabled) {
            var dropAreaElement = document.getElementById('n2-ss-slide-canvas-container');

            _addEventListener(dropAreaElement, 'dragover', (function (e) {
                e.preventDefault();
            }).bind(this));
            _addEventListener(dropAreaElement, 'drop', (function (e) {
                e.preventDefault();
                var files = [];
                if (e.dataTransfer.items) {
                    // Use DataTransferItemList interface to access the file(s)
                    for (var i = 0; i < e.dataTransfer.items.length; i++) {
                        // If dropped items aren't files, reject them
                        if (e.dataTransfer.items[i].kind === 'file') {
                            files.push(e.dataTransfer.items[i].getAsFile());
                        }
                    }
                } else {
                    // Use DataTransfer interface to access the file(s)
                    for (var i = 0; i < e.dataTransfer.files.length; i++) {
                        files.push(e.dataTransfer.files[i]);
                    }
                }

                if (files.length) {
                    var promises = [];
                    _N2.LoadingScreen.startLoading();

                    for (var i = 0; i < files.length; i++) {

                        promises.push(new Promise((function (resolve, reject) {
                            var xhr = new XMLHttpRequest(),
                                formData = new FormData();
                            xhr.open('POST', options.uploadUrl, true);
                            xhr.onload = (function () {
                                var response = JSON.parse(xhr.response);
                                if (response.data && response.data.name) {
                                    var layer = this.createLayerItem(this.fragmentEditor.mainContainer.getTargetGroupDefault(), {item: 'image'});
                                    layer.item.reRender({
                                        image: response.data.url
                                    });
                                    layer.item.activate();
                                } else {
                                    _N2.AjaxHelper.notification(response);
                                }

                                resolve();
                            }).bind(this);
                            xhr.onerror = function () {
                                reject();
                            };
                            formData.append('image', files[i]);
                            formData.append('path', '/' + options.uploadDir);
                            xhr.send(formData)
                        }).bind(this)));

                    }

                    Promise.all(promises).finally((function () {

                        _N2.LoadingScreen.stopLoading();
                    }).bind(this));
                }
            }).bind(this));
        }
    }

    ItemManager.prototype.setActiveItem = function (item, focusFirstField, force) {
        // Must be called through _N2.Item.activate();
        if (item !== this.activeItem || force) {
            this.activeItemOriginalData = null;

            var type = item.type;

            if (this.activeItem) {
                this.activeItem.deActivate();
            }

            this.activeForm = this.getItemForm(type);

            var values = _Assign({}, this.activeForm.values, item.values);

            this.activeItem = item;

            this.suppressChange = true;

            for (var key in values) {
                var field = $('#item_' + type + key).data('field');
                if (field) {
                    field.insideChange(values[key]);
                }
            }

            this.suppressChange = false;

            if (focusFirstField) {
                this.focusFirst();
            }
            return true;
        }
        return false;
    };

    ItemManager.prototype.focusFirst = function () {
        var field = this.activeForm.fields.eq(0).data('field');
        if (this.fragmentEditor.editor.generator.isDynamicSlide() && field.connectedField && field.connectedField instanceof _N2.FormElementImage) {

        } else {
            field.focus(true);
        }
    };

    ItemManager.prototype.startItems = function () {
        var $addLayerBox = $('.n2_add_layer_box');

        for (var i = 0; i < $addLayerBox.length; i++) {
            new _N2.UICanvasItem($addLayerBox.eq(i), {
                canvasUIManager: this.fragmentEditor.canvasUIManager,
                $layer: function () {
                    return this.element.clone();
                },
                start: (function () {
                    this.fragmentEditor.addLayer.hide();
                }).bind(this)
            });
        }

        $addLayerBox.on('click', (function (e) {
            var position = e.ctrlKey || e.metaKey ? 'absolute' : this.fragmentEditor.addLayer.getPosition(),
                targetGroup;
            if (position === 'absolute') {
                targetGroup = this.fragmentEditor.mainContainer.getTargetGroupAbsolute();
            } else {
                targetGroup = this.fragmentEditor.mainContainer.getTargetGroupDefault();
            }

            this.createLayerItem(targetGroup, $(e.currentTarget).data(), 'click');

            this.fragmentEditor.addLayer.hide();
        }).bind(this));

        $('.n2_add_layer__bar_button[data-add-layer-shortcut]')
            .on({
                click: (function (e) {
                    e.preventDefault();
                    $('.n2_add_layer_box[data-item="' + $(e.currentTarget).data('add-layer-shortcut') + '"]').trigger(e);
                }).bind(this),
                mousedown: (function (e) {
                    $('.n2_add_layer_box[data-item="' + $(e.currentTarget).data('add-layer-shortcut') + '"]').trigger(e);
                }).bind(this)
            });
    };

    ItemManager.prototype.createWithDrag = function (e, doneDeferred, placement, $layer, targetContainer, targetIndex) {
        var layer;
        if (placement === 'absolute') {
            var rect = this.fragmentEditor.mainContainer.layer[0].getBoundingClientRect();

            layer = this.createLayerItem(targetContainer.layer, $layer.data(), 'click');

            layer.layer.one('LayerCreated', (function () {

                var isAutoWidth = layer.layer[0].style.width === 'auto',
                    left = e.clientX - rect.left;

                if (isAutoWidth) {
                    left = Math.min(left, layer.group.layer.width() - layer.layer.width() - 5);
                }

                layer.placement.current.setPosition(left, e.clientY - rect.top);

                doneDeferred.resolve();
            }).bind(this));
        } else {
            _N2.History.get().startBatch();

            layer = this.createLayerItem(targetContainer.layer, $layer.data(), 'click');

            _N2.History.get().addControl('skipForwardUndos');

            targetContainer.layer.container.insertLayerAt(layer, targetIndex);
            layer.changeGroup(false, targetContainer.layer);

            _N2.History.get().endBatch();

            doneDeferred.resolve();
        }
    };

    ItemManager.prototype.createLayerItem = function (group, data, interaction, props) {
        var type = data.item,
            layer;
        if (type.match(/^structure-/)) {
            layer = new _N2.Row(this.fragmentEditor, group, {});
            layer.create(type);


            layer.layer.one('LayerCreated', function () {
                layer.highlightStructure();
            });
        } else {
            var itemData = this.getItemForm(type),
                extraValues = {};
            switch (type) {
                case 'image':
                    if (group.type === 'slide') {
                        extraValues.size = '100%|*|auto';
                    } else {
                        extraValues.size = 'auto|*|auto';
                    }
                    break;
            }

            var $item = $('<div></div>').attr('data-item', type)
                .data('itemvalues', _AssignRecursive({}, itemData.values, this.getLastValues(type), extraValues))
                .addClass('n2-ss-item n2-ss-item-' + type);
            layer = this._createLayer($item, group, _Assign($('.n2_add_layer_box[data-item="' + type + '"]').data('layerproperties'), props));


            _N2.History.get().addSimple(this, this.historyDelete, this.historyCreate, [group, layer, data]);

        }

        layer.layer.one('LayerCreated', (function () {
            if (interaction === "click") {
                layer.activate(true);
            } else {
                layer.activate();
            }

            this.fragmentEditor.layerWindow.tab.displayTab('content');
        }).bind(this));

        return layer;
    };

    ItemManager.prototype.getLastValues = function (type) {
        if (this.lastValues[type] !== undefined) {
            return this.lastValues[type];
        }
        return {};
    };

    ItemManager.prototype.getItemClass = function (type) {
        var itemClass = 'Item' + _N2.StringHelper.capitalize(type);
        if (_N2[itemClass] === undefined) {
            return 'Item';
        }
        return itemClass;
    };

    ItemManager.prototype._createLayer = function ($item, group, properties) {
        var defaultAlign = this.fragmentEditor.layerOptions.layerFeatures.layerDefault;
        for (var k in defaultAlign) {
            if (defaultAlign[k] !== null) {
                properties[k] = defaultAlign[k];
            }
        }

        var newLayer = new _N2.Layer(this.fragmentEditor, group, properties);

        newLayer.create(newLayer._createLayer().append($item));

        newLayer.renderModeProperties();

        return newLayer;
    };

    /**
     * Initialize an item type and subscribe the field changes on that type.
     * We use event normalization to prevent rendering.
     * @param type
     * @private
     */
    ItemManager.prototype.getItemForm = function (type) {
        if (this.forms[type] === undefined) {
            var form = $('.n2_ss_layer_window__tab_panel[data-panel="item/' + type + '"]'),
                formData = {
                    form: form,
                    values: window['itemValues/' + type],
                    fields: form.find('[name^="item_"]'),
                    fieldNameRegexp: new RegExp('item_' + type + "\\[(.*?)\\]", "")
                };
            formData.fields.on({
                nextendChange: this.updateCurrentItem.bind(this),
                keydown: this.updateCurrentItemDeBounced.bind(this)
            });

            this.forms[type] = formData;
        }
        return this.forms[type];
    };

    /**
     * This function renders the current item with the current values of the related form field.
     */
    ItemManager.prototype.updateCurrentItem = function (e) {
        if (!this.suppressChange) {
            if (this.activeItemOriginalData === null) {
                this.activeItemOriginalData = _Assign({}, this.activeItem.values);
            }
            var data = {},
                originalData = {};
            // Get the current values of the fields
            // Run through the related item filter
            this.activeForm.fields.each((function (i, field) {
                var field = $(field),
                    name = field.attr('name').match(this.activeForm.fieldNameRegexp)[1];

                originalData[name] = data[name] = field.val();

            }).bind(this));

            if (e && e.type === 'nextendChange') {
                var task = _N2.History.get().addValue(this, this.historyUpdateCurrentItem, [this.activeItem]);
                if (task) {
                    task.setValues(this.activeItemOriginalData, _Assign({}, originalData));
                }

                this.activeItemOriginalData = null;
            }

            this.activeItem.render(_Assign({}, this.activeItem.getDefault(), data), originalData);
        }
    };

    /**
     *
     * @param {{}} values
     * @param {_N2.Item} historyActiveItem
     */
    ItemManager.prototype.historyUpdateCurrentItem = function (values, historyActiveItem) {
        /**
         * @type {_N2.Item}
         */
        var maybeOldActiveItem = historyActiveItem.getSelf();
        maybeOldActiveItem.reRender(_AssignRecursive({}, values));
        maybeOldActiveItem.values = values;
        if (this.activeItem === maybeOldActiveItem) {
            maybeOldActiveItem.activate(false, true);
        }
    };

    ItemManager.prototype.updateCurrentItemDeBounced = NextendDeBounce(function (e) {
        this.updateCurrentItem(e);
    }, 100);

    ItemManager.prototype.historyDelete = function (historyGroup, historyLayer) {
        historyLayer.getSelf().delete();
    };

    ItemManager.prototype.historyCreate = function (historyGroup, historyLayer, data) {
        var layer = this.createLayerItem(historyGroup.getSelf(), data);
        historyLayer.setSelf(layer);
    };

    ItemManager.prototype.getSelf = function () {
        return this;
    };

    return ItemManager;
});_N2.d('PluginShowOn', '$', function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.ComponentAbstract} component
     * @constructor
     */
    function PluginShowOn(component) {
        this.component = component;
    }

    PluginShowOn.prototype.addProperties = function ($layer) {
        this.component.createProperty('desktopPortrait', 1, $layer, this);
        this.component.createProperty('desktopLandscape', 1, $layer, this);
        this.component.createProperty('tabletPortrait', 1, $layer, this);
        this.component.createProperty('tabletLandscape', 1, $layer, this);
        this.component.createProperty('mobilePortrait', 1, $layer, this);
        this.component.createProperty('mobileLandscape', 1, $layer, this);
    };


    PluginShowOn.prototype._syncdesktopPortrait = function () {
        var value = this.component.getProperty('desktopPortrait');
        this.component.syncShowOnDevice('desktopPortrait', value);
    };

    PluginShowOn.prototype._synctabletPortrait = function () {
        var value = this.component.getProperty('tabletPortrait');
        this.component.syncShowOnDevice('tabletPortrait', value);
    };

    PluginShowOn.prototype._syncmobilePortrait = function () {
        var value = this.component.getProperty('mobilePortrait');
        this.component.syncShowOnDevice('mobilePortrait', value);
    };

    return PluginShowOn;
});_N2.d('Col', ['$', 'ContentAbstract'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param fragmentEditor
     * @param group
     * @param properties
     * @constructor

     * @augments {_N2.ContentAbstract}
     */
    function Col(fragmentEditor, group, properties) {
        this.label = n2_('Column');
        this.type = 'col';

        this.innerContainer = '> .n2-ss-layer-col';

        this.localStyle = [
            {
                group: 'normal', selector: '-inner', css: {}
            },
            {group: 'hover', selector: '-inner:HOVER', css: {}}
        ];

        _N2.ContentAbstract.prototype.constructor.call(this, fragmentEditor, group, properties);

        this.placement.allow('default');
    }

    Col.prototype = Object.create(_N2.ContentAbstract.prototype);
    Col.prototype.constructor = Col;

    /**
     *
     * @param {_N2.ComponentSettings} componentSettings
     * @returns {{}}
     */
    Col.getRegistryDataToFields = function (componentSettings) {

        var registry = {
            maxwidth: $('#layercol-maxwidth'),
            padding: $('#layercol-padding'),
            inneralign: $('#layercol-inneralign'),
            verticalalign: $('#layercol-verticalalign'),
            href: $('#layercol-href'),
            'href-target': $('#layercol-href-target'),
            'aria-label': $('#layercol-aria-label'),
            bgimage: $('#layercol-background-image'),
            bgimagex: $('#layercol-background-focus-x'),
            bgimagey: $('#layercol-background-focus-y'),
            stylemode: $('#layerfields-col-background-mode'),
            bgcolor: $('#layercol-background-color'),
            bgcolorgradient: $('#layercol-background-gradient'),
            bgcolorgradientend: $('#layercol-background-color-end'),
            borderradius: $('#layercol-border-radius'),
            boxshadow: $('#layercol-boxshadow'),
            borderwidth: $('#layercol-border-width'),
            borderstyle: $('#layercol-border-style'),
            bordercolor: $('#layercol-border-color'),
            opened: $('#layercol-opened'),
            colwidth: $('#layercol-colwidth'),
            order: $('#layercol-order')
        };

        componentSettings.initStyleMode('column', registry.stylemode, ['#layerfields-col-border-mode']);

        componentSettings.fragmentEditor.editor.generator.registerField(registry.href);
        componentSettings.fragmentEditor.editor.generator.registerField(registry['aria-label']);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.bgimage);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.bgcolor);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.bgcolorgradientend);

        return registry;
    };

    Col.prototype.getIcon = function () {
        return 'ssi_16 ssi_16--column';
    };

    Col.prototype._createLayer = function () {
        return $('<div class="n2-ss-layer"><div class="n2-ss-layer-col n2-ss-layer-with-background n2-ss-layer-content"></div></div>')
            .attr('data-sstype', this.type);
    };

    Col.prototype.addProperties = function ($layer) {

        _N2.ContentAbstract.prototype.addProperties.call(this, $layer);

        this.createProperty('colwidth', '1', $layer);
        this.createProperty('href', '', $layer);
        this.createProperty('href-target', '_self', $layer);
        this.createProperty('aria-label', '', $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('borderradius', 0, {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('boxshadow', '0|*|0|*|0|*|0|*|00000080', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('borderwidth', '0|*|0|*|0|*|0', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('borderstyle', 'solid', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('bordercolor', 'ffffffff', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createDeviceProperty('order', {desktopPortrait: 0}, $layer);
    };

    Col.prototype.create = function ($layer) {
        _N2.ContentAbstract.prototype.create.apply(this, arguments);

        this._syncorder();

        this._syncborder();

        this._syncborderradius();
        this._syncboxshadow();

        this._onReady();
    };

    Col.prototype.load = function ($layer, isEditorStart) {

        _N2.ContentAbstract.prototype.load.call(this, $layer, isEditorStart);

        this._syncorder();

        this._syncborder();
        this._syncborderradius();
        this._syncboxshadow();

        this._onReady();
    };

    Col.prototype.createRow = function () {
        this.$content = this.layer.find('.n2-ss-layer-content:first');

        this.container = new _N2.LayerContainer(this);
        this.container.setLayerContainerElement(this.$content);

        this.addClassElement(this.layer);
        this.addClassElement(this.$content, '-inner');

        this._createLayerListRow();

        var $buttonOpenClose = $('<div class="n2_layer_navigation_list_layer__title_label_folder" data-action="open-close"><i class="ssi_16"></i></div>')
            .prependTo(this.$rowLabel)
            .on('click', this.switchOpened.bind(this));

        _N2._tooltip.addElement($buttonOpenClose, n2_('Open/Close'));

        this.container.$ul.appendTo(this.layerRow);

        this.readyDeferred.done(this._syncopened.bind(this));
    };

    Col.prototype.getPlacement = function () {
        return 'default';
    };

    Col.prototype._start = function (isCreate) {

        _N2.ContentAbstract.prototype._start.call(this, isCreate);

        if (isCreate) {

            this.layer.addClass('n2_layer_col_highlight');
            setTimeout((function () {
                if (!this.isDeleted) {
                    this.layer.removeClass('n2_layer_col_highlight');
                }
            }).bind(this), 2000);
        }
    };

    Col.prototype.getRealOrder = function () {
        var order = this.getProperty('order');
        if (order == 0) {
            return 10;
        }
        return order;
    };

    Col.prototype._syncorder = function () {
        var order = this.getProperty('order');

        if (order == 0) {
            this.layer.css('order', '');
        } else {
            this.layer.css('order', String(order * 2));
        }

        this.group.refreshUI();
    };

    Col.prototype._synccolwidth = function () {

        this.widthPercentage = ((new Fraction(this.getProperty('colwidth'))).valueOf() * 100);
    };

    Col.prototype.getWidthPercentage = function () {

        return this.widthPercentage;
    };

    Col.prototype.setTemporaryWidth = function () {

    };

    Col.prototype._synchref =
        Col.prototype['_synchref-target'] =
            Col.prototype['_syncaria-label'] = function () {
            };

    Col.prototype._syncborderradius =
        Col.prototype['_syncborderradius-hover'] = function () {
            var borderRadius = this.getProperty('borderradius');
            if (borderRadius > 0) {
                this.addLocalStyle('normal', 'borderradius', 'border-radius:' + borderRadius + 'px;');
            } else {
                this.addLocalStyle('normal', 'borderradius', '');
            }

            var borderRadiusHover = this.getProperty('borderradius-hover');
            if (borderRadiusHover && borderRadiusHover != borderRadius) {
                this.addLocalStyle('hover', 'borderradius', 'border-radius:' + borderRadiusHover + 'px;');
            } else {
                this.addLocalStyle('hover', 'borderradius', '');
            }
        };

    Col.prototype._syncborderwidth =
        Col.prototype._syncbordercolor =
            Col.prototype._syncborderstyle =
                Col.prototype['_syncborderstyle-hover'] =
                    Col.prototype['_syncbordercolor-hover'] =
                        Col.prototype['_syncborderwidth-hover'] = function () {
                            this._syncborder();
                        };

    Col.prototype._syncborder = function () {
        var borderstyle = this.getProperty('borderstyle'),
            bordercolor = this.getProperty('bordercolor'),
            borderwidth = this.getProperty('borderwidth');

        this.addLocalStyle('normal', 'border', this.getBorderCSS(borderstyle, bordercolor, borderwidth));

        var hoverStyle = '',
            isHoverDifferent = false,
            borderstyleHover = this.getProperty('borderstyle-hover'),
            bordercolorHover = this.getProperty('bordercolor-hover'),
            borderwidthHover = this.getProperty('borderwidth-hover');

        if (borderstyleHover !== undefined && borderstyleHover != borderstyle) {
            isHoverDifferent = true;
        }
        if (bordercolorHover !== undefined && bordercolorHover != bordercolor) {
            isHoverDifferent = true;
        }
        if (borderwidthHover !== undefined && borderwidthHover != borderwidth) {
            isHoverDifferent = true;
        }

        if (isHoverDifferent) {
            if (borderstyleHover === undefined) {
                borderstyleHover = borderstyle;
            }
            if (bordercolorHover === undefined) {
                bordercolorHover = bordercolor;
            }
            if (borderwidthHover === undefined) {
                borderwidthHover = borderwidth;
            }
            hoverStyle = this.getBorderCSS(borderstyleHover, bordercolorHover, borderwidthHover);
        }
        this.addLocalStyle('hover', 'border', hoverStyle);

        this.update();
    };

    Col.prototype.getBorderCSS = function (borderStyle, borderColor, borderWidth) {
        var style = '';
        if (borderStyle !== 'none') {
            style += 'border-color:' + N2Color.hex2rgbaCSS(borderColor) + ';';
            style += 'border-style:' + borderStyle + ';';
            borderWidth = borderWidth.split('|*|');
            var unit = 'px';

            style += 'border-width:' + borderWidth.join(unit + ' ') + unit + ';';
        }
        return style;
    };

    Col.prototype._syncboxshadow =
        Col.prototype['_syncboxshadow-hover'] = function () {

            var boxShadow = this.getProperty('boxshadow');
            this.addLocalStyle('normal', 'boxshadow', this.getBoxShadowCSS(boxShadow.split('|*|')));

            var hoverStyle = '',
                boxShadowHover = this.getProperty('boxshadow-hover');
            if (boxShadowHover !== undefined && boxShadowHover != boxShadow) {
                hoverStyle = this.getBoxShadowCSS(boxShadowHover.split('|*|'));
            }
            this.addLocalStyle('hover', 'boxshadow', hoverStyle);
        };

    Col.prototype.getBoxShadowCSS = function (boxShadow) {
        if ((boxShadow[0] != 0 || boxShadow[1] != 0 || boxShadow[2] != 0 || boxShadow[3] != 0) && N2Color.hex2alpha(boxShadow[4]) != 0) {
            return 'box-shadow:' + boxShadow[0] + 'px ' + boxShadow[1] + 'px ' + boxShadow[2] + 'px ' + boxShadow[3] + 'px ' + N2Color.hex2rgbaCSS(boxShadow[4]) + ';';
        }
        return '';
    };

    Col.prototype.delete = function () {
        if (this.group.container.getLayerCount('col') > 1) {
            this._delete();
        } else {
            this.group.delete();
        }
    };

    Col.prototype.renderModeProperties = function (isReset) {
        this._syncorder();

        _N2.ContentAbstract.prototype.renderModeProperties.call(this, isReset);
    };


    Col.prototype.update = function () {

        this.group._syncwrapafter();
        _N2.ContentAbstract.prototype.update.call(this);
    };


    Col.prototype.setPropertystylemode = function () {
        _N2.ContentAbstract.prototype.setPropertystylemode.apply(this, arguments);

        this.syncAdvancedField('borderradius');
        this.syncAdvancedField('boxshadow');
        this.syncAdvancedField('borderwidth');
        this.syncAdvancedField('borderstyle');
        this.syncAdvancedField('bordercolor');
    };

    Col.prototype.getOrderedIndex = function () {
        return this.group.getOrderedColumns().indexOf(this);
    };

    /**
     * @returns {[]}
     */
    Col.prototype.getContextMenuItems = function () {
        var items = _N2.ComponentCommonAbstract.prototype.getContextMenuItems.call(this);

        items.push({
            priority: 55,
            label: n2_('Duplicate'),
            icon: 'ssi_16 ssi_16--duplicate',
            action: this.duplicate.bind(this)
        });

        items.push({
            priority: 1000,
            label: n2_('Delete'),
            icon: 'ssi_16 ssi_16--delete',
            action: this.delete.bind(this),
            color: 'red'
        });

        return items;
    };

    return Col;
});var dependencies = ['$', 'Historical', 'LayerDataStorage'];
_N2.d('ComponentAbstract', dependencies, function () {
    var $ = _N2.$;
    "use strict";

    var counter = 0;
    window.layers = [];

    /**
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @param group
     * @param properties
     * @constructor
     * @augments _N2.Historical
     * @augments _N2.LayerDataStorage
     */
    function ComponentAbstract(fragmentEditor, group, properties) {

        this.supports = this.supports || [];
        this.supports.push('PluginShowOn');

        this.isActive = false;
        this.isAnimatable = false;

        /**
         * @type {_N2.LayerContainer}
         */
        this.container = undefined;

        this.plugins = [];

        for (var i = 0; i < this.supports.length; i++) {
            this.plugins.push(new _N2[this.supports[i]](this));
        }

        this.isRenameAllowed = this.isRenameAllowed === undefined ? false : this.isRenameAllowed;

        this.contextualBarPosition = this.contextualBarPosition || 'left';

        this.editorUI = {};

        this.showsOnCurrent = true;

        this.counter = ++counter;
        window.layers[this.counter] = this;
        this.self = this;
        this.originalProperties = properties || {};
        _N2.LayerDataStorage.prototype.constructor.call(this);
        this.readyDeferred = $.Deferred();
        this.isDeleteStarted = false;
        this.isDeleted = false;

        this.$ = $(this);

        this.proxyRefreshTextAlign = this.refreshTextAlign.bind(this);

        this.status = ComponentAbstract.STATUS.UNDEFINED;

        /**
         * @type {_N2.FragmentEditor}
         */
        this.fragmentEditor = fragmentEditor;

        /**
         * @type {_N2.ComponentAbstract}
         */
        this.group = group;

        this.classElements = [];

        this.localStyleSyncThrottled = NextendThrottle(this.localStyleSync, 50);

        /** @type {_N2.Placement} */
        this.placement = new _N2.Placement(this);

    }

    ComponentAbstract.STATUS = {
        UNDEFINED: 0,
        NORMAL: 1,
        LOCKED: 2,
        HIDDEN: 3
    };

    ComponentAbstract.STATUS_INV = {
        0: 'UNDEFINED',
        1: 'NORMAL',
        2: 'LOCKED',
        3: 'HIDDEN'
    };

    ComponentAbstract.selfAlignToStyle = function ($element, selfAlign) {

        var style = $element[0].style;

        if (selfAlign === 'left') {
            style.setProperty('align-self', 'var(--ss-fs)');
        } else if (selfAlign === 'center') {
            style.setProperty('align-self', selfAlign);
        } else if (selfAlign === 'right') {
            style.setProperty('align-self', 'var(--ss-fe)');
        } else {
            style.removeProperty('align-self');
        }
    }

    ComponentAbstract.innerAlignToStyle = function ($element, innerAlign) {
        var style = $element[0].style;
        if (innerAlign === 'left') {
            style.textAlign = innerAlign;
            style.setProperty('--ssselfalign', 'var(--ss-fs)');
        } else if (innerAlign === 'center') {
            style.textAlign = innerAlign;
            style.setProperty('--ssselfalign', innerAlign);
        } else if (innerAlign === 'right') {
            style.textAlign = innerAlign;
            style.setProperty('--ssselfalign', 'var(--ss-fe)');
        } else {
            style.removeProperty('text-align');
            style.removeProperty('--ssselfalign');
        }
    }

    ComponentAbstract.prototype = Object.create(_N2.LayerDataStorage.prototype);
    ComponentAbstract.prototype.constructor = ComponentAbstract;

    _N2.Historical(ComponentAbstract);

    ComponentAbstract.prototype.onSelfChange = function () {
    };

    ComponentAbstract.prototype.getUILabel = function () {
        return this.label;
    };

    ComponentAbstract.prototype.hasUp = function () {
        return false;
    };

    ComponentAbstract.prototype.up = function (e) {
        e.stopImmediatePropagation();
    };

    ComponentAbstract.prototype.addProperties = function ($layer) {

        this.createDeviceProperty('fontsize', {desktopPortrait: 100}, $layer);

        this.callPlugin('addProperties', $layer);
    };

    ComponentAbstract.prototype.callPlugin = function (method) {

        var args = Array.prototype.slice.call(arguments, 1);
        for (var i = 0; i < this.plugins.length; i++) {
            if (this.plugins[i][method] !== undefined) {
                this.plugins[i][method].apply(this.plugins[i], args);
            }
        }
    };

    ComponentAbstract.prototype.getSubType = function () {
        return '';
    };

    ComponentAbstract.prototype._hide = function () {
        this.layer.css('display', 'none');
        this.showsOnCurrent = false;
        this.update();
    };

    ComponentAbstract.prototype._show = function () {
        if (parseInt(this.property[this.fragmentEditor.getMode()])) {
            this.layer.css('display', '');
            this.showsOnCurrent = true;
        }
        this.update();
    };

    ComponentAbstract.prototype.getRootElement = function () {
        return this.layer;
    };

    ComponentAbstract.prototype.create = function ($layer) {

        this.addProperties(false);

        this.layer = $layer;

        this.layer.addClass('n2_layer--creation-in-progress');

        this.layer.data('layerObject', this);
        this.layer.triggerHandler('layerStarted', [this]);


        this.group.container.insert(this);
        this.group.onChildCountChange();

        this.onBeforeStart();
        this.$.triggerHandler('create');

        this._start(true);
    };

    ComponentAbstract.prototype.load = function ($layer, isEditorStart) {

        this.addProperties($layer);

        this.layer = $layer.data('layerObject', this);

        this.layer.triggerHandler('layerStarted', [this]);

        this.onBeforeStart();
        this.$.triggerHandler('load');

        this._start(false);

        var status = $layer.data('status');
        if (status !== null && status !== undefined) {
            this.changeStatus(status);
        } else {
            this.changeStatus(ComponentAbstract.STATUS.NORMAL);
        }

        if (!isEditorStart) {
            this.regenerateUniqueClass();
        }

        var uniqueClass = this.getProperty('uniqueclass');
        if (uniqueClass) {
            this.fragmentEditor.removeRules(uniqueClass)
        }
    };

    ComponentAbstract.prototype.onBeforeStart = function () {

    };

    /**
     * @param isCreate
     * @protected
     * @abstract
     */
    ComponentAbstract.prototype._start = function (isCreate) {

    };

    ComponentAbstract.prototype.afterStart = function (isCreate) {

        if (isCreate) {

            _N2.ImagesLoaded(this.layer[0])
                .finally((function () {
                    if (!this.isDeleteStarted) {

                        this.placement.startResize();

                        this.layer.css('visibility', '');

                        this.layer.triggerHandler('LayerCreated');

                        this.layer.removeClass('n2_layer--creation-in-progress');

                        document.dispatchEvent(new CustomEvent("EditorInvalidated"));

                    }
                }).bind(this));
        } else {

            this.placement.startResize();

            this.layer.css('visibility', '');
        }
    };

    ComponentAbstract.prototype.updateAnimations = function () {

    };

    ComponentAbstract.prototype.createLayerAnimations = function (horizontalRatio, verticalRatio) {
        var animations = [];
        if (this.container) {
            var children = this.container.getSortedLayers();
            for (var i = 0; i < children.length; i++) {
                animations.push.apply(animations, children[i].createLayerAnimations(horizontalRatio, verticalRatio));
            }
        }
        return animations;
    };

    ComponentAbstract.prototype._onReady = function () {

        this.originalProperties = {};

        this.readyDeferred.resolve();
    };

    ComponentAbstract.prototype.isReady = function () {
        return this.readyDeferred.state() === 'resolved';
    };

    ComponentAbstract.prototype.getLabel = function () {
        return this.label;
    };

    ComponentAbstract.prototype.getName = function () {
        return this.getDefaultName();
    };

    ComponentAbstract.prototype.getIcon = function () {
        return 'ssi_16 ssi_16--layer';
    };

    ComponentAbstract.prototype.getDefaultName = function () {
        return this.label;
    };

    ComponentAbstract.prototype.setGroup = function (group) {
        this.group.$.off('textAlignUpdated.sslayer' + this.counter);

        this.group = group;
        group.container.syncLayerRow(this);

        this.group.$.on('textAlignUpdated.sslayer' + this.counter, this.proxyRefreshTextAlign);
    };

    ComponentAbstract.prototype.changeGroup = function (originalIndex, newGroup) {
        var originalGroup = this.group;
        originalGroup.$.off('textAlignUpdated.sslayer' + this.counter);

        this.group = newGroup;
        newGroup.container.syncLayerRow(this);

        newGroup.$.on('textAlignUpdated.sslayer' + this.counter, this.proxyRefreshTextAlign);

        this.userGroupChange(originalGroup, originalIndex, newGroup, this.getIndex());

        originalGroup.update();
    };

    ComponentAbstract.prototype.userGroupChange = function (originalGroup, originalIndex, newGroup, newIndex) {
        if (originalGroup === newGroup) {
            this.userIndexChange(originalIndex, newIndex);
        } else {
            var task = _N2.History.get().addValue(this, this.historyUserGroupChange, []);

            if (task) {
                task.setValues({
                    historyGroup: originalGroup,
                    index: originalIndex
                }, {
                    historyGroup: newGroup,
                    index: newIndex
                });
            }
        }
    };

    ComponentAbstract.prototype.historyUserGroupChange = function (data) {
        var originalGroup = this.group,
            group = data.historyGroup.getSelf(),
            index = data.index;
        group.container.insertLayerAt(this, index);

        this.group.$.off('refreshTextAlign.sslayer' + this.counter);

        this.group = group;

        group.container.syncLayerRow(this);

        group.onChildCountChange();

        originalGroup.update();
    };

    ComponentAbstract.prototype.userIndexChange = function (originalIndex, newIndex) {

        var task = _N2.History.get().addValue(this, this.historyUserIndexChange);
        if (task) {
            task.setValues(originalIndex, newIndex);
        }
        this.group.container.insertLayerAt(this, newIndex);
    };

    ComponentAbstract.prototype.historyUserIndexChange = function (value) {
        this.group.container.insertLayerAt(this, value);
    };

    ComponentAbstract.prototype.getLayerListParent = function () {
        return this.group.container.$ul;
    };

    ComponentAbstract.prototype._createLayerListRow = function (actions) {
        this.layerRow = $('<div class="n2_layer_navigation_list_layer"></div>')
            .attr('data-type', this.type)
            .data('layer', this)
            .appendTo(this.getLayerListParent());

        this.$rowLabel = $('<div class="n2_layer_navigation_list_layer__title_label"></div>');

        this.layerTitleSpan = $('<div class="n2_layer_navigation_list_layer__title_label_text">' + this.getName() + '</div>')
            .appendTo(this.$rowLabel);

        this.layerTitle = $('<div class="n2_layer_navigation_list_layer__title"></div>')
            .append(this.$rowLabel)
            .appendTo(this.layerRow);

        this.$layerRowActions = $('<div class="n2_layer_navigation_list_layer__title_actions"></div>')
            .appendTo(this.layerTitle);

        if (actions !== undefined) {
            this.$layerRowActions.append(actions)
        }

        this.$layerRowMore = $('<div class="n2_layer_navigation_list_layer__title_action"><i class="ssi_16 ssi_16--more"></i></div>')
            .on('click', (function () {
                _N2.WindowManager.actionPrevent('ComponentRowActivate');

                this.onContextMenu(this.$layerRowMore[0], {
                    y: 6
                });
            }).bind(this))
            .appendTo(this.$layerRowActions);

        _N2._tooltip.add(this.layerRow);

        new _N2.UILayerListItem(this.layerRow, {
            UIManager: this.fragmentEditor.mainContainer.layerListUIManager,
            layer: this,
            $item: this.layerRow
        });

        this.$layerRowIcon = $('<div class="n2_layer_navigation_list_layer__title_label_icon"><i class="' + this.getIcon() + '"></i></div>')
            .prependTo(this.$rowLabel)
    };

    ComponentAbstract.prototype.update = function () {
        this.readyDeferred.done(this.placement.updatePosition.bind(this.placement));
    };

    ComponentAbstract.prototype.getHTML = function (base64) {
        var $layer = this._createLayer();

        $layer.attr('data-pm', this.placement.getType());

        for (var k in this.property) {
            if (k != 'width' && k != 'height' && k != 'left' && k != 'top') {
                $layer.attr('data-' + k.toLowerCase(), this.property[k]);
            }
        }

        for (var k in this.deviceProperty) {
            for (var k2 in this.deviceProperty[k]) {
                $layer.attr('data-' + k.toLowerCase() + k2, this.deviceProperty[k][k2]);
            }
        }

        for (var k in this.deviceProperty['desktop']) {
            $layer.css(k, this.deviceProperty['desktop'][k] + 'px');
        }

        if (this.container !== undefined) {
            var $innerContainer = $layer;
            if (this.innerContainer !== undefined) {
                $innerContainer = $layer.find(this.innerContainer);
            }

            $innerContainer.append(this.container.getHTML(base64));
        }

        var id = this.getProperty('id');
        if (id !== '') {
            $layer.attr('id', id);
        }

        if (this.status > _N2.ComponentAbstract.STATUS.NORMAL) {
            $layer.attr('data-status', this.status);
        }

        return $layer;
    };

    ComponentAbstract.prototype.duplicate = function (needActivate) {
        var $component = this.getHTML(false);

        /**
         * If we duplicate a container element, we must generate new unique ids for children components.
         * @see SSDEV-3189
         */
        const idTranslationTable = {};

        var id = $component.attr('id');
        if (id) {
            const newID = $.fn.uid();
            $component.attr('id', newID);
            $component.attr('data-id', newID);

            idTranslationTable[id] = newID;
        }

        const childrenWithIDs = $component.find('[data-id]');
        for (let i = 0; i < childrenWithIDs.length; i++) {
            let $childComponent = childrenWithIDs.eq(i);
            id = $childComponent.attr('id');
            if (id) {
                const newID = $.fn.uid();
                $childComponent.attr('id', newID);
                $childComponent.attr('data-id', newID);

                idTranslationTable[id] = newID;
            }
        }

        const childrenWithParentIDs = $component.find('[data-parentid]');
        for (let i = 0; i < childrenWithParentIDs.length; i++) {
            let $childComponent = childrenWithParentIDs.eq(i),
                newID = idTranslationTable[$childComponent.attr('data-parentid')];

            if (newID) {
                $childComponent.attr('data-parentid', newID);
            }
        }


        if (this.placement.getType() === 'absolute') {
            if ($component.attr('data-parentid')) {
                $component.data('desktopportraittop', 0);
                $component.data('desktopportraitleft', 0);
            } else {
                $component.data('desktopportraittop', $component.data('desktopportraittop') + 40);
                $component.data('desktopportraitleft', $component.data('desktopportraitleft') + 40);
            }
            $component.attr('data-parentid', '');
        }

        var newComponent = this.fragmentEditor.insertComponentWithNode(this.group, $component, this.getIndex() + 1, false, true);

        this.layerRow.trigger('mouseleave');

        if (needActivate) {
            newComponent.activate();
        }

        _N2.History.get().addSimple(this, this.historyDeleteDuplicated, this.historyDuplicate, [newComponent, newComponent.container ? newComponent.container.getAllLayers() : false]);

        return newComponent;
    };

    ComponentAbstract.prototype.historyDeleteDuplicated = function (historicalNewComponent) {
        historicalNewComponent.getSelf().delete();
    };

    ComponentAbstract.prototype.historyDuplicate = function (historicalNewComponent, historicalAllLayers) {
        var newComponent = this.duplicate(false);
        historicalNewComponent.setSelf(newComponent);

        if (historicalAllLayers) {
            var newAllLayers = newComponent.container.getAllLayers();
            for (var i = 0; i < newAllLayers.length; i++) {
                historicalAllLayers[i].setSelf(newAllLayers[i]);
            }
        }
    };

    ComponentAbstract.prototype.historyDelete = function () {
        this.delete();
    };

    ComponentAbstract.prototype.historyRestore = function ($component, historicalGroup, index, historicalAllLayers) {
        var newComponent = this.fragmentEditor.insertComponentWithNode(this.group.getSelf(), $component.clone(), index, false, true);
        this.setSelf(newComponent);

        if (historicalAllLayers) {
            var newAllLayers = newComponent.container.getAllLayers();
            for (var i = 0; i < newAllLayers.length; i++) {
                historicalAllLayers[i].setSelf(newAllLayers[i]);
            }
        }
    };

    ComponentAbstract.prototype.delete = function () {
        _N2.PositionDisplay.get().hide();
        _N2._tooltip.onLeave();
        this._delete();
    };

    ComponentAbstract.prototype._delete = function () {

        this.isDeleteStarted = true;

        if (this.fragmentEditor.getActiveLayer() === this) {
            _N2.WindowManager.actionPrevent('ComponentRowActivate');
            this.fragmentEditor.mainContainer.activate();
        }

        _N2.History.get().startBatch();
        _N2.History.get().addSimple(this, this.historyRestore, this.historyDelete, [this.getHTML(false), this.group, this.getIndex(), this.container ? this.container.getAllLayers() : false]);

        this.deActivate();

        if (this.container !== undefined) {
            _N2.History.get().off();
            var layers = this.container.getSortedLayers();
            for (var i = 0; i < layers.length; i++) {
                layers[i]._delete();
            }
            _N2.History.get().on();
        }
        _N2.History.get().endBatch();

        if (this.item !== undefined) {
            this.item.delete();
        }

        this.placement.delete();

        // If delete happen meanwhile layer dragged or resized, we have to cancel that.
        this.layer.trigger('mouseup');

        this.isDeleted = true;

        this.fragmentEditor.mainContainer.layerDeleted(this);

        this.layer.triggerHandler('LayerDeleted');
        this.getRootElement().remove();
        this.layerRow.remove();

        if (!this.group.isDeleteStarted) {
            this.group.update();

            this.group.$.off('refreshTextAlign.sslayer' + this.counter);
        }

        this.$.trigger('layerDeleted');

        //delete this.fragmentEditor;
        delete this.layer;
        delete this.itemEditor;
        this.group.onChildCountChange();
    };

    ComponentAbstract.prototype.getData = function (params) {
        var data = {
            type: this.type,
            pm: this.placement.getType()
        };

        if (this.status > _N2.ComponentAbstract.STATUS.NORMAL) {
            data.status = this.status;
        }

        var properties = _Assign({}, this.property);

        // store the device based properties
        for (var device in this.deviceProperty) {
            for (var property in this.deviceProperty[device]) {
                delete properties[property];
                var value = this.deviceProperty[device][property];
                if (value === undefined) {
                    continue;
                }

                switch (property) {
                    case 'width':
                    case 'height':
                        if (!this.isDimensionPropertyAccepted(value)) {
                            value = parseFloat(value);
                        }
                        break;
                    case 'fontsize':
                    case 'left':
                    case 'top':
                    case 'gutter':
                    case 'wrap':
                        value = parseFloat(value);
                        break;
                }
                data[device.toLowerCase() + property] = value;
            }
        }

        for (var k in properties) {
            data[k.toLowerCase()] = properties[k];
        }

        return data;
    };

    ComponentAbstract.prototype.onChildCountChange = function () {

    };

    /**
     *
     * @param {array} layersData Contains
     * @param {array} layers Contains layer objects to be able to track layers in the current copy process to prevent same layer inserted into the clipboard twice when parent picker used.
     * @returns array layersData
     */
    ComponentAbstract.prototype.getDataWithChildren = function (layersData, layers) {
        if (layers.indexOf(this) === -1) {
            layers.push(this);
            layersData.push(this.getData({
                layersIncluded: true,
                itemsIncluded: true
            }));
            this.layer.triggerHandler('LayerGetDataWithChildren', [layersData, layers]);
        }
        return layersData;
    };

    ComponentAbstract.prototype.markOver = function () {
        this.layer.addClass('n2-ss-mouse-over');

        this.group && this.group.markEnter();
    };

    ComponentAbstract.prototype.markOut = function () {
        this.layer.removeClass('n2-ss-mouse-over');

        this.group && this.group.markLeave();
    };

    ComponentAbstract.prototype.markEnter = function (e) {
        this.layer.addClass('n2-ss-mouse-hover');
        this.group.markEnter();
    };

    ComponentAbstract.prototype.markLeave = function (e) {
        this.layer.removeClass('n2-ss-mouse-hover');
        this.group.markLeave();
    };

    ComponentAbstract.prototype.sync = function () {
        this._syncid();
        if (this.container) {
            var layers = this.container.getSortedLayers();
            for (var i = 0; i < layers.length; i++) {
                layers[i].sync();
            }
        }
        this.placement.sync();
    };

    ComponentAbstract.prototype._syncid = function () {
        var value = this.getProperty('id');
        if (value === '') {
            this.layer.removeAttr('id');
        } else {
            this.layer.attr('id', value);
        }
    };

    ComponentAbstract.prototype.requestID = function () {
        var id = this.getProperty('id');
        if (id === '') {
            id = $.fn.uid();
            this.setProperty('id', id, 'layer');
        }
        return id;
    };

    ComponentAbstract.prototype.requestUniqueClass = function () {
        var uniqueClass = this.getProperty('uniqueclass');
        if (!uniqueClass) {
            uniqueClass = $.fn.generateUniqueClass('n-uc-');
            _N2.History.get().off();
            this.setProperty('uniqueclass', uniqueClass, 'layer');
            _N2.History.get().on();
        }
        return uniqueClass;
    };

    /**
     * Used when duplicate or paste node to prevent class name conflicts
     */
    ComponentAbstract.prototype.regenerateUniqueClass = function () {
        if (this.getProperty('uniqueclass')) {
            this.setProperty('uniqueclass', $.fn.generateUniqueClass('n-uc-'), 'layer');
        }
    };

    ComponentAbstract.prototype._syncuniqueclass = function () {
        var value = this.getProperty('uniqueclass');

        for (var i = 0; i < this.classElements.length; i++) {
            this.classElements[i].$el
                .removeClass(function (index, className) {
                    return (className.match(/n-uc-[a-z0-9\-]+/gi) || []).join(' ');
                })
                .addClass(value + this.classElements[i].postfix);
        }
    };

    ComponentAbstract.prototype.adjustFontSize = function (shouldUpdatePosition) {

        _NodeSetStyle(this.layer[0], '--ssfont-scale', this.getFontSize() / 100);

        if (shouldUpdatePosition) {
            this.update();
        }
    };

    ComponentAbstract.prototype.getFontSize = function () {
        return parseInt(this.getProperty('fontsize'));
    };

    ComponentAbstract.prototype._syncfontsize = function () {
        this.adjustFontSize(true);
    };

    ComponentAbstract.prototype.refreshTextAlign = function () {

        this.$.triggerHandler('textAlignUpdated');
    };

    ComponentAbstract.prototype.setPropertydesktopPortrait =
        ComponentAbstract.prototype.setPropertydesktopLandscape =
            ComponentAbstract.prototype.setPropertytabletPortrait =
                ComponentAbstract.prototype.setPropertytabletLandscape =
                    ComponentAbstract.prototype.setPropertymobilePortrait =
                        ComponentAbstract.prototype.setPropertymobileLandscape = function (name, value, from) {
                            this._setProperty(name, parseInt(value), from);
                        };

    ComponentAbstract.prototype.formSetdesktopPortrait = function (options, value) {
        options.currentForm.desktopPortrait.data('field').insideChange(value);
    };

    ComponentAbstract.prototype.formSettabletPortrait = function (options, value) {
        options.currentForm.tabletPortrait.data('field').insideChange(value);
    };

    ComponentAbstract.prototype.formSetmobilePortrait = function (options, value) {
        options.currentForm.mobilePortrait.data('field').insideChange(value);
    };

    ComponentAbstract.prototype.getContents = function () {
        return false;
    };

    ComponentAbstract.prototype.renderModeProperties = function (isReset) {

        _N2.LayerDataStorage.prototype.renderModeProperties.call(this);


        this.placement.renderModeProperties(isReset);
    };

    ComponentAbstract.prototype.getIndex = function () {
        return this.group.container.getLayerIndex(this.layer);
    };

    ComponentAbstract.prototype.toString = function () {
        return this.type + ' #' + this.counter;
    };

    ComponentAbstract.prototype.historyStoreOnPlacement = function () {
        var args = Array.prototype.slice.call(arguments);
        args.splice(1, 1);
        this.placement.current[arguments[1]].apply(this.placement.current, args);
    };

    ComponentAbstract.prototype.getDroppable = function () {
        return false;
    };

    ComponentAbstract.prototype.onCanvasUpdate = function (originalIndex, targetGroup, newIndex) {

        if (this.group === targetGroup) {

            if (originalIndex !== newIndex) {
                this.userIndexChange(originalIndex, newIndex)
            }
        } else {
            var oldAbsoluteParent;
            if (this.fragmentEditor.isCol(this.group)) {
                oldAbsoluteParent = this;
                while (oldAbsoluteParent && (!oldAbsoluteParent.placement || oldAbsoluteParent.placement.getType() !== 'absolute')) {
                    oldAbsoluteParent = oldAbsoluteParent.group;
                }
            }
            this.changeGroup(originalIndex, targetGroup);

            targetGroup.onChildCountChange();

            // Find the the first absolute element from the layer parents
            var absoluteParent = this;
            while (absoluteParent && (!absoluteParent.placement || absoluteParent.placement.getType() !== 'absolute')) {
                absoluteParent = absoluteParent.group;
            }

            // Update the closest absolute parent's position as the content changed
            if (oldAbsoluteParent && oldAbsoluteParent !== absoluteParent) {
                oldAbsoluteParent.placement.updatePosition();
            }
            if (absoluteParent) {
                absoluteParent.placement.updatePosition();
            }
        }
    };

    ComponentAbstract.prototype.toggleHideShow = function () {
        _N2.WindowManager.actionPrevent('ComponentRowActivate');
        if (this.status === _N2.ComponentAbstract.STATUS.HIDDEN) {
            this.setStatusNormal();
        } else {
            this.changeStatus(_N2.ComponentAbstract.STATUS.HIDDEN);
        }
    };

    ComponentAbstract.prototype.setStatusNormal = function () {
        this.changeStatus(ComponentAbstract.STATUS.NORMAL);
    };

    ComponentAbstract.prototype.changeStatus = function (status) {
        var oldStatus = this.status;

        if (status === this.status) {
            status = ComponentAbstract.STATUS.NORMAL;
        }

        switch (this.status) {
            case ComponentAbstract.STATUS.HIDDEN:
                this.getRootElement().removeAttr('data-visibility');
                this.layerTitle.removeClass('n2_layer_navigation_list_layer__title--hidden');
                break;
        }

        this.status = status;

        switch (this.status) {
            case ComponentAbstract.STATUS.HIDDEN:
                this.getRootElement().attr('data-visibility', 'hidden');
                this.layerTitle.addClass('n2_layer_navigation_list_layer__title--hidden');
                break;
        }

        this.placement.current.changeStatus(oldStatus, this.status);

    };

    ComponentAbstract.prototype.moveX = function (x) {
        if (this.placement.getType() === 'absolute') {
            this.placement.current.moveX(x);
            this.fragmentEditor.pointerManager.refreshPosition();
        }
    };

    ComponentAbstract.prototype.moveY = function (y) {
        if (this.placement.getType() === 'absolute') {
            this.placement.current.moveY(y);
            this.fragmentEditor.pointerManager.refreshPosition();
        }
    };

    ComponentAbstract.prototype.localStyleSync = function () {
        if (this.localStyle !== undefined) {
            var rulesToDelete = [],
                css = '';
            if (this.$localStyle !== undefined) {
                this.$localStyle.remove();
                delete this.$localStyle;
            }

            for (var i = 0; i < this.localStyle.length; i++) {

                var rule = '@rule' + this.localStyle[i].selector,
                    style = '';
                rulesToDelete.push(rule);

                if (Object.keys(this.localStyle[i].css).length === 1 && this.localStyle[i].css.transition !== undefined) {
                    continue;
                }
                for (var k in this.localStyle[i].css) {
                    style += this.localStyle[i].css[k];
                }
                if (style !== '') {
                    css += rule + '{' + style + '}';
                }
            }

            var className = this.getProperty('uniqueclass');
            if (className) {
                // We have to remove all previous rules before adding new ones.
                for (var i = 0; i < rulesToDelete.length; i++) {
                    _N2._css.deleteRule(rulesToDelete[i].replace(/@rule/g, _N2._pre + '.' + className));
                }
            }
            if (css !== '') {
                if (!className) {
                    className = this.requestUniqueClass();
                }
                var styleEl = document.createElement('style');
                styleEl.appendChild(document.createTextNode(css.replace(/@rule/g, _N2._pre + '.' + className)));
                document.head.appendChild(styleEl);

                this.$localStyle = $(styleEl);
            }
        }
    };

    ComponentAbstract.prototype.addLocalStyle = function (group, name, style) {
        for (var i = 0; i < this.localStyle.length; i++) {
            if (this.localStyle[i].group === group) {
                if (style === '') {
                    if (this.localStyle[i].css[name] !== undefined) {
                        delete this.localStyle[i].css[name];
                    }
                } else {
                    this.localStyle[i].css[name] = style;
                }
                this.localStyleSyncThrottled();
                break;
            }
        }
    };

    ComponentAbstract.prototype.addClassElement = function ($el, postfix) {
        if (arguments.length < 2) postfix = '';
        this.classElements.push({
            $el: $el,
            postfix: postfix
        });
    };

    ComponentAbstract.prototype.setState = function (name, value) {
    };

    ComponentAbstract.prototype.activate = function (showLayerWindow, focusFirstItemField) {
        if (showLayerWindow) {
            this.fragmentEditor.layerWindow.show(this);
        } else {
            this.fragmentEditor.layerWindow.setTitle(this);
        }

        // Set the layer active if it is not active currently
        if (this.fragmentEditor.getActiveLayer() !== this) {
            this.layerRow.addClass('n2_layer_navigation_list_layer--active');
            this.layer.addClass('n2-active');
            this.layer.triggerHandler('n2-ss-activate');
            this.fragmentEditor.changeActiveLayer(this);


            this.fragmentEditor.layerNavigation.onActivateLayer(this);
        }
        this.isActive = true;
    };

    ComponentAbstract.prototype.deActivate = function () {
        this.isActive = false;
        if (this.layer === undefined) {
            console.error();
        }
        this.layer.removeClass('n2-active');
        this.layerRow.removeClass('n2_layer_navigation_list_layer--active');
        this.layer.triggerHandler('n2-ss-deactivate');
    };

    ComponentAbstract.prototype.isAbsolute = function () {
        return this.placement.getType() === 'absolute';
    };

    ComponentAbstract.prototype.doubleClick = function () {

    };

    ComponentAbstract.prototype.hasResponsiveTools = function () {
        return false;
    };

    /**
     *
     * @param {MouseEvent|Element} target
     * @param {{}} [options]
     */
    ComponentAbstract.prototype.onContextMenu = function (target, options) {

        options = options || {};

        var items = this.getContextMenuItems();

        if (!this.isActive) {
            this.activate();
        }

        this.fragmentEditor.pointerManager.contextualBarActive.enableActiveForced();

        if (options.onClose === undefined) {
            options.onClose = this.onContextMenuBlur.bind(this);
        } else {
            options.onClose = (function (callback) {
                callback.call();

                this.onContextMenuBlur();
            }).bind(this, options.onClose)
        }

        _N2.ContextMenu(target, items, options);
    };

    ComponentAbstract.prototype.onContextMenuBlur = function () {
        this.fragmentEditor.pointerManager.contextualBarActive.disableActiveForced();
    };

    /**
     * @returns {[]}
     */
    ComponentAbstract.prototype.getContextMenuItems = function () {

        var items = [],
            copySubItems = this.getContextMenuCopySubItems(),
            pasteSubItems = this.getContextMenuPasteSubItems();

        if (copySubItems.length) {
            items.push({
                priority: 60,
                label: n2_('Copy'),
                icon: 'ssi_16 ssi_16--copy',
                items: copySubItems
            });
        }

        if (pasteSubItems.length) {
            items.push({
                priority: 70,
                label: n2_('Paste'),
                icon: 'ssi_16 ssi_16--paste',
                items: pasteSubItems
            });
        }

        if (this.group) {

            var item = {
                    priority: 50,
                    label: n2_('Parent'),
                    icon: 'ssi_16 ssi_16--up',
                    action: (function () {
                        this.group.activate(true);
                    }).bind(this),
                    focus: (function () {
                        this.group.layer.addClass('n2_layer_context_menu_target');
                    }).bind(this),
                    blur: (function () {
                        this.group.layer.removeClass('n2_layer_context_menu_target');
                    }).bind(this)
                },
                parents = [],
                /**
                 * @type {_N2.ComponentAbstract}
                 */
                parent = this.group;
            for (var i = 0; i < 6 && parent; i++) {
                parents.push({
                    label: parent.getLabel(),
                    icon: parent.getIcon(),
                    action: (function () {
                        this.activate(true);
                    }).bind(parent),
                    focus: (function (component) {
                        component.layer.addClass('n2_layer_context_menu_target');
                    }).bind(this, parent),
                    blur: (function (component) {
                        component.layer.removeClass('n2_layer_context_menu_target');
                    }).bind(this, parent)
                });

                parent = parent.group;
            }

            if (parents.length) {
                item.items = parents;
            }

            items.push(item);
        }

        return items;
    };

    /**
     * @returns {[]}
     */
    ComponentAbstract.prototype.getContextMenuCopySubItems = function () {

        return [];
    };

    /**
     * @returns {[]}
     */
    ComponentAbstract.prototype.getContextMenuPasteSubItems = function () {
        var items = [];
        if (this.fragmentEditor.clipboard.componentHasClipboard()) {
            items.push({
                label: n2_('Layer(s)'),
                icon: 'ssi_16 ssi_16--layer',
                action: (function () {
                    this.fragmentEditor.clipboard.componentPaste(this);
                }).bind(this)
            });
        }
        return items;
    };

    ComponentAbstract.prototype.syncShowOnDevice = function (mode, value) {
        if (this.getMode() === mode) {
            value = parseInt(value);
            if (value) {
                this._show();
            } else {
                this._hide();
            }
            this.layer.triggerHandler('LayerShowChange', [mode, value]);

            this.placement.current.triggerLayerResized();
        }
    };

    ComponentAbstract.prototype.triggerLayerResized = function () {
        if (this.container) {
            var layers = this.container.getSortedLayers();
            for (var i = 0; i < layers.length; i++) {
                layers[i].triggerLayerResized();
            }
        }

        this.placement.current.triggerLayerResized();
    };

    return ComponentAbstract;
});_N2.d('ComponentCommonAbstract', ['$', 'ComponentAbstract'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @param group
     * @param properties
     *
     * @constructor
     * @augments _N2.ComponentAbstract
     */
    function ComponentCommonAbstract(fragmentEditor, group, properties) {

        this.supports = this.supports || [];

        this.isRenameAllowed = this.isRenameAllowed === undefined ? true : this.isRenameAllowed;

        this.wraps = {};


        this._lastClasses = false;

        _N2.ComponentAbstract.prototype.constructor.call(this, fragmentEditor, group, properties);
    }

    ComponentCommonAbstract.prototype = Object.create(_N2.ComponentAbstract.prototype);
    ComponentCommonAbstract.prototype.constructor = ComponentCommonAbstract;

    /**
     *
     * @param {_N2.ComponentSettings} componentSettings
     * @returns {{}}
     */
    ComponentCommonAbstract.getRegistryDataToFields = function (componentSettings) {

        var availableDeviceModes = componentSettings.fragmentEditor.editor.getAvailableDeviceModes(),
            registry = {
                id: $('#layerid'),
                uniqueclass: $('#layeruniqueclass'),
                desktopPortrait: $('#layershow-desktop-portrait'),
                tabletPortrait: $('#layershow-tablet-portrait'),
                mobilePortrait: $('#layershow-mobile-portrait'),
                zindex: $('#layerzindex'),
                class: $('#layerclass'),
                generatorvisible: $('#layergenerator-visible'),
                generatorvisible2: $('#layergenerator-visible2'),
                crop: $('#layercrop'),
                rotation: $('#layerrotation'),
                parallax: $('#layerparallax'),
                fontsize: $('#layerfont-size'),
                mouseenter: $('#layeronmouseenter'),
                click: $('#layeronclick'),
                mouseleave: $('#layeronmouseleave'),
                play: $('#layeronplay'),
                pause: $('#layeronpause'),
                stop: $('#layeronstop'),

            };

        componentSettings.fragmentEditor.editor.generator.registerField(registry.class);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.generatorvisible);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.generatorvisible2);

        if (!availableDeviceModes.tabletPortrait) {
            registry.tabletPortrait.closest('.n2_field_radio__option').css('display', 'none');
        }
        if (!availableDeviceModes.mobilePortrait) {
            registry.mobilePortrait.closest('.n2_field_radio__option').css('display', 'none');
        }

        return registry;
    };

    ComponentCommonAbstract.prototype.hasUp = function () {
        return true;
    };

    ComponentCommonAbstract.prototype.up = function (e) {
        e.stopImmediatePropagation();
        this.group.activate(e);
    };

    ComponentCommonAbstract.prototype.addProperties = function ($layer) {

        this.createProperty('id', '', $layer, this);
        this.createProperty('uniqueclass', null, $layer, this);

        this.createProperty('generatorvisible', '', $layer);
        this.createProperty('generatorvisible2', '', $layer);

        this.createProperty('zindex', 2, $layer);
        this.createProperty('class', '', $layer);
        this.createProperty('crop', 'visible', $layer);
        this.createProperty('rotation', 0, $layer);
        this.createProperty('parallax', 0, $layer);


        _N2.ComponentAbstract.prototype.addProperties.call(this, $layer);
    };

    ComponentCommonAbstract.prototype.onBeforeStart = function () {
        if (this.isRenameAllowed) {
            this.createProperty('name', this.getName(), this.layer);
            this.createProperty('nameSynced', 1, this.layer);
        }
    };

    ComponentCommonAbstract.prototype.hasTextScale = function () {
        return true;
    };

    ComponentCommonAbstract.prototype.hasResponsiveTools = function () {
        return true;
    };

    /**
     *
     * @param {Element} target
     * @param {function} [blurCallback]
     */
    ComponentCommonAbstract.prototype.onContextResponsiveTools = function (target, blurCallback) {

        if (!this.isActive) {
            this.activate();
        }

        var contextMenu = _N2.ContextMenu(target, [{
            type: 'raw',
            render: (function ($item) {

                var $responsiveTools = $('<div class="n2_layer_contextual_responsive_tools"></div>')
                    .appendTo($item);

                var $icon = $('<div class="n2_layer_contextual_responsive_tools__hide"><i class="ssi_16"></i></div>')
                    .toggleClass('n2_layer_contextual_responsive_tools__hide--hidden', !this.getRawProperty(this.getMode()))
                    .appendTo($responsiveTools)
                    .on('click', (function () {
                        var device = this.getMode(),
                            value = this.getProperty(device) ? 0 : 1;
                        this.setProperty(device, value);
                        $icon.toggleClass('n2_layer_contextual_responsive_tools__hide--hidden', !value);
                    }).bind(this));

                if (this.hasTextScale()) {
                    var $container = $('<div class="n2_layer_contextual_responsive_tools__text_scale"></div>').appendTo($responsiveTools),
                        $fontSizeField = this.fragmentEditor.layerOptions.currentForm.fontsize,
                        fontSize = this.getFontSize(),
                        $slider = $('<div></div>')
                            .width('50px')
                            .appendTo($container),
                        $input = $('<input type="number" min="10" max="990" step="10">')
                            .on('change', function () {
                                $fontSizeField.val(this.value).trigger('change');
                            })
                            .width('26px')
                            .val(fontSize)
                            .appendTo($container);
                    $('<div class="n2_layer_contextual_responsive_tools__text_scale_percent">%</div>')
                        .appendTo($container);

                    new _N2.UISlider($slider, _Assign({
                        start: (function () {
                            this.fragmentEditor.pointerManager.disable();
                            contextMenu.lock();
                        }).bind(this),
                        stop: (function (e, ui) {
                            $input.val(ui.value).trigger('change');
                            this.fragmentEditor.pointerManager.enable();
                            contextMenu.unlock();
                        }).bind(this),
                        cancel: (function () {
                            this.fragmentEditor.pointerManager.enable();
                            contextMenu.unlock();
                        }).bind(this),
                        sliding: (function (e, ui) {
                            $input.val(ui.value).trigger('change');
                        }).bind(this)
                    }, {
                        min: 30,
                        max: 120,
                        step: 10,
                        value: fontSize,
                    }));
                }
            }).bind(this)
        }], {
            forceLtr: true,
            verticalAlign: 'top',
            onClose: (function () {
                if (blurCallback !== undefined) {
                    blurCallback.call();
                }
            }).bind(this)
        });
    };

    /**
     * @returns {[]}
     */
    ComponentCommonAbstract.prototype.getContextMenuItems = function () {
        var items = _N2.ComponentAbstract.prototype.getContextMenuItems.call(this),
            labels = {
                "desktopPortrait": n2_('Desktop'),
                "desktopLandscape": n2_('Large desktop'),
                "tabletPortrait": n2_('Tablet'),
                "tabletLandscape": n2_('Large tablet'),
                "mobilePortrait": n2_('Mobile'),
                "mobileLandscape": n2_('Large mobile')
            },
            availableDevices = this.fragmentEditor.editor.getAvailableDeviceModes(),
            hideSubItems = [],
            hideItem = (function (device, label) {
                return {
                    label: label,
                    icon: 'ssi_16 ssi_16--eye',
                    action: (function (item) {
                        item.setData(showItem(device, label));
                        this.setProperty(device, 0);

                        return false;
                    }).bind(this)
                };
            }).bind(this),
            showItem = (function (device, label) {
                return {
                    label: label,
                    icon: 'ssi_16 ssi_16--hide',
                    action: (function (item) {
                        item.setData(hideItem(device, label));
                        this.setProperty(device, 1);

                        return false;
                    }).bind(this)
                };
            }).bind(this);

        for (var k in availableDevices) {
            if (availableDevices[k]) {
                if (parseInt(this.getProperty(k))) {
                    hideSubItems.push(hideItem(k, labels[k]));
                } else {
                    hideSubItems.push(showItem(k, labels[k]));
                }
            }
        }

        items.push({
            priority: 57,
            label: n2_('Hide on'),
            icon: 'ssi_16 ssi_16--hide',
            items: hideSubItems
        });

        return items;
    };

    ComponentCommonAbstract.prototype.getName = function () {
        if (this.property.nameSynced || this.property.name === undefined) {
            return this.getDefaultName();
        } else {
            return _strip_tags(this.property.name);
        }
    };

    ComponentCommonAbstract.prototype.editName = function () {
        this.activate(false);

        new _N2.InlineTextEditor(this.layerTitleSpan[0], {
            onSave: (function (value) {
                this.rename(value, true);
            }).bind(this),
            onCancel: (function () {
                this.layerTitleSpan.text(this.getName());
            }).bind(this),
            onTab: (function (e) {
                var $renames = $('.n2_layer_navigation_list_layer__title_label_rename');
                for (var i = 0; i < $renames.length; i++) {
                    if (this.$layerRowRename.is($renames[i])) {
                        if (e.shiftKey) {
                            i--;
                        } else {
                            i++;
                        }
                        break;
                    }
                }

                if (i < 0) {
                    i = $renames.length - 1;
                } else if (i >= $renames.length) {
                    i = 0;
                }

                $renames.eq(i).trigger('click');

            }).bind(this)
        });
    };

    ComponentCommonAbstract.prototype.rename = function (newName, force) {

        if (this.property.nameSynced || force) {

            if (force) {
                this.property.nameSynced = 0;
            }

            if (newName === '') {
                if (force) {
                    this.property.nameSynced = 1;
                }

                newName = this.getDefaultName();
            }

            newName = newName.trim().substr(0, 35);

            if (this.property.name !== newName) {
                this.property.name = newName;

                this.$.trigger('layerRenamed', newName);
            }

            this.layerTitleSpan.text(newName);
            if (this === this.fragmentEditor.activeLayer) {
                this.fragmentEditor.layerWindow.setTitle(this);
            }
        }
    };

    ComponentCommonAbstract.prototype.getPlacement = function () {
        var placement = this.layer.data('pm');
        if (placement !== undefined) {
            return placement
        }

        if (this.group.type === 'slide') {
            return 'absolute';
        }

        return 'normal';
    };

    ComponentCommonAbstract.prototype._start = function (isCreate) {
        this.createRow();

        var mask = this.layer.find('> .n2-ss-layer-mask');
        if (mask.length) {
            this.wraps.mask = mask;
        }

        this._synccrop();
        this._syncrotation();
        this._synczindex();

        this.placement.start();

        this.placement.setMode(this.getPlacement(), this.originalProperties);

        this.setGroup(this.group);


        this.fragmentEditor.$.triggerHandler('layerCreated', this);

        if (isCreate) {
            this.$.triggerHandler('created');
        }

        this.afterStart(isCreate);
    };

    ComponentCommonAbstract.prototype._createLayerListRow = function (actions) {
        _N2.ComponentAbstract.prototype._createLayerListRow.call(this, actions);

        if (this.isRenameAllowed) {

            this.$layerRowRename = $('<div class="n2_layer_navigation_list_layer__title_label_rename"><i class="ssi_16 ssi_16--rename"></i></div>')
                .on('click', (function () {
                    _N2.WindowManager.actionPrevent('ComponentRowActivate');

                    this.editName();
                }).bind(this))
                .insertAfter(this.$layerRowIcon);
            _N2._tooltip.addElement(this.$layerRowRename, n2_('Rename'));
        }

        this.$layerRowIcon
            .attr('data-action', 'hide-show')
            .on('click', this.toggleHideShow.bind(this));

        _N2._tooltip.addElement(this.$layerRowIcon, n2_('Show/Hide in editor'));
    };

    ComponentCommonAbstract.prototype.changeEditorMode = function (mode) {
        var value = parseInt(this.property[mode]);
        if (value) {
            this._show();
        } else {
            this._hide();
        }

        _N2.LayerDataStorage.prototype.changeEditorMode.call(this, mode);
    };

    ComponentCommonAbstract.prototype._synccrop = function () {
        var value = this.getProperty('crop');
        if (value === 'auto') {
            value = 'hidden';
        }

        if (value === 'mask') {
            value = 'hidden';
            var wrapper = _CreateElementDiv();
            wrapper.className = 'n2-ss-layer-mask n2-ss-layer-wrapper';
            this.addWrap('mask', wrapper);

        } else {
            this.removeWrap('mask');

            this.layer.data('animatableselector', null);
        }
        this.layer.css('overflow', value);
    };

    ComponentCommonAbstract.prototype._syncrotation = function () {

        _N2.MW.___getSMW(this.layer[0]).layerRotation = parseFloat(this.getProperty('rotation')) || 0;
    };

    ComponentCommonAbstract.prototype._synczindex = function () {

        var value = parseInt(this.getProperty('zindex'));

        if (isNaN(value) || value === 2) {
            this.layer.css('zIndex', '');
        } else {
            this.layer.css('zIndex', '' + value);
        }
    };

    ComponentCommonAbstract.prototype._syncclass = function () {
        if (this._lastClasses !== false) {
            this.layer.removeClass(this._lastClasses);
        }
        var value = this.fragmentEditor.editor.generator.fill(this.getProperty('class'));

        if (value && value != '') {
            this.layer.addClass(value);
            this._lastClasses = value;
        } else {
            this._lastClasses = false;
        }
    };

    ComponentCommonAbstract.prototype.addWrap = function (key, html) {
        if (this.wraps[key] === undefined) {
            var $el = $(html);
            this.wraps[key] = $el;

            switch (key) {
                case 'mask':
                    $el.appendTo(this.layer);
                    $el.append(this.getContents());
                    _NodeSetData(this.layer[0], 'animatableselector', '.n2-ss-layer-mask');
                    break;
            }
        }
        return this.wraps[key];
    };

    ComponentCommonAbstract.prototype.removeWrap = function (key) {
        if (this.wraps[key] !== undefined) {

            switch (key) {
                case 'mask':
                    this.layer.append(this.getContents());
                    _NodeRemoveData(this.layer[0], 'animatableselector');
                    break;
            }
            this.wraps[key].remove();
            delete this.wraps[key];
        }
    };

    ComponentCommonAbstract.prototype.getContextMenuCopySubItems = function () {
        var items = _N2.ComponentAbstract.prototype.getContextMenuCopySubItems.call(this);

        if (this.isAnimatable) {
            if (this.animations.hasAnimation()) {
                items.push({
                    label: n2_('Animation'),
                    icon: 'ssi_16 ssi_16--animation',
                    action: (function () {
                        localStorage.setItem('layerAnimationCopied', JSON.stringify(this.animations.getData()));
                    }).bind(this)
                });
            }
        }

        return items;
    };

    ComponentCommonAbstract.prototype.getContextMenuPasteSubItems = function () {
        var items = _N2.ComponentAbstract.prototype.getContextMenuPasteSubItems.call(this);

        if (this.isAnimatable) {

            var copied = localStorage.getItem('layerAnimationCopied');
            if (copied !== null) {

                items.push({
                    label: n2_('Animation'),
                    icon: 'ssi_16 ssi_16--animation',
                    action: (function () {
                        this.animations.loadData(JSON.parse(copied));
                    }).bind(this)
                });
            }
        }


        return items;
    };

    ComponentCommonAbstract.prototype.convertToNormal = function () {
        if (this.type === 'layer' || this.type === 'row') {

            var targetContainer = this.fragmentEditor.mainContainer.mainContent,
                group = this.group;

            while (group) {
                if (group.type === 'content' || group.type === 'col') {
                    targetContainer = group;
                    break;
                }

                group = group.group;
            }

            this.layer.css({
                position: 'relative',
                width: '',
                left: '',
                top: ''
            });

            if (targetContainer !== this.group) {
                var originalIndex = this.getIndex();
                targetContainer.container.insertLayerAt(this, 0);
                this.onCanvasUpdate(originalIndex, targetContainer, 0);
            }

            var originalPlacementData = this.placement.setMode('normal');

            var task = _N2.History.get().addValue(this, this.historyConvertPlacement, []);

            if (task) {
                task.setValues({
                    placement: 'absolute',
                    placementData: originalPlacementData
                }, {
                    placement: 'normal'
                });
            }
        }
    };

    ComponentCommonAbstract.prototype.convertToAbsolute = function () {
        if (this.type === 'layer' || this.type === 'row') {

            var originalIndex = this.getIndex(),
                targetContainer = this.group,
                width = this.layer.width(),
                height = this.layer.height();


            _N2.History.get().startBatch();

            var originalPlacementData = this.placement.setMode('absolute');

            var task = _N2.History.get().addValue(this, this.historyConvertPlacement, []);

            if (task) {
                task.setValues({
                    placement: 'normal',
                    placementData: originalPlacementData
                }, {
                    placement: 'absolute'
                });
            }

            _N2.History.get().addControl('skipForwardUndos');

            if (this.type === 'layer' && this.item) {
                if (!this.item.needHeight) {
                    height = 'auto';
                }
            }

            this.placement.current._setPosition('center', 'middle', 0, 0, width, height, false);

            _N2.History.get().endBatch();
        }
    };

    ComponentCommonAbstract.prototype.historyConvertPlacement = function (data) {
        if (data.placementData) {
            this.layer.data(data.placementData);
        }

        this.placement.setMode(data.placement);

        if (data.placementData) {
            this.placement.sync();
        }
    };

    return ComponentCommonAbstract;
});_N2.d('Content', ['$', 'ContentAbstract'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments {_N2.ContentAbstract}
     */
    function Content(fragmentEditor, group, properties) {
        this.label = n2_('Content');
        this.type = 'content';

        this.isRenameAllowed = false;

        this.innerContainer = '> .n2-ss-layer-content';

        this._defaults = _Assign({
            verticalalign: 'center',
            selfalign: 'center'
        }, this._defaults);


        this.localStyle = [
            {
                group: 'normal', selector: '-inner', css: {}
            },
            {group: 'hover', selector: '-inner:HOVER', css: {}}
        ];

        _N2.ContentAbstract.prototype.constructor.call(this, fragmentEditor, group, properties);

        this.placement.allow('default');

        group.setMainContent(this);
    }

    Content.prototype = Object.create(_N2.ContentAbstract.prototype);
    Content.prototype.constructor = Content;

    /**
     *
     * @param {_N2.ComponentSettings} componentSettings
     * @returns {{}}
     */
    Content.getRegistryDataToFields = function (componentSettings) {

        var registry = {
            maxwidth: $('#layercontent-maxwidth'),
            selfalign: $('#layercontent-selfalign'),
            padding: $('#layercontent-padding'),
            inneralign: $('#layercontent-inneralign'),
            verticalalign: $('#layercontent-verticalalign'),
            stylemode: $('#layerfields-content-background-mode'),
            bgcolor: $('#layercontent-background-color'),
            bgimage: $('#layercontent-background-image'),
            bgimagex: $('#layercontent-background-focus-x'),
            bgimagey: $('#layercontent-background-focus-y'),
            bgcolorgradient: $('#layercontent-background-gradient'),
            bgcolorgradientend: $('#layercontent-background-color-end'),
            opened: $('#layercontent-opened')
        };

        componentSettings.initStyleMode('content', registry.stylemode, []);

        componentSettings.fragmentEditor.editor.generator.registerField(registry.bgimage);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.bgcolor);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.bgcolorgradientend);

        return registry;
    };

    Content.prototype.getIcon = function () {
        return 'ssi_16 ssi_16--content';
    };

    Content.prototype.addProperties = function ($layer) {

        _N2.ContentAbstract.prototype.addProperties.call(this, $layer);

        this.createDeviceProperty('selfalign', {desktopPortrait: this._defaults.selfalign || 'inherit'}, $layer);

    };

    Content.prototype.getRootElement = function () {
        return this.layer;
    };

    Content.prototype.getBackgroundElement = function () {
        return this.layer;
    };

    Content.prototype.getPlacement = function () {
        return 'default';
    };

    Content.prototype._createLayer = function () {
        return $('<div class="n2-ss-layer n2-ss-content-empty"><div class="n2-ss-section-main-content n2-ss-layer-with-background n2-ss-layer-content"></div></div>')
            .attr('data-sstype', this.type);
    };

    Content.prototype.createRow = function () {

        this.$content = this.layer.find('.n2-ss-layer-content:first');

        this.addClassElement(this.layer);
        this.addClassElement(this.$content, '-inner');


        this.container = new _N2.LayerContainer(this);
        this.container.setLayerContainerElement(this.$content);

        this._createLayerListRow();

        var $buttonOpenClose = $('<div class="n2_layer_navigation_list_layer__title_label_folder" data-action="open-close"><i class="ssi_16"></i></div>')
            .on('click', this.switchOpened.bind(this))
            .prependTo(this.$rowLabel);

        _N2._tooltip.addElement($buttonOpenClose, n2_('Open/Close'));

        this.container.$ul.appendTo(this.layerRow);

        this.readyDeferred.done(this._syncopened.bind(this));
    };

    Content.prototype.create = function () {

        _N2.ContentAbstract.prototype.create.call(this);

        this._syncselfalign();

        this._onReady();
    };

    Content.prototype.load = function ($layer, isEditorStart) {

        _N2.ContentAbstract.prototype.load.call(this, $layer, isEditorStart);

        this._syncselfalign();

        this._onReady();
    };

    Content.prototype.delete = function () {
        var layers = this.container.getSortedLayers();
        for (var i = 0; i < layers.length; i++) {
            layers[i].delete();
        }
    };

    Content.prototype.remove = function () {
        this._delete();
    };

    Content.prototype.update = function () {
        this.group.update();
    };

    Content.prototype.onChildCountChange = function () {

        var layers = this.container.getSortedLayers();

        this.layer.toggleClass('n2-ss-content-empty', layers.length === 0);
    };

    Content.prototype.renderModeProperties = function (isReset) {
        _N2.ContentAbstract.prototype.renderModeProperties.call(this, isReset);

        this._syncselfalign();
    };

    Content.prototype._syncselfalign = function () {

        _N2.ComponentAbstract.selfAlignToStyle(this.layer, this.getProperty('selfalign'));

        this.update();
    };

    Content.prototype.duplicate = function (needActivate) {

    };

    return Content;
});_N2.d('ContentAbstract', ['$', 'LayerContainer', 'ComponentCommonAbstract'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param fragmentEditor
     * @param group
     * @param properties
     * @constructor
     * @augments {_N2.ComponentCommonAbstract}
     */
    function ContentAbstract(fragmentEditor, group, properties) {

        this._defaults = _Assign({verticalalign: 'center'}, this._defaults);

        this._syncbgThrottled = NextendThrottle(this._syncbgThrottled, 50);

        this.stylemode = '';

        _N2.ComponentCommonAbstract.prototype.constructor.call(this, fragmentEditor, group, properties);
    }

    ContentAbstract.prototype = Object.create(_N2.ComponentCommonAbstract.prototype);
    ContentAbstract.prototype.constructor = ContentAbstract;

    ContentAbstract.prototype.addProperties = function ($layer) {

        this.createProperty('opened', 1, $layer, this);

        _N2.ComponentCommonAbstract.prototype.addProperties.call(this, $layer);

        this.createProperty('bgimage', '', $layer);
        this.createProperty('bgimagex', 50, $layer);
        this.createProperty('bgimagey', 50, $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('bgcolor', '00000000', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('bgcolorgradient', 'off', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('bgcolorgradientend', '00000000', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createDeviceProperty('verticalalign', {desktopPortrait: this._defaults.verticalalign}, $layer);

        this.createDeviceProperty('maxwidth', {desktopPortrait: 0}, $layer);

        this.createDeviceProperty('inneralign', {desktopPortrait: 'inherit'}, $layer);
        this.createDeviceProperty('padding', {desktopPortrait: '10|*|10|*|10|*|10'}, $layer);

    };

    ContentAbstract.prototype.getBackgroundElement = function () {
        return this.$content;
    };

    ContentAbstract.prototype.getPaddingElement = function () {
        return this.$content;
    };

    ContentAbstract.prototype.create = function ($layer) {
        _N2.ComponentCommonAbstract.prototype.create.apply(this, arguments);

        this.initUI();

        this._syncverticalalign();
        this._syncmaxwidth();
        this._syncpadding();
        this._syncinneralign();
        this._syncbgThrottled();
    };

    ContentAbstract.prototype.load = function ($layer, isEditorStart) {

        _N2.ComponentCommonAbstract.prototype.load.call(this, $layer, isEditorStart);

        this.initUI();

        this._syncverticalalign();
        this._syncmaxwidth();
        this._syncpadding();
        this._syncinneralign();
        this._syncbgThrottled();

        this.container.startWithExistingNodes(isEditorStart);
    };

    ContentAbstract.prototype.initUI = function () {

        this.uiSpacing = new _N2.UISpacingPadding(this.getPaddingElement(), this.fragmentEditor, {
            stop: this.onSpacingStop.bind(this),
        });

        this.editorUI.padding = this.uiSpacing.$handles;

        this.uiResizeNormal = new _N2.UIResizeNormal(this.layer, this.fragmentEditor, {
            stopMaxWidth: (function (e, ui) {
                this.setProperty('maxwidth', ui.value);
            }).bind(this)
        });

        this.editorUI.normalSizing = this.uiResizeNormal.$handles;
    };

    ContentAbstract.prototype.doubleClick = function () {
        $('[data-tab="layer"]').trigger('click');
    };

    ContentAbstract.prototype.onSpacingStop = function (event, ui) {
        var padding = this.getPadding().split('|*|');

        for (var k in ui.changed) {
            var value = ui.changed[k];
            switch (k) {
                case 'top':
                    padding[0] = Math.round(value);
                    break;
                case 'right':
                    padding[1] = Math.round(value);
                    break;
                case 'bottom':
                    padding[2] = Math.round(value);
                    break;
                case 'left':
                    padding[3] = Math.round(value);
                    break;
            }
        }
        this.setProperty('padding', padding.join('|*|'));
        $('#layercol-padding').data('field').insideChange(padding.join('|*|'));
    };

    ContentAbstract.prototype.switchOpened = function (e) {
        e.preventDefault();

        _N2.WindowManager.actionPrevent('ComponentRowActivate');

        if (!this.layerRow.hasClass('n2_layer_navigation_list_layer--closed')) {
            this.setProperty('opened', 0);
        } else {
            this.setProperty('opened', 1);
        }
    };

    ContentAbstract.prototype._syncopened = function () {
        if (this.getProperty('opened')) {
            this.layerRow.removeClass('n2_layer_navigation_list_layer--closed');
            this.container.$ul.css('display', '');

            this.layer.triggerHandler('opened');
        } else {
            this.layerRow.addClass('n2_layer_navigation_list_layer--closed');
            this.container.$ul.css('display', 'none');

            this.layer.triggerHandler('closed');
        }
    };

    ContentAbstract.prototype.forceOpen = function () {
        if (this.layerRow.hasClass('n2_layer_navigation_list_layer--closed')) {
            this.layerRow.removeClass('n2_layer_navigation_list_layer--closed');
            this.container.$ul.css('display', '');

            this.layer.triggerHandler('opened');
        }
    };

    ContentAbstract.prototype.removeForceOpen = function () {
        if (!this.getProperty('opened') && !this.layerRow.hasClass('n2_layer_navigation_list_layer--closed')) {
            this.layerRow.addClass('n2_layer_navigation_list_layer--closed');
            this.container.$ul.css('display', 'none');

            this.layer.triggerHandler('closed');
        }
    };

    ContentAbstract.prototype.getPadding = function () {
        return this.getProperty('padding');
    };

    ContentAbstract.prototype._syncpadding = function () {
        var padding = this.getPadding().split('|*|'),
            unit = 'px';

        padding.splice(4); // drop legacy unit

        var css = padding.join(unit + ' ') + unit;
        this.getPaddingElement().css('padding', css);
        this.update();

        if (this.uiSpacing) {
            this.uiSpacing.setOption('current', css);
        }
    };

    ContentAbstract.prototype._syncmaxwidth = function () {
        var value = parseInt(this.getProperty('maxwidth'));
        if (value <= 0 || isNaN(value)) {
            this.layer.css('maxWidth', 'none');

            this.uiResizeNormal.setMaxWidth(-1);
        } else {
            this.layer.css('maxWidth', value + 'px');

            this.uiResizeNormal.setMaxWidth(value);
        }

        this.update();
    };

    ContentAbstract.prototype.getInnerAlign = function () {
        return this.getProperty('inneralign');
    };

    ContentAbstract.prototype._syncinneralign = function () {
        _N2.ComponentAbstract.innerAlignToStyle(this.$content, this.getInnerAlign());

        this.refreshTextAlign();

        this.update();
    };

    ContentAbstract.prototype.getVerticalAlign = function () {
        return this.getProperty('verticalalign');
    };

    ContentAbstract.prototype._syncverticalalign = function () {
        this.$content.css('justify-content', this.getVerticalAlign());

        this.update();
    };

    ContentAbstract.prototype._syncbgimage =
        ContentAbstract.prototype._syncbgimagex =
            ContentAbstract.prototype._syncbgimagey =
                ContentAbstract.prototype._syncbgcolor =
                    ContentAbstract.prototype._syncbgcolorgradient =
                        ContentAbstract.prototype._syncbgcolorgradientend =
                            ContentAbstract.prototype['_syncbgcolor-hover'] =
                                ContentAbstract.prototype['_syncbgcolorgradient-hover'] =
                                    ContentAbstract.prototype['_syncbgcolorgradientend-hover'] = function () {
                                        this._syncbgThrottled();
                                    };


    ContentAbstract.prototype._syncbgThrottled = function () {
        var backgroundStyle = '',
            image = this.fragmentEditor.editor.generator.fill(this.getProperty('bgimage'));
        if (image !== '') {
            var x = parseInt(this.getProperty('bgimagex'));
            if (!isFinite(x)) {
                x = 50;
            }
            var y = parseInt(this.getProperty('bgimagey'));
            if (!isFinite(y)) {
                y = 50;
            }
            backgroundStyle += '--n2bgimage:URL("' + _N2._imageHelper.fixed(image) + '");';
            backgroundStyle += 'background-position:50% 50%,' + x + '% ' + y + '%;';
        }
        var color = this.fragmentEditor.editor.generator.fill(this.getProperty('bgcolor')),
            gradient = this.getProperty('bgcolorgradient'),
            colorend = this.fragmentEditor.editor.generator.fill(this.getProperty('bgcolorgradientend')),
            normalStyle = this.getBackgroundCSS(color, gradient, colorend, backgroundStyle) + backgroundStyle;

        this.addLocalStyle('normal', 'bgcolor', normalStyle);


        var hoverStyle = '',
            isHoverDifferent = false,
            colorHover = this.fragmentEditor.editor.generator.fill(this.getProperty('bgcolor-hover')),
            gradientHover = this.getProperty('bgcolorgradient-hover'),
            colorendHover = this.fragmentEditor.editor.generator.fill(this.getProperty('bgcolorgradientend-hover'));

        if (colorHover !== undefined && colorHover !== color) {
            isHoverDifferent = true;
        }
        if (gradientHover !== undefined && gradientHover !== gradient) {
            isHoverDifferent = true;
        }
        if (colorendHover !== undefined && colorendHover !== colorend) {
            isHoverDifferent = true;
        }

        if (isHoverDifferent) {
            if (colorHover === undefined) {
                colorHover = color;
            }
            if (gradientHover === undefined) {
                gradientHover = gradient;
            }
            if (colorendHover === undefined) {
                colorendHover = colorend;
            }
            hoverStyle = this.getBackgroundCSS(colorHover, gradientHover, colorendHover, backgroundStyle) + backgroundStyle;
        }
        this.addLocalStyle('hover', 'bgcolor', hoverStyle);
    };

    ContentAbstract.prototype.getBackgroundCSS = function (color, gradient, colorend, backgroundStyle) {
        if (N2Color.hex2alpha(color) !== 0 || (gradient !== 'off' && N2Color.hex2alpha(colorend) !== 0)) {
            switch (gradient) {
                case 'horizontal':
                    return '--n2bggradient:linear-gradient(to right, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorend) + ' 100%);';
                case 'vertical':
                    return '--n2bggradient:linear-gradient(to bottom, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorend) + ' 100%);';
                case 'diagonal1':
                    return '--n2bggradient:linear-gradient(45deg, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorend) + ' 100%);';
                case 'diagonal2':
                    return '--n2bggradient:linear-gradient(135deg, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorend) + ' 100%);';
                case 'off':
                default:
                    if (backgroundStyle !== '') {
                        return "--n2bggradient: linear-gradient(" + N2Color.hex2rgbaCSS(color) + ", " + N2Color.hex2rgbaCSS(color) + ');';
                    }
                    return 'background-color:' + N2Color.hex2rgbaCSS(color) + ';';
            }
        }

        return '';
    };

    ContentAbstract.prototype.getData = function (params) {
        var data = _N2.ComponentCommonAbstract.prototype.getData.call(this, params);

        if (params.layersIncluded) {
            data.layers = this.container.getData(params);
        }

        return data;
    };

    ContentAbstract.prototype.onChildCountChange = function () {
        this.layer.toggleClass('n2-ss-content-empty', this.container.getLayerCount() === 0);

        this.update();
    };

    ContentAbstract.prototype.renderModeProperties = function (isReset) {
        _N2.ComponentCommonAbstract.prototype.renderModeProperties.call(this, isReset);

        this._syncverticalalign();
        this._syncmaxwidth();
        this._syncpadding();
        this._syncinneralign();
    };

    ContentAbstract.prototype.getDroppable = function () {
        if (!this.layer.is(":visible") || this.status === _N2.ComponentAbstract.STATUS.HIDDEN) {
            return 'hidden';
        }
        return {
            $container: this.$content,
            layer: this,
            placement: 'normal',
            axis: 'y'
        }
    };

    ContentAbstract.prototype.getLLDroppable = function (layer) {

        if (this.getProperty('opened')) {
            var layerPlacement = layer.placement.getType();
            if (layerPlacement === 'absolute') {
                return {
                    $container: this.container.$ul,
                    layer: this
                };
            }

            if (layer.placement.getType() === 'normal') {
                switch (layer.type) {
                    case 'layer':
                    case 'row':
                        return {
                            $container: this.container.$ul,
                            layer: this
                        };
                }
            }
        }

        return false;
    };

    ContentAbstract.prototype.getContents = function () {
        return this.$content;
    };

    ContentAbstract.prototype.setPropertystylemode = function (name, value, from) {
        this.stylemode = value;

        this.syncAdvancedField('bgcolor');
        this.syncAdvancedField('bgcolorgradient');
        this.syncAdvancedField('bgcolorgradientend');
    };

    ContentAbstract.prototype.onSyncFields = function () {
        this.fragmentEditor.layerOptions.updateField('stylemode', this.stylemode);
    };

    ContentAbstract.prototype.getContextMenuCopySubItems = function () {
        var items = _N2.ComponentCommonAbstract.prototype.getContextMenuCopySubItems.call(this);

        items.push({
            label: n2_('Child layers'),
            icon: 'ssi_16 ssi_16--layer',
            action: (function () {
                this.fragmentEditor.clipboard.componentCopy(this);
            }).bind(this)
        });

        return items;
    };

    return ContentAbstract;
});_N2.d('Layer', ['$', 'ComponentCommonAbstract'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments _N2.ComponentCommonAbstract
     */
    function Layer(fragmentEditor, group, properties) {
        this.label = n2_('Layer');
        this.type = 'layer';
        /**
         * @type {_N2.Item}
         */
        this.item = null;

        this.parent = false;

        this.itemEditor = fragmentEditor.itemEditor;

        _N2.ComponentCommonAbstract.prototype.constructor.call(this, fragmentEditor, group, properties);

        this.placement.allow('normal');
        this.placement.allow('absolute');
    }

    Layer.prototype = Object.create(_N2.ComponentCommonAbstract.prototype);
    Layer.prototype.constructor = Layer;

    Layer.prototype.getSubType = function () {
        return this.item.type;
    };

    Layer.prototype.getLabel = function () {

        return this.item.getLabel();
    };

    Layer.prototype.hasTextScale = function () {
        return this.item.hasTextScale();
    };

    Layer.prototype.create = function () {

        _N2.ComponentCommonAbstract.prototype.create.apply(this, arguments);

        this._onReady();
    };

    Layer.prototype.load = function ($layer, isEditorStart) {

        _N2.ComponentCommonAbstract.prototype.load.call(this, $layer, isEditorStart);

        this._onReady();
    };

    Layer.prototype.onBeforeStart = function () {
        var $item = this.layer.find('.n2-ss-item');

        new _N2[this.itemEditor.getItemClass($item.data('item'))]($item, this, this.itemEditor);

        new _N2.UICanvasItem(this.layer, {
            canvasUIManager: this.fragmentEditor.canvasUIManager,
            layer: this,
            $layer: this.layer
        });

        if (this.item.needHeight) {
            this.layer.addClass('n2-ss-layer--need-height');
        }

        _N2.ComponentCommonAbstract.prototype.onBeforeStart.call(this);
    };

    Layer.prototype.doubleClick = function () {
        $('[data-related-tab="content"]').trigger('click');
        this.item.itemEditor.focusFirst();
    };

    Layer.prototype.getDefaultName = function () {
        return this.item.getLabel();
    };

    Layer.prototype.getContent = function () {

        var $content = this.layer,
            selector = $content.data('animatableselector');
        if (selector) {
            $content = $($content[0].querySelector(selector));
        }
        return $content;
    };

    Layer.prototype._createLayer = function () {
        return $('<div class="n2-ss-layer"></div>')
            .attr('data-sstype', this.type);
    };

    Layer.prototype.createRow = function () {

        this._createLayerListRow();
    };

    /**
     *
     * @param showLayerWindow
     * @param focusFirstItemField
     */
    Layer.prototype.activate = function (showLayerWindow, focusFirstItemField) {

        _N2.ComponentCommonAbstract.prototype.activate.call(this, showLayerWindow, focusFirstItemField);

        if (this.item) {
            this.item.activate(focusFirstItemField);
        } else {
            console.error('The layer do not have item on it!');
        }
    };

    Layer.prototype.getHTML = function (base64) {

        var $node = _N2.ComponentCommonAbstract.prototype.getHTML.call(this, base64);

        var $item = this.item.getHTML(base64);
        $node.attr('style', $node.attr('style') + this.getStyleText())
            .append($item);

        return $node;
    };

    Layer.prototype.getData = function (params) {
        var data = _N2.ComponentCommonAbstract.prototype.getData.call(this, params);

        if (params.itemsIncluded) {
            data.item = this.item.getData();
        }
        return data;
    };

    Layer.prototype.getStyleText = function () {
        var style = '';
        var crop = this.property.crop;
        if (crop === 'auto' || crop === 'mask') {
            crop = 'hidden';
        }

        style += 'overflow:' + crop + ';';
        return style;
    };

    Layer.prototype.getContents = function () {
        return this.item.$item;
    };

    Layer.prototype.onSelfChange = function () {
        _N2.ComponentCommonAbstract.prototype.onSelfChange.call(this);

        this.item.setSelf(this.self.item);
    };

    /**
     * @returns {[]}
     */
    Layer.prototype.getContextMenuItems = function () {
        var items = _N2.ComponentCommonAbstract.prototype.getContextMenuItems.call(this);

        items.push({
            priority: 55,
            label: n2_('Duplicate'),
            icon: 'ssi_16 ssi_16--duplicate',
            action: this.duplicate.bind(this)
        });

        items.push({
            priority: 1000,
            label: n2_('Delete'),
            icon: 'ssi_16 ssi_16--delete',
            action: this.delete.bind(this),
            color: 'red'
        });

        return items;
    };

    Layer.prototype.getContextMenuCopySubItems = function () {
        var items = _N2.ComponentCommonAbstract.prototype.getContextMenuCopySubItems.call(this);

        items.unshift({
            label: n2_('Layer'),
            icon: 'ssi_16 ssi_16--layer',
            action: (function () {
                this.fragmentEditor.clipboard.componentCopy(this);
            }).bind(this)
        });

        return items;
    };

    return Layer;
});_N2.d('LayerAdvancedProperty', '$', function () {
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param basename
     * @param def
     * @param modesDef
     * @param obj
     * @param propName
     * @constructor
     */
    function LayerAdvancedProperty(basename, def, modesDef, obj, propName) {
        this.basename = basename;
        this.def = def;
        this.modesDef = modesDef;
        this.obj = obj;
        this.propName = propName;
    }

    LayerAdvancedProperty.prototype.getBaseName = function () {
        return this.basename;
    };

    /**
     *
     * @returns {object}
     */
    LayerAdvancedProperty.prototype.getNames = function () {
        var a = {};
        a[this.basename] = this.def;
        for (var k in this.modesDef) {
            a[this.basename + k] = this.modesDef[k];
        }
        return a;
    };

    LayerAdvancedProperty.prototype.getCurrentMode = function () {
        return this.obj[this.propName];
    };

    LayerAdvancedProperty.prototype.getName = function () {
        var currentMode = this.getCurrentMode();
        if (currentMode !== '') {
            return this.basename + currentMode;
        }
        return this.basename;
    };

    LayerAdvancedProperty.prototype.getDefault = function () {
        var currentMode = this.getCurrentMode();
        if (currentMode !== '') {
            return this.modesDef[currentMode];
        }
        return this.def;
    };

    LayerAdvancedProperty.prototype.resetMode = function (name) {
        if (this.propName === name) {
            var currentMode = this.getCurrentMode();
            if (currentMode !== '') {
                var oldValue = this.obj.property[this.basename + currentMode];
                this.obj.property[this.basename + currentMode] = this.modesDef[currentMode];
                this.obj.syncAdvancedField(this.basename);

                this.obj.render(this.basename + currentMode, oldValue, 'manager');
            }
        }
    };

    return LayerAdvancedProperty;
});_N2.d('Row', ['$', 'LayerContainer', 'ComponentCommonAbstract'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param fragmentEditor
     * @param group
     * @param properties
     * @constructor
     * @augments {_N2.ComponentCommonAbstract}
     */
    function Row(fragmentEditor, group, properties) {
        this.label = n2_('Row');
        this.type = 'row';

        this._syncbgThrottled = NextendThrottle(this._syncbgThrottled, 50);

        this.innerContainer = '> .n2-ss-layer-row > .n2-ss-layer-row-inner';

        this.localStyle = [
            {
                group: 'normal', selector: '-inner', css: {}
            },
            {group: 'hover', selector: '-inner:HOVER', css: {}}
        ];

        this.columnsField = $('#layerrow-columns').data('field');

        this.refreshUI = NextendDeBounce(this.refreshUI, 100);

        this.stylemode = '';

        _N2.ComponentCommonAbstract.prototype.constructor.call(this, fragmentEditor, group, properties);

        this.placement.allow('normal');
        this.placement.allow('absolute');
    }

    Row.prototype = Object.create(_N2.ComponentCommonAbstract.prototype);
    Row.prototype.constructor = Row;

    /**
     *
     * @param {_N2.ComponentSettings} componentSettings
     * @returns {{}}
     */
    Row.getRegistryDataToFields = function (componentSettings) {

        var registry = {
            padding: $('#layerrow-padding'),
            gutter: $('#layerrow-gutter'),
            fullwidth: $('#layerrow-fullwidth'),
            stretch: $('#layerrow-stretch'),
            wrapafter: $('#layerrow-wrap-after'),
            inneralign: $('#layerrow-inneralign'),
            href: $('#layerrow-href'),
            'href-target': $('#layerrow-href-target'),
            'aria-label': $('#layerrow-aria-label'),
            bgimage: $('#layerrow-background-image'),
            bgimagex: $('#layerrow-background-focus-x'),
            bgimagey: $('#layerrow-background-focus-y'),
            stylemode: $('#layerfields-row-background-mode'),
            bgcolor: $('#layerrow-background-color'),
            bgcolorgradient: $('#layerrow-background-gradient'),
            bgcolorgradientend: $('#layerrow-background-color-end'),
            borderwidth: $('#layerrow-border-width'),
            borderstyle: $('#layerrow-border-style'),
            bordercolor: $('#layerrow-border-color'),
            borderradius: $('#layerrow-border-radius'),
            boxshadow: $('#layerrow-boxshadow'),
            opened: $('#layerrow-opened')
        };

        componentSettings.initStyleMode('row', registry.stylemode, ['#layerfields-row-border-mode']);

        componentSettings.fragmentEditor.editor.generator.registerField(registry.href);
        componentSettings.fragmentEditor.editor.generator.registerField(registry['aria-label']);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.bgimage);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.bgcolor);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.bgcolorgradientend);

        return registry;
    };

    Row.prototype.getIcon = function () {
        return 'ssi_16 ssi_16--row';
    };

    Row.prototype.addProperties = function ($layer) {

        this.createProperty('opened', 1, $layer, this);

        _N2.ComponentCommonAbstract.prototype.addProperties.call(this, $layer);


        this.createProperty('href', '', $layer);
        this.createProperty('href-target', '_self', $layer);
        this.createProperty('aria-label', '', $layer);

        this.createProperty('bgimage', '', $layer);
        this.createProperty('bgimagex', 50, $layer);
        this.createProperty('bgimagey', 50, $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('bgcolor', '00000000', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('bgcolorgradient', 'off', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('bgcolorgradientend', '00000000', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('borderwidth', '0|*|0|*|0|*|0', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('borderstyle', 'solid', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('bordercolor', 'ffffffff', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('borderradius', 0, {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createAdvancedProperty(new _N2.LayerAdvancedProperty('boxshadow', '0|*|0|*|0|*|0|*|00000080', {
            "-hover": undefined
        }, this, "stylemode"), $layer);

        this.createProperty('fullwidth', 1, $layer);
        this.createProperty('stretch', 0, $layer);


        this.createDeviceProperty('inneralign', {desktopPortrait: 'inherit'}, $layer);
        this.createDeviceProperty('padding', {desktopPortrait: '10|*|10|*|10|*|10'}, $layer);
        this.createDeviceProperty('gutter', {desktopPortrait: 20}, $layer);
        this.createDeviceProperty('wrapafter', {desktopPortrait: 0, mobilePortrait: 1, mobileLandscape: 1}, $layer);

    };

    Row.prototype.historyDeleteSelf = function () {
        this.delete();
    };

    Row.prototype.historyCreateSelf = function (historyGroup, preset, historyCols) {
        var newLayer = new _N2.Row(this.fragmentEditor, historyGroup.getSelf(), {});
        newLayer.create(preset);

        this.setSelf(newLayer);

        var newCols = newLayer.container.getSortedLayers();
        for (var i = 0; i < newCols.length; i++) {
            historyCols[i].setSelf(newCols[i]);
        }
    };

    Row.prototype.create = function (preset) {
        var $row,
            _createRawRow = function (cols) {
                return $("<div class='n2-ss-layer'></div>")
                    .append($("<div class='n2-ss-layer-row n2-ss-layer-with-background'></div>")
                        .append($("<div class='n2-ss-layer-row-inner'></div>")
                            .append(cols)))
                    .attr('data-sstype', 'row');
            },
            _createRawCol = function (inner) {
                return $("<div class='n2-ss-layer' data-sstype='col'></div>").append($("<div class='n2-ss-layer-col n2-ss-layer-with-background n2-ss-layer-content'></div>").append(inner));
            };
        switch (preset) {
            case 'structure-2col':
                $row = _createRawRow([_createRawCol(), _createRawCol()]);
                break;
            case 'structure-3col':
                $row = _createRawRow([_createRawCol(), _createRawCol(), _createRawCol()]);
                break;
            case 'structure-1col':
            default:
                $row = _createRawRow([_createRawCol()]);
        }

        if (this.group.type === 'slide') {
            this.originalProperties = _Assign({
                width: '100%',
                align: 'center',
                valign: 'top',
                top: 20
            }, this.originalProperties);
        }

        _N2.ComponentCommonAbstract.prototype.create.call(this, $row);

        this.initUI();

        this.container.startWithExistingNodes(false);

        this._syncpadding();
        this._syncinneralign();
        this._syncfullwidth();
        this._syncstretch();
        this._syncbgThrottled();

        this._syncborder();
        this._syncborderradius();
        this._syncboxshadow();


        this.renderModeProperties();
        this.container.renderModeProperties();


        _N2.History.get().addSimple(this, this.historyDeleteSelf, this.historyCreateSelf, [this.group, preset, this.container.getSortedLayers()]);

        this._onReady();
    };

    Row.prototype.load = function ($layer, isEditorStart) {

        _N2.ComponentCommonAbstract.prototype.load.call(this, $layer, isEditorStart);

        this.initUI();

        this.container.startWithExistingNodes(isEditorStart);

        this._syncpadding();
        this._syncinneralign();
        this._syncfullwidth();
        this._syncstretch();
        this._syncbgThrottled();

        this._syncborder();
        this._syncborderradius();
        this._syncboxshadow();

        this._onReady();
    };

    Row.prototype.initUI = function () {

        new _N2.UICanvasItem(this.layer, {
            canvasUIManager: this.fragmentEditor.canvasUIManager,
            layer: this,
            $layer: this.layer
        });

        this.uiSpacing = new _N2.UISpacingPadding(this.$row, this.fragmentEditor, {
            stop: this.____makeLayerResizeableStop.bind(this),
        });

        this.editorUI.padding = this.uiSpacing.$handles;

        this.editorUI.columns = this.uiColumns = new _N2.UIColumns(this.$row, this.fragmentEditor, {
            columns: '1',
            gutter: this.getGutter(),
            start: (function (e, ui) {
                _N2.PositionDisplay.get().show('Columns');
            }).bind(this),
            colwidth: (function (e, ui) {
                this.updateColumnWidth(ui.currentPercent);

                _N2.PositionDisplay.get().update(e, 'Columns', Math.round(ui.currentPercent[ui.index] * 100) + '% &mdash; ' + Math.round(ui.currentPercent[ui.index + 1] * 100) + '%');


            }).bind(this),
            stop: (function (e, ui) {
                _N2.PositionDisplay.get().hide('Columns');

                this.setRealColsWidth(ui.currentFractions);
            }).bind(this)
        });
    };

    Row.prototype.doubleClick = function () {
        $('[data-tab="row"]').trigger('click');
    };

    Row.prototype.____makeLayerResizeableStop = function (event, ui) {
        var padding = this.getPadding().split('|*|');

        for (var k in ui.changed) {
            var value = ui.changed[k];
            switch (k) {
                case 'top':
                    padding[0] = Math.round(value);
                    break;
                case 'right':
                    padding[1] = Math.round(value);
                    break;
                case 'bottom':
                    padding[2] = Math.round(value);
                    break;
                case 'left':
                    padding[3] = Math.round(value);
                    break;
            }
        }
        this.setProperty('padding', padding.join('|*|'));
        $('#layerrow-padding').data('field').insideChange(padding.join('|*|'));
    };

    Row.prototype._createLayer = function () {
        return $('<div class="n2-ss-layer"><div class="n2-ss-layer-row n2-ss-layer-with-background"><div class="n2-ss-layer-row-inner"></div></div></div>')
            .attr('data-sstype', this.type);
    };

    Row.prototype.historyDeleteCol = function (historicalRow, historicalCol) {
        historicalCol.getSelf().delete();
    };

    Row.prototype.historyCreateCol = function (historicalRow, historicalCol) {
        var newCol = historicalRow.getSelf().createCol();
        historicalCol.setSelf(newCol);
    };

    Row.prototype.createCol = function () {

        var col = new _N2.Col(this.fragmentEditor, this, {});
        _N2.History.get().addSimple(this, this.historyDeleteCol, this.historyCreateCol, [this, col]);
        col.create(col._createLayer());

        if (this.isReady()) {
            this.placement.updatePosition();
        }

        return col;
    };

    Row.prototype.createRow = function () {
        this.$row = this.layer.find('.n2-ss-layer-row:first');
        this.$rowInner = this.$row.find('.n2-ss-layer-row-inner:first');

        //Fix for Slide Library 3.2 --> 3.3 change
        if (this.$rowInner.length === 0) {
            this.$rowInner = $('<div class="n2-ss-layer-row-inner"></div>').append(this.$row.find('> *')).appendTo(this.$row);
        }

        this.container = new _N2.LayerContainer(this);
        this.container.setLayerContainerElement(this.$rowInner);

        this.addClassElement(this.layer);
        this.addClassElement(this.$row, '-inner');

        this._createLayerListRow();

        var $buttonOpenClose = $('<div class="n2_layer_navigation_list_layer__title_label_folder" data-action="open-close"><i class="ssi_16"></i></div>')
            .prependTo(this.$rowLabel)
            .on('click', this.switchOpened.bind(this));

        _N2._tooltip.addElement($buttonOpenClose, n2_('Open/Close'));

        this.container.$ul.appendTo(this.layerRow);

        this.readyDeferred.done(this._syncopened.bind(this));
    };

    Row.prototype.activate = function () {
        _N2.ComponentCommonAbstract.prototype.activate.apply(this, arguments);

        this.columnsField.setRow(this);

        this.uiColumns.setOption('active', 1);
    };

    Row.prototype.deActivate = function () {

        this.uiColumns.setOption('active', 0);

        _N2.ComponentCommonAbstract.prototype.deActivate.apply(this, arguments);
    };

    Row.prototype.switchOpened = function (e) {
        e.preventDefault();

        _N2.WindowManager.actionPrevent('ComponentRowActivate');

        if (!this.layerRow.hasClass('n2_layer_navigation_list_layer--closed')) {
            this.setProperty('opened', 0);
        } else {
            this.setProperty('opened', 1);
        }
    };

    Row.prototype._syncopened = function () {
        if (this.getProperty('opened')) {
            this.layerRow.removeClass('n2_layer_navigation_list_layer--closed');
            this.container.$ul.css('display', '');

            this.layer.triggerHandler('opened');
        } else {
            this.layerRow.addClass('n2_layer_navigation_list_layer--closed');
            this.container.$ul.css('display', 'none');

            this.layer.triggerHandler('closed');
        }
    };

    Row.prototype.forceOpen = function () {
        if (this.layerRow.hasClass('n2_layer_navigation_list_layer--closed')) {
            this.layerRow.removeClass('n2_layer_navigation_list_layer--closed');
            this.container.$ul.css('display', '');

            this.layer.triggerHandler('opened');
        }
    };

    Row.prototype.removeForceOpen = function () {
        if (!this.getProperty('opened') && !this.layerRow.hasClass('n2_layer_navigation_list_layer--closed')) {
            this.layerRow.addClass('n2_layer_navigation_list_layer--closed');
            this.container.$ul.css('display', 'none');

            this.layer.triggerHandler('closed');
        }
    };

    /**
     *
     * @returns {_N2.Col[]}
     */
    Row.prototype.getSortedColumns = function () {
        var sortedLayers = this.container.getSortedLayers(),
            sortedColumns = [];

        for (var i = 0; i < sortedLayers.length; i++) {
            if (sortedLayers[i] instanceof _N2.Col) {
                sortedColumns.push(sortedLayers[i]);
            }
        }

        return sortedColumns;
    };

    Row.prototype.getColumns = function () {
        var layers = this.getSortedColumns(),
            columns = [];
        for (var i = 0; i < layers.length; i++) {
            columns.push(layers[i].getProperty('colwidth'));
        }
        return columns.join('+');
    };

    Row.prototype.getColumnsOrdered = function () {
        var layers = this.getOrderedColumns(),
            columns = [];
        for (var i = 0; i < layers.length; i++) {
            columns.push(layers[i].getProperty('colwidth'));
        }
        return columns.join('+');
    };

    Row.prototype.getPadding = function () {
        return this.getProperty('padding');
    };

    Row.prototype._syncpadding = function () {
        var padding = this.getPadding().split('|*|'),
            unit = 'px';

        padding.splice(4); // drop legacy unit

        var css = padding.join(unit + ' ') + unit;
        this.$row.css('padding', css);
        if (this.uiSpacing) {
            this.uiSpacing.setOption('current', css);
        }

        this.update();
    };

    Row.prototype.refreshColumnsWidth = function () {
        var wrapAfter = parseInt(this.getProperty('wrapafter')),
            gutter = this.getGutter(),
            sideGutter = gutter / 2,
            columns = this.getOrderedColumnsVisible(),
            i;

        this.$rowInner.css({
            width: 'calc(100% + ' + (gutter + 1) + 'px)', /** +1 to fix Safari line break */
            margin: '-' + sideGutter + 'px'
        });
        for (i = 0; i < columns.length; i++) {
            columns[i].layer.css('margin', sideGutter + 'px');
        }

        if (wrapAfter > 0 || !this.isFullWidth()) {
            this.$row.attr('data-row-wrapped', '1');
            this.$rowInner.css('flex-wrap', 'wrap');

            if (this.isFullWidth() && wrapAfter <= columns.length) {
                var rows = new Array(Math.ceil(columns.length / wrapAfter)).fill(0),
                    rowIndex;
                for (i = 0; i < columns.length; i++) {
                    rowIndex = Math.floor(i / wrapAfter);
                    rows[rowIndex] += columns[i].getWidthPercentage();
                }

                for (i = 0; i < columns.length; i++) {
                    rowIndex = Math.floor(i / wrapAfter);

                    columns[i].layer.css('width', 'calc(' + (columns[i].getWidthPercentage() / rows[rowIndex] * 100) + '% - ' + gutter + 'px)');
                }
            } else {
                for (i = 0; i < columns.length; i++) {
                    columns[i].layer.css('width', 'auto');
                }
            }
        } else {
            this.$row.attr('data-row-wrapped', '0');
            this.$rowInner.css('flex-wrap', '');
            if (this.isFullWidth()) {
                for (i = 0; i < columns.length; i++) {
                    columns[i].layer.css('width', columns[i].getWidthPercentage() + '%');
                }
            } else {
                for (i = 0; i < columns.length; i++) {
                    columns[i].layer.css('width', 'auto');
                }
            }
        }
    }

    Row.prototype.getGutter = function () {
        return this.getProperty('gutter');
    };

    Row.prototype._syncgutter = function () {

        this.refreshColumnsWidth();

        this.update();
    };

    Row.prototype._syncwrapafter = function () {

        if (!this.isDeleted && !this.isDeleteStarted) {

            this.refreshColumnsWidth();

            this.update();
        }
    };

    Row.prototype.getOrderedColumns = function () {
        return this.getSortedColumns().sort(function (a, b) {
            return a.getRealOrder() - b.getRealOrder();
        });
    };

    Row.prototype.getOrderedColumnsVisible = function () {

        return this.getOrderedColumns().filter(function (col) {

            return col.showsOnCurrent;
        });
    }

    Row.prototype.getInnerAlign = function () {
        return this.getProperty('inneralign');
    };

    Row.prototype._syncinneralign = function () {

        _N2.ComponentAbstract.innerAlignToStyle(this.$row, this.getInnerAlign());

        this.refreshTextAlign();
    };

    Row.prototype._syncfullwidth = function () {

        this.layer.toggleClass('n2-ss-layer--block', this.isFullWidth());
        this.layer.toggleClass('n2-ss-layer--auto', !this.isFullWidth());

        this.refreshColumnsWidth();

        this.update();
    };

    Row.prototype.isFullWidth = function () {
        return parseInt(this.getProperty('fullwidth')) === 1;
    }

    Row.prototype._syncstretch = function () {
        this.layer.toggleClass('n2-ss-stretch-layer', this.getProperty('stretch') == 1);
    };

    Row.prototype._syncborderwidth =
        Row.prototype._syncbordercolor =
            Row.prototype._syncborderstyle =
                Row.prototype['_syncborderstyle-hover'] =
                    Row.prototype['_syncbordercolor-hover'] =
                        Row.prototype['_syncborderwidth-hover'] = function () {
                            this._syncborder();
                        };

    Row.prototype._syncborder = function () {
        var borderstyle = this.getProperty('borderstyle'),
            bordercolor = this.getProperty('bordercolor'),
            borderwidth = this.getProperty('borderwidth');

        this.addLocalStyle('normal', 'border', this.getBorderCSS(borderstyle, bordercolor, borderwidth));

        var hoverStyle = '',
            isHoverDifferent = false,
            borderstyleHover = this.getProperty('borderstyle-hover'),
            bordercolorHover = this.getProperty('bordercolor-hover'),
            borderwidthHover = this.getProperty('borderwidth-hover');

        if (borderstyleHover !== undefined && borderstyleHover != borderstyle) {
            isHoverDifferent = true;
        }
        if (bordercolorHover !== undefined && bordercolorHover != bordercolor) {
            isHoverDifferent = true;
        }
        if (borderwidthHover !== undefined && borderwidthHover != borderwidth) {
            isHoverDifferent = true;
        }

        if (isHoverDifferent) {
            if (borderstyleHover === undefined) {
                borderstyleHover = borderstyle;
            }
            if (bordercolorHover === undefined) {
                bordercolorHover = bordercolor;
            }
            if (borderwidthHover === undefined) {
                borderwidthHover = borderwidth;
            }
            hoverStyle = this.getBorderCSS(borderstyleHover, bordercolorHover, borderwidthHover);
        }
        this.addLocalStyle('hover', 'border', hoverStyle);

        this.update();
    };

    Row.prototype.getBorderCSS = function (borderStyle, borderColor, borderWidth) {
        var style = '';
        if (borderStyle !== 'none') {
            style += 'border-color:' + N2Color.hex2rgbaCSS(borderColor) + ';';
            style += 'border-style:' + borderStyle + ';';
            borderWidth = borderWidth.split('|*|');
            var unit = 'px';

            style += 'border-width:' + borderWidth.join(unit + ' ') + unit + ';';
        }
        return style;
    };

    Row.prototype._syncborderradius =
        Row.prototype['_syncborderradius-hover'] = function () {
            var borderRadius = this.getProperty('borderradius');
            if (borderRadius >= 0) {
                this.addLocalStyle('normal', 'borderradius', 'border-radius:' + borderRadius + 'px;');
            }

            var borderRadiusHover = this.getProperty('borderradius-hover');
            if (borderRadiusHover && borderRadiusHover != borderRadius) {
                this.addLocalStyle('hover', 'borderradius', 'border-radius:' + borderRadiusHover + 'px;');
            } else {
                this.addLocalStyle('hover', 'borderradius', '');
            }
        };

    Row.prototype._syncboxshadow =
        Row.prototype['_syncboxshadow-hover'] = function () {
            var boxShadow = this.getProperty('boxshadow');
            this.addLocalStyle('normal', 'boxshadow', this.getBoxShadowCSS(boxShadow.split('|*|')));

            var hoverStyle = '',
                boxShadowHover = this.getProperty('boxshadow-hover');
            if (boxShadowHover !== undefined && boxShadowHover != boxShadow) {
                hoverStyle = this.getBoxShadowCSS(boxShadowHover.split('|*|'));
            }
            this.addLocalStyle('hover', 'boxshadow', hoverStyle);
        };

    Row.prototype.getBoxShadowCSS = function (boxShadow) {
        if ((boxShadow[0] != 0 || boxShadow[1] != 0 || boxShadow[2] != 0 || boxShadow[3] != 0) && N2Color.hex2alpha(boxShadow[4]) != 0) {
            return 'box-shadow:' + boxShadow[0] + 'px ' + boxShadow[1] + 'px ' + boxShadow[2] + 'px ' + boxShadow[3] + 'px ' + N2Color.hex2rgbaCSS(boxShadow[4]) + ';';
        }
        return '';
    };

    Row.prototype._synchref =
        Row.prototype['_synchref-target'] =
            Row.prototype['_syncaria-label'] = function () {
            };

    Row.prototype._syncbgimage =
        Row.prototype._syncbgimagex =
            Row.prototype._syncbgimagey =
                Row.prototype._syncbgcolor =
                    Row.prototype._syncbgcolorgradient =
                        Row.prototype._syncbgcolorgradientend =
                            Row.prototype['_syncbgcolor-hover'] =
                                Row.prototype['_syncbgcolorgradient-hover'] =
                                    Row.prototype['_syncbgcolorgradientend-hover'] = function () {
                                        this._syncbgThrottled();
                                    };


    Row.prototype._syncbgThrottled = function () {

        var backgroundStyle = '',
            image = this.fragmentEditor.editor.generator.fill(this.getProperty('bgimage'));
        if (image !== '') {
            var x = parseInt(this.getProperty('bgimagex'));
            if (!isFinite(x)) {
                x = 50;
            }
            var y = parseInt(this.getProperty('bgimagey'));
            if (!isFinite(y)) {
                y = 50;
            }
            backgroundStyle += '--n2bgimage:URL("' + _N2._imageHelper.fixed(image) + '");';
            backgroundStyle += 'background-position:50% 50%,' + x + '% ' + y + '%;';
        }
        var color = this.fragmentEditor.editor.generator.fill(this.getProperty('bgcolor')),
            gradient = this.getProperty('bgcolorgradient'),
            colorend = this.fragmentEditor.editor.generator.fill(this.getProperty('bgcolorgradientend')),
            normalStyle = this.getBackgroundCSS(color, gradient, colorend, backgroundStyle) + backgroundStyle;

        this.addLocalStyle('normal', 'bgcolor', normalStyle);


        var hoverStyle = '',
            isHoverDifferent = false,
            colorHover = this.fragmentEditor.editor.generator.fill(this.getProperty('bgcolor-hover')),
            gradientHover = this.getProperty('bgcolorgradient-hover'),
            colorendHover = this.fragmentEditor.editor.generator.fill(this.getProperty('bgcolorgradientend-hover'));

        if (colorHover !== undefined && colorHover != color) {
            isHoverDifferent = true;
        }
        if (gradientHover !== undefined && gradientHover != gradient) {
            isHoverDifferent = true;
        }
        if (colorendHover !== undefined && colorendHover != colorend) {
            isHoverDifferent = true;
        }

        if (isHoverDifferent) {
            if (colorHover === undefined) {
                colorHover = color;
            }
            if (gradientHover === undefined) {
                gradientHover = gradient;
            }
            if (colorendHover === undefined) {
                colorendHover = colorend;
            }
            hoverStyle = this.getBackgroundCSS(colorHover, gradientHover, colorendHover, backgroundStyle) + backgroundStyle;
        }
        this.addLocalStyle('hover', 'bgcolor', hoverStyle);
    };

    Row.prototype.getBackgroundCSS = function (color, gradient, colorend, backgroundStyle) {
        if (N2Color.hex2alpha(color) !== 0 || (gradient !== 'off' && N2Color.hex2alpha(colorend) !== 0)) {
            switch (gradient) {
                case 'horizontal':
                    return '--n2bggradient:linear-gradient(to right, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorend) + ' 100%);';
                case 'vertical':
                    return '--n2bggradient:linear-gradient(to bottom, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorend) + ' 100%);';
                case 'diagonal1':
                    return '--n2bggradient:linear-gradient(45deg, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorend) + ' 100%);';
                case 'diagonal2':
                    return '--n2bggradient:linear-gradient(135deg, ' + N2Color.hex2rgbaCSS(color) + ' 0%,' + N2Color.hex2rgbaCSS(colorend) + ' 100%);';
                case 'off':
                default:
                    if (backgroundStyle !== '') {
                        return "--n2bggradient: linear-gradient(" + N2Color.hex2rgbaCSS(color) + ", " + N2Color.hex2rgbaCSS(color) + ');';
                    }
                    return 'background-color:' + N2Color.hex2rgbaCSS(color) + ';';
            }
        }

        return '';
    };

    Row.prototype.getData = function (params) {
        var data = _N2.ComponentCommonAbstract.prototype.getData.call(this, params);

        if (params.itemsIncluded) {
            data.cols = this.container.getData(params);
        }
        return data;
    };

    /**
     * Example: Cols: 0 - 1 - 2
     * oldIndex: 0, newIndex: 2 => 1 - 2 - 0 Moves the col #0 to after the #2 col
     * @param oldIndex
     * @param newIndex
     */
    Row.prototype.moveCol = function (oldIndex, newIndex) {

        this._moveCol(oldIndex, newIndex);

        var task = _N2.History.get().addValue(this, this.historyMoveCol, []);
        if (task) {
            task.setValues({
                oldIndex: newIndex,
                newIndex: oldIndex
            }, {
                oldIndex: oldIndex,
                newIndex: newIndex
            });
        }
    };

    /**
     * Example: Cols: 0 - 1 - 2
     * oldIndex: 0, newIndex: 2 => 1 - 2 - 0 Moves the col #0 to after the #2 col
     * @param oldColIndex
     * @param newColIndex
     */
    Row.prototype.moveColOrOrder = function (oldColIndex, newColIndex) {
        var i;

        if (this.getMode() === 'desktopPortrait') {
            /**
             * Change column order in desktopPortrait results layer ordering, so we need to update col index to layer index.
             * There might be absolute layers before columns which results in shifted col indexes.
             */
            var oldIndex, newIndex,
                layers = this.container.getSortedLayers(),
                columns = this.getSortedColumns(),
                oldColumn = columns[oldColIndex],
                newColumn = columns[newColIndex];
            /**
             * We must translate col index to layer index!
             */
            for (i = 0; i < layers.length; i++) {
                if (layers[i] === oldColumn) {
                    oldIndex = i;
                    break;
                }
            }
            for (i = 0; i < layers.length; i++) {
                if (layers[i] === newColumn) {
                    newIndex = i;
                    break;
                }
            }

            this.moveCol(oldIndex, newIndex);
        } else {
            /**
             * On non desktopPortrait devices, we just set the ordering of the columns.
             */
            var orderedColumns = this.getOrderedColumns(),
                colToMove = orderedColumns[oldColIndex];

            orderedColumns.splice(oldColIndex, 1);
            orderedColumns.splice(newColIndex, 0, colToMove);
            for (i = 0; i < orderedColumns.length; i++) {
                orderedColumns[i].setProperty('order', i + 1);
            }
            this.refreshUI();
        }
    };

    Row.prototype._moveCol = function (oldIndex, newIndex) {

        var layers = this.container.getSortedLayers();
        this.container.insertLayerAt(layers[oldIndex], newIndex);

        this.refreshUI();
    };

    Row.prototype.historyMoveCol = function (data) {

        this._moveCol(data.oldIndex, data.newIndex);
    };

    /**
     * After sidebar resize ends. Store the widths.
     * @param fractions
     */
    Row.prototype.setRealColsWidth = function (fractions) {

        var cols = this.getOrderedColumns();
        for (var i = 0; i < fractions.length; i++) {
            cols[i].setProperty('colwidth', fractions[i].toFraction());
        }

        this.refreshColumnsWidth();
        this.update();

        this.refreshUI();
    };

    /**
     * During resize
     *
     * @param {float[]} widths
     */
    Row.prototype.updateColumnWidth = function (widths) {

        var columns = this.getOrderedColumns(),
            storedWidths = [],
            i;

        for (i = 0; i < columns.length; i++) {
            storedWidths.push(columns[i].getWidthPercentage());

            columns[i].widthPercentage = widths[i] * 100;
        }

        this.refreshColumnsWidth();

        /**
         * Restore widths
         */
        for (i = 0; i < columns.length; i++) {
            columns[i].widthPercentage = storedWidths[i];
        }

        this.update();
    };

    Row.prototype.activateColumn = function (index, e) {
        this.container.getSortedLayers()[index].activate(e);
    };

    Row.prototype.onChildCountChange = function () {
        if (!this.isDeleted && !this.isDeleteStarted) {
            var layers = this.getSortedColumns(),
                colLength = layers.length;
            if (colLength) {
                var currentColumns = this.getColumns().split('+'),
                    add = 0;
                for (var i = 0; i < currentColumns.length; i++) {
                    add = (new Fraction(currentColumns[i])).add(add);
                }
                if (add.valueOf() != 1) {
                    for (var i = 0; i < colLength; i++) {
                        layers[i].setProperty('colwidth', "1/" + colLength);
                    }
                } else {

                    for (var i = 0; i < colLength; i++) {
                        layers[i]._synccolwidth();
                    }
                }
                this.refreshUI();
            }
            this._syncgutter();
        }
    };

    Row.prototype.renderModeProperties = function (isReset) {
        _N2.ComponentCommonAbstract.prototype.renderModeProperties.call(this, isReset);

        this._syncpadding();
        this._syncinneralign();
        this._syncgutter();

        if (this.isActive) {
            this.columnsField.setRow(this);
        }
    };

    Row.prototype.highlightStructure = function (hideInterval) {

        hideInterval = hideInterval || 4000;
        if (this.isStructureHighlighted) {
            clearTimeout(this.isStructureHighlighted);
            this.isStructureHighlighted = false;
        }

        var columns = this.getSortedColumns();

        this.layer.addClass('n2_layer_highlight_row_structure');
        for (var i = 0; i < columns.length; i++) {
            columns[i].layer.addClass('n2_layer_highlight_row_structure');
        }
        this.isStructureHighlighted = setTimeout((function () {
            if (!this.isDeleted) {
                this.layer.removeClass('n2_layer_highlight_row_structure');
                for (var i = 0; i < columns.length; i++) {
                    if (!columns[i].isDeleted) {
                        columns[i].layer.removeClass('n2_layer_highlight_row_structure');
                    }
                }
            }
        }).bind(this), hideInterval);
    };

    Row.prototype.refreshUI = function () {
        if (!this.isDeleteStarted) {
            if (this.isActive) {
                this.columnsField.setRow(this);
            }
            this.refreshColumnsWidth();
            this.uiColumns.setOption('columns', this.getColumnsOrdered());
        }
    };

    Row.prototype.getLLDroppable = function (layer) {

        if (this.getProperty('opened')) {
            var layerPlacement = layer.placement.getType();
            if (layerPlacement === 'absolute') {
                return {
                    $container: this.container.$ul,
                    layer: this
                };
            }

            switch (layer.type) {
                case 'col':
                    if (layer.group === this) {
                        return {
                            $container: this.container.$ul,
                            layer: this
                        };
                    }
                    break;
            }
        }

        return false;
    };

    Row.prototype.getContents = function () {
        return this.$row;
    };

    Row.prototype.setPropertystylemode = function (name, value, from) {
        this.stylemode = value;

        this.syncAdvancedField('bgcolor');
        this.syncAdvancedField('bgcolorgradient');
        this.syncAdvancedField('bgcolorgradientend');
        this.syncAdvancedField('borderradius');
        this.syncAdvancedField('boxshadow');
        this.syncAdvancedField('borderwidth');
        this.syncAdvancedField('borderstyle');
        this.syncAdvancedField('bordercolor');
    };

    Row.prototype.onSyncFields = function () {
        this.fragmentEditor.layerOptions.updateField('stylemode', this.stylemode);
    };

    /**
     * @returns {[]}
     */
    Row.prototype.getContextMenuItems = function () {
        var items = _N2.ComponentCommonAbstract.prototype.getContextMenuItems.call(this);

        items.push({
            priority: 55,
            label: n2_('Duplicate'),
            icon: 'ssi_16 ssi_16--duplicate',
            action: this.duplicate.bind(this)
        });

        items.push({
            priority: 1000,
            label: n2_('Delete'),
            icon: 'ssi_16 ssi_16--delete',
            action: this.delete.bind(this),
            color: 'red'
        });

        return items;
    };

    Row.prototype.getContextMenuCopySubItems = function () {
        var items = _N2.ComponentCommonAbstract.prototype.getContextMenuCopySubItems.call(this);

        items.unshift({
            label: this.label,
            icon: 'ssi_16 ssi_16--layer',
            action: (function () {
                this.fragmentEditor.clipboard.componentCopy(this);
            }).bind(this)
        });

        return items;
    };

    return Row;
});_N2.d('SectionSlide', ['$', 'LayerContainer', 'ComponentAbstract'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     *
     * @constructor
     * @augments {_N2.ComponentAbstract}
     */
    function SectionSlide(fragmentEditor) {
        this.label = n2_('Slide');
        this.type = 'slide';

        this.contextualBarPosition = 'center';

        this.$ = fragmentEditor.$;

        this.isMainGroup = true;

        _N2.ComponentAbstract.prototype.constructor.call(this, fragmentEditor, false);

        this.placement.allow('default');

        this.layer = this.fragmentEditor.editor.getMainContainerElement().find('.n2-ss-layer[data-sstype="slide"]');

        this.layerListUIManager = new _N2.UILayerList(this.layer, {
            mainContainer: this,
            $scrolled: $('.n2_layer_navigation_list')
        });
    }

    SectionSlide.prototype = Object.create(_N2.ComponentAbstract.prototype);
    SectionSlide.prototype.constructor = SectionSlide;

    SectionSlide.prototype.getPaddingElement = function () {
        return this.layer;
    };

    SectionSlide.prototype.lateInit = function () {

        this.load(this.layer, true);

        this.slideBackground = this.fragmentEditor.editor.getSlideBackground();
        if (this.slideBackground) {
            this.slideBackground.setRelatedSectionSlide(this);
        }
    };
    /**
     *
     * @param {_N2.ComponentSettings} componentSettings
     * @returns {{}}
     */
    SectionSlide.getRegistryDataToFields = function (componentSettings) {

        var availableDeviceModes = componentSettings.fragmentEditor.editor.getAvailableDeviceModes(),
            registry = {
                title: $('#layerslide-title'),
                publish_up: $('#layerslide-publish_up'),
                publish_down: $('#layerslide-publish_down'),
                published: $('#layerslide-published'),
                description: $('#layerslide-description'),
                thumbnail: $('#layerslide-thumbnail'),
                thumbnailAlt: $('#layerslide-thumbnailAlt'),
                thumbnailTitle: $('#layerslide-thumbnailTitle'),

                'record-slides': $('#layerslide-slide-generator-slides'),

                thumbnailType: $('#layerslide-thumbnailType'),
                ligthboxImage: $('#layerslide-ligthboxImage'),

                'background-type': $('#layerslide-background-type'),
                backgroundImage: $('#layerslide-backgroundImage'),
                backgroundFocusX: $('#layerslide-backgroundFocusX'),
                backgroundFocusY: $('#layerslide-backgroundFocusY'),
                backgroundImageOpacity: $('#layerslide-backgroundImageOpacity'),
                backgroundImageBlur: $('#layerslide-backgroundImageBlur'),
                backgroundAlt: $('#layerslide-backgroundAlt'),
                backgroundTitle: $('#layerslide-backgroundTitle'),
                backgroundColor: $('#layerslide-backgroundColor'),
                backgroundGradient: $('#layerslide-backgroundGradient'),
                backgroundColorEnd: $('#layerslide-backgroundColorEnd'),
                backgroundColorOverlay: $('#layerslide-backgroundColorOverlay'),
                backgroundMode: $('#layerslide-backgroundMode'),
                backgroundBlurFit: $('#layerslide-backgroundBlurFit'),

                href: $('#layerslide-href'),
                'href-target': $('#layerslide-href-target'),
                'aria-label': $('#layerslide-aria-label'),
                'slide-duration': $('#layerslide-slide-duration'),

                padding: $('#layerslide-padding'),
            };

        for (var i = 0; i < SectionSlide.externalDataToField.length; i++) {
            registry[SectionSlide.externalDataToField[i].name] = $('#' + SectionSlide.externalDataToField[i].id);
        }

        componentSettings.fragmentEditor.editor.generator.registerField(registry.title);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.description);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.thumbnail);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.thumbnailAlt);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.thumbnailTitle);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.backgroundImage);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.backgroundFocusX);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.backgroundFocusY);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.backgroundAlt);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.backgroundTitle);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.backgroundColor);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.backgroundColorEnd);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.href);
        componentSettings.fragmentEditor.editor.generator.registerField(registry['aria-label']);
        componentSettings.fragmentEditor.editor.generator.registerField(registry.backgroundVideoMp4);

        return registry;
    };

    SectionSlide.externalDataToField = [];

    SectionSlide.addExternalDataToField = function (externalData) {
        SectionSlide.externalDataToField.push.apply(SectionSlide.externalDataToField, externalData);
    };

    SectionSlide.prototype.getIcon = function () {
        return 'ssi_16 ssi_16--image';
    };

    SectionSlide.prototype.addProperties = function ($layer) {

        this.createProperty('title', n2_('Slide'), $layer);
        this.createProperty('publish_up', '0000-00-00 00:00:00', $layer);
        this.createProperty('publish_down', '0000-00-00 00:00:00', $layer);
        this.createProperty('published', 1, $layer);
        this.createProperty('description', '', $layer);

        this.createProperty('record-slides', 5, $layer);

        this.createProperty('thumbnail', '', $layer);
        this.createProperty('thumbnailAlt', '', $layer);
        this.createProperty('thumbnailTitle', '', $layer);
        this.createProperty('thumbnailType', 'default', $layer);

        this.createProperty('static-slide', 0, $layer);
        this.createProperty('slide-duration', 0, $layer);
        this.createProperty('ligthboxImage', '', $layer);

        for (var i = 0; i < SectionSlide.externalDataToField.length; i++) {
            this.createProperty(SectionSlide.externalDataToField[i].name, SectionSlide.externalDataToField[i].def, $layer);
        }

        _N2.ComponentAbstract.prototype.addProperties.call(this, $layer);

        this.createProperty('href', '', $layer);
        this.createProperty('href-target', '', $layer);
        this.createProperty('aria-label', '', $layer);


        this.createProperty('background-type', 'color', $layer);

        this.createProperty('backgroundColor', 'ffffff00', $layer);
        this.createProperty('backgroundGradient', 'off', $layer);
        this.createProperty('backgroundColorEnd', 'ffffff00', $layer);
        this.createProperty('backgroundColorOverlay', 0, $layer);

        this.createProperty('backgroundImage', '', $layer);
        this.createProperty('backgroundFocusX', 50, $layer);
        this.createProperty('backgroundFocusY', 50, $layer);
        this.createProperty('backgroundImageOpacity', 100, $layer);
        this.createProperty('backgroundImageBlur', 0, $layer);
        this.createProperty('backgroundAlt', '', $layer);
        this.createProperty('backgroundTitle', '', $layer);
        this.createProperty('backgroundMode', 'default', $layer);
        this.createProperty('backgroundBlurFit', '7', $layer);

        this.createDeviceProperty('padding', {desktopPortrait: '10|*|10|*|10|*|10'}, $layer);

    };

    SectionSlide.prototype.load = function ($layer, isEditorStart) {

        _N2.ComponentAbstract.prototype.load.call(this, $layer, isEditorStart);

        this.initUI();

        this._syncpadding();

        this.container.startWithExistingNodes(isEditorStart);

        this._onReady();
    };

    SectionSlide.prototype.onSpacingStop = function (event, ui) {
        var padding = this.getPadding().split('|*|'),
            ratioH = 1,
            ratioV = 1;

        for (var k in ui.changed) {
            var value = ui.changed[k];
            switch (k) {
                case 'top':
                    padding[0] = Math.round(value);
                    break;
                case 'right':
                    padding[1] = Math.round(value);
                    break;
                case 'bottom':
                    padding[2] = Math.round(value);
                    break;
                case 'left':
                    padding[3] = Math.round(value);
                    break;
            }
        }
        this.setProperty('padding', padding.join('|*|'));
        $('#layerslide-padding').data('field').insideChange(padding.join('|*|'));
    };

    SectionSlide.prototype.initUI = function () {

        this.uiSpacing = new _N2.UISpacingPadding(this.getPaddingElement(), this.fragmentEditor, {
            stop: this.onSpacingStop.bind(this),
        });

        this.editorUI.padding = this.uiSpacing.$handles;
    };

    SectionSlide.prototype.createRow = function () {

        this.container = new _N2.LayerContainer(this);

        this._createLayerListRow();

        this.container.$ul.appendTo(this.layerRow);
    };

    SectionSlide.prototype._start = function (isCreate) {
        this.createRow();

        this.placement.start();
        this.placement.setMode('default', this.originalProperties);

        this.setGroup(this.group);


        this.fragmentEditor.$.triggerHandler('layerCreated', this);

        if (isCreate) {
            this.$.triggerHandler('created');
        }

        this.afterStart(isCreate);
    };

    SectionSlide.prototype.changeEditorMode = function (mode) {

        _N2.LayerDataStorage.prototype.changeEditorMode.call(this, mode);

        this.container.changeEditorModes(mode);
    };

    SectionSlide.prototype.getLayerListParent = function () {
        return $('.n2_layer_navigation_list_layers');
    };

    SectionSlide.prototype.setGroup = function (group) {

    };

    SectionSlide.prototype['_syncbackground-type'] = function () {
        this.slideBackground.setType(this.getProperty('background-type'));
    };

    SectionSlide.prototype.syncBackgroundColor = function () {

        var color = _N2._currentEditor.generator.fill(this.getProperty('backgroundColor')),
            gradient = this.getProperty('backgroundGradient'),
            colorEnd;
        if (gradient !== 'off') {
            colorEnd = _N2._currentEditor.generator.fill(this.getProperty('backgroundColorEnd'));
        }
        this.slideBackground.updateColor(color, gradient, colorEnd, this.getProperty('backgroundColorOverlay'));
    };

    SectionSlide.prototype._syncbackgroundColor = SectionSlide.prototype.syncBackgroundColor;
    SectionSlide.prototype._syncbackgroundGradient = SectionSlide.prototype.syncBackgroundColor;
    SectionSlide.prototype._syncbackgroundColorEnd = SectionSlide.prototype.syncBackgroundColor;
    SectionSlide.prototype._syncbackgroundColorOverlay = SectionSlide.prototype.syncBackgroundColor;

    SectionSlide.prototype._syncbackgroundImage = function (oldValue, from) {
        var image = this.getProperty('backgroundImage');
        this.slideBackground.setImage(_N2._currentEditor.generator.fill(image));
        this.slideBackground.setThumbnail(image, oldValue);
    };

    SectionSlide.prototype.syncBackgroundFocus = function () {
        this.slideBackground.setFocus(_N2._currentEditor.generator.fill(this.getProperty('backgroundFocusX')), _N2._currentEditor.generator.fill(this.getProperty('backgroundFocusY')));
    };

    SectionSlide.prototype._syncbackgroundFocusX = SectionSlide.prototype.syncBackgroundFocus;
    SectionSlide.prototype._syncbackgroundFocusY = SectionSlide.prototype.syncBackgroundFocus;

    SectionSlide.prototype._syncbackgroundImageOpacity = function () {

        this.slideBackground.setImageOpacity(this.getProperty('backgroundImageOpacity'));
    };

    SectionSlide.prototype._syncbackgroundImageBlur = function () {
        this.slideBackground.setBlur(this.getProperty('backgroundImageBlur'));
    };

    SectionSlide.prototype._syncbackgroundMode = function () {

        this.slideBackground.setMode(this.getProperty('backgroundMode'));
    };

    SectionSlide.prototype._syncbackgroundBlurFit = function () {
        this.slideBackground.setBlur(this.getProperty('backgroundBlurFit'), true);
    };

    SectionSlide.prototype.getPadding = function () {
        return this.getProperty('padding');
    };

    SectionSlide.prototype._syncpadding = function () {
        var padding = this.getPadding().split('|*|'),
            unit = 'px';

        var css = padding.join(unit + ' ') + unit;
        this.getPaddingElement().css('padding', css);
        this.update();

        if (this.uiSpacing) {
            this.uiSpacing.setOption('current', css);
        }
    };

    SectionSlide.prototype.syncShowOnDevice = function (mode, value) {

    };

    SectionSlide.prototype.renderModeProperties = function (isReset) {
        _N2.ComponentCommonAbstract.prototype.renderModeProperties.call(this, isReset);

        this._syncpadding();
    };

    SectionSlide.prototype.getData = function (params) {
        var data = {
            type: this.type
        };

        /**
         * @type {{}}
         */
        var property,
            properties = _Assign({}, this.property);

        // store the device based properties
        for (var device in this.deviceProperty) {
            for (property in this.deviceProperty[device]) {
                delete properties[property];
                var value = this.deviceProperty[device][property];
                if (value === undefined) {
                    continue;
                }

                data[device.toLowerCase() + property] = value;
            }
        }

        for (property in properties) {

            switch (property) {
                case "desktopPortrait":
                case "desktopLandscape":
                case "tabletPortrait":
                case "tabletLandscape":
                case "mobilePortrait":
                case "mobileLandscape":
                    data[property.toLowerCase()] = properties[property];
                    break;
                default:
                    data[property] = properties[property];
                    break;
            }
        }

        data.layers = this.container.getData(params);

        return data;
    };

    SectionSlide.prototype.getSectionData = function (params) {
        var data = {
            type: this.type
        };

        /**
         * @type {{}}
         */
        var sectionWideProperties = ['background-type', 'backgroundAlt', 'backgroundColor', 'backgroundColorEnd', 'backgroundColorOverlay', 'backgroundFocusX', 'backgroundFocusY', 'backgroundGradient', 'backgroundImage', 'backgroundImageBlur', 'backgroundImageOpacity', 'backgroundMode', 'backgroundBlurFit', 'backgroundTitle', 'backgroundVideoLoop', 'backgroundVideoMode', 'backgroundVideoMp4', 'backgroundVideoReset', 'backgroundVideoOpacity', 'href', 'href-target', 'aria-label'],
            sectionWideDevicesProperties = ['padding'],
            i, property, value;

        // store the device based properties
        for (var device in this.deviceProperty) {
            for (i = 0; i < sectionWideDevicesProperties.length; i++) {
                property = sectionWideDevicesProperties[i];
                value = this.deviceProperty[device][property];
                if (value === undefined) {
                    continue;
                }

                data[device.toLowerCase() + property] = value;
            }
        }
        for (i = 0; i < sectionWideProperties.length; i++) {
            property = sectionWideProperties[i];
            value = this.property[property];
            if (value === undefined) {
                continue;
            }

            data[property] = value;
        }

        data.layers = this.container.getData(params);

        return data;
    };

    SectionSlide.prototype.getLLDroppable = function (layer) {
        var layerPlacement = layer.placement.getType();

        if (layerPlacement === 'absolute' || layer.type === 'content') {
            switch (layer.type) {
                case 'layer':
                case 'row':
                case 'content':
                    return {
                        $container: this.container.$ul,
                        layer: this
                    };
            }
        }
        return false;
    };

    SectionSlide.prototype.update = function () {

    };

    SectionSlide.prototype.onChildCountChange = function () {

    };

    SectionSlide.prototype.markEnter = function (e) {

    };

    SectionSlide.prototype.markLeave = function (e) {

    };

    SectionSlide.prototype.getSelf = function () {
        return this;
    };

    SectionSlide.prototype.getLLDroppables = function (layer) {
        return this.container.getLLDroppables(layer);
    };

    SectionSlide.prototype.getDroppables = function (placement, exclude) {

        if (placement === 'absolute') {
            /**
             * If we move a canvas layer, it can only be part of the SectionSlide.
             */
            return [this.getDroppable()];
        } else {

            return this.container.getDroppables(exclude);
        }
    };

    SectionSlide.prototype.getDroppable = function () {
        return {
            $container: this.layer,
            layer: this,
            placement: 'absolute'
        }
    };

    SectionSlide.prototype.onResize = function (ratios) {
        var sortedLayerList = this.getEverySortedLayers();

        for (var i = 0; i < sortedLayerList.length; i++) {
            sortedLayerList[i].placement.doLinearResize(ratios);
        }
    };

    SectionSlide.prototype.getEverySortedLayers = function () {
        var list = this.container.getChildLayersRecursive(false),
            children = {};
        for (var i = list.length - 1; i >= 0; i--) {
            if (list[i].property.parentid !== undefined && list[i].property.parentid) {
                if (children[list[i].property.parentid] === undefined) {
                    children[list[i].property.parentid] = [];
                }
                children[list[i].property.parentid].push(list[i]);
                list.splice(i, 1);
            }
        }
        for (var i = 0; i < list.length; i++) {
            if (list[i].property.id !== undefined && list[i].property.id && children[list[i].property.id] !== undefined) {
                children[list[i].property.id].unshift(0);
                children[list[i].property.id].unshift(i + 1);
                list.splice.apply(list, children[list[i].property.id]);
                delete children[list[i].property.id];
            }
        }
        return list;
    };

    SectionSlide.prototype.getTargetGroupDefault = function () {

        var group = this.mainContent,
            activeLayer = this.fragmentEditor.getActiveLayer();
        if (this.fragmentEditor.isCol(activeLayer) || this.fragmentEditor.isContent(activeLayer)) {
            group = activeLayer;
        } else if (this.fragmentEditor.isRow(activeLayer)) {
            /**
             * First column in the group
             */
            group = activeLayer.container.getSortedLayers()[0];
        } else if (activeLayer.placement.getType() === 'normal') {
            group = activeLayer.group;
        }

        return group;
    };

    /**
     * @returns {_N2.SectionSlide}
     */
    SectionSlide.prototype.getTargetGroupAbsolute = function () {
        return this;
    };

    SectionSlide.prototype.setMainContent = function (layer) {
        /**
         * @type {_N2.Content}
         */
        this.mainContent = layer;
    };

    SectionSlide.prototype.layerDeleted = function (layer) {

        this._afterLayerDeletedDeBounced(layer);
    };

    SectionSlide.prototype._afterLayerDeletedDeBounced = NextendDeBounce(function (layer) {

        var activeLayer = this.fragmentEditor.getActiveLayer();

        if (!activeLayer || activeLayer.isDeleted) {
            this.fragmentEditor.resetActiveLayer();
        }
    }, 50);

    /**
     * Delete removes every layer on the slide
     */
    SectionSlide.prototype.delete = function () {
        var layers = this.container.getSortedLayers();
        for (var i = 0; i < layers.length; i++) {
            layers[i].delete();
        }
    };

    /**
     * Duplicate is not allowed on slide
     */
    SectionSlide.prototype.duplicate = function () {

    };

    SectionSlide.prototype.isAbsolute = function () {
        return false;
    };

    SectionSlide.prototype.getContextMenuCopySubItems = function () {
        var items = _N2.ComponentAbstract.prototype.getContextMenuCopySubItems.call(this);

        items.push({
            label: n2_('Slide'),
            icon: 'ssi_16 ssi_16--image',
            action: (function () {
                this.fragmentEditor.clipboard.sectionCopy();
            }).bind(this)
        });

        return items;
    };

    SectionSlide.prototype.getContextMenuPasteSubItems = function () {
        var items = _N2.ComponentAbstract.prototype.getContextMenuPasteSubItems.call(this);

        if (this.fragmentEditor.clipboard.sectionHasClipboard()) {

            items.unshift({
                label: n2_('Slide'),
                icon: 'ssi_16 ssi_16--image',
                action: (function () {
                    this.fragmentEditor.clipboard.sectionPaste();
                }).bind(this)
            });
        }

        return items;
    };

    SectionSlide.prototype.update = function () {
        this.fragmentEditor.editor.getMainContainerElement().triggerHandler('updateSize');
    };

    return SectionSlide;
});_N2.d('ComponentSettings', '$', function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @param {_N2.FragmentEditor} fragmentEditor
     * @constructor
     */
    function ComponentSettings(fragmentEditor) {

        this.fragmentEditor = fragmentEditor;

        this.componentType = undefined;
        this.placementType = undefined;

        $('html')
            .attr('data-component', '')
            .attr('data-placement', '');

        this.currentForm = {};

        this.forms = {
            'undefined': null,
            placement: {
                absolute: _N2.PlacementAbsolute.getRegistryDataToFields(this),
                normal: _N2.PlacementNormal.getRegistryDataToFields(this),
                default: {}
            },
            component: {
                slide: _N2.SectionSlide.getRegistryDataToFields(this),
                content: _N2.Content.getRegistryDataToFields(this),
                row: _N2.Row.getRegistryDataToFields(this),
                col: _N2.Col.getRegistryDataToFields(this),
                layer: {}
            },
            global: _N2.ComponentCommonAbstract.getRegistryDataToFields(this),
        };

        var fieldPositionDefault = $('#layerposition-default').data('field');
        fieldPositionDefault.element.on('outsideChange', (function () {
            fieldPositionDefault.insideChange('default');

            this.currentLayer.convertToAbsolute();

        }).bind(this));

        var fieldPositionAbsolute = $('#layerposition-absolute').data('field');
        fieldPositionAbsolute.element.on('outsideChange', (function () {
            fieldPositionAbsolute.insideChange('absolute');

            this.currentLayer.convertToNormal();
        }).bind(this));
    }

    ComponentSettings.prototype.initStyleMode = function (name, $styleMode, relatedSelectors) {

        /**
         * @type {_N2.FormElementList[]}
         */
        var relatedModeFields = [],
            $containers = $('.n2_ss_layer_window__tab_panel[data-panel="' + name + '"] .n2_fields_layer_window[data-fieldset-type="style-mode"]')
                .attr('data-state', '');

        for (var i = 0; i < relatedSelectors.length; i++) {
            relatedModeFields.push($(relatedSelectors[i]).data('field'));
        }

        for (var j = 0; j < relatedModeFields.length; j++) {
            relatedModeFields[j].element
                .on('outsideChange', (function (element) {
                    $styleMode.data('field').select
                        .val(element.val())
                        .trigger('change');
                }).bind(this, relatedModeFields[j].element));
        }

        $styleMode
            .on('nextendChange', (function () {
                var value = $styleMode.val();
                $containers.attr('data-state', value);

                for (var i = 0; i < relatedModeFields.length; i++) {
                    if (value !== relatedModeFields[i].element.val()) {
                        relatedModeFields[i].insideChange(value);
                    }
                }
            }).bind(this));

        $containers.find('.n2_form__table_label_field[data-style-mode-feature="reset-to-normal"] a')
            .on('click', (function (e) {
                e.preventDefault();
                this.resetStyleMode('stylemode');
            }).bind(this));
    };

    /**
     *
     * @param {_N2.ComponentAbstract} layer
     * @param componentType
     * @param componentSubType
     * @param placementType
     * @param properties
     */
    ComponentSettings.prototype.changeActiveComponent = function (layer, componentType, componentSubType, placementType, properties) {

        /**
         * @type {_N2.LayerWindowTab}
         */
        var tab = this.fragmentEditor.layerWindow.tab;

        if (this.currentLayer) {
            if (this.currentLayer.type === 'slide') {
                tab.displayTab('content');
            }
        }

        /**
         * @type {_N2.ComponentAbstract}
         */
        this.currentLayer = layer;

        $('html')
            .attr('data-component', componentType)
            .attr('data-component-sub', componentSubType);

        switch (componentType) {
            case 'slide':
                tab.setPanel(['slide'], ['content', 'style']);
                tab.displayTab('style');
                break;
            case 'content':
                tab.setPanel(['content', 'common'], ['content', 'style', 'animations']);
                break;
            case 'row':
                tab.setPanel(['row', 'common'], ['content', 'style', 'animations']);
                break;
            case 'col':
                tab.setPanel(['column', 'common'], ['content', 'style', 'animations']);
                break;
            case 'layer':
                tab.setPanel(['item/' + layer.item.type, 'item', 'common'], ['content', 'style', 'animations']);
                break;
        }
        this.componentType = componentType;


        this.changeActiveComponentPlacement(placementType);
        this.syncFields(properties);
    };

    ComponentSettings.prototype.changeActiveComponentPlacement = function (placementType, properties) {

        $('html').attr('data-placement', placementType);
        this.placementType = placementType;

        this.syncFields(properties);
    };

    ComponentSettings.prototype.syncFields = function (properties) {
        if (typeof properties === 'object') {
            this.currentForm = _Assign({}, this.forms.global, this.forms.component[this.componentType], this.forms.placement[this.placementType]);

            for (var name in properties) {
                if (properties[name] !== undefined) {

                    this.updateField(name, properties[name]);
                }
            }

            this.currentLayer.onSyncFields();

            for (var k in this.currentForm) {
                this.currentForm[k].off('.layeroptions').on('outsideChange.layeroptions', this.activeComponentPropertyChanged.bind(this, k));
            }
        }
    };

    ComponentSettings.prototype.onUpdateField = function (e, name, value) {
        if (e.target === this.currentLayer) {
            this.updateField(name, value);
        }
    };

    ComponentSettings.prototype.updateField = function (name, value) {

        var field;

        if (typeof this.currentLayer['formSet' + name] === 'function') {
            this.currentLayer['formSet' + name](this, value);
        } else if (this.currentLayer.advancedProperties[name]) {

            var advancedProperty = this.currentLayer.advancedProperties[name];
            if (advancedProperty.getName() === name) {
                var baseName = advancedProperty.getBaseName();
                if (this.currentForm[baseName]) {
                    field = this.currentForm[baseName].data('field');
                    if (field) {
                        if (value === undefined) {
                            value = this.currentLayer.property[baseName];
                        }
                        field.insideChange(value);
                    }
                }
            }
        } else if (this.currentForm[name] !== undefined) {
            field = this.currentForm[name].data('field');
            if (field) {
                field.insideChange(value);
            }
        }
    };

    ComponentSettings.prototype.activeComponentPropertyChanged = function (name, e) {
        if (this.currentLayer && !this.currentLayer.isDeleted) {
            this.updateLayerProperty(name);
        } else {
            var field = this.currentForm[name].data('field');
            if (field !== undefined && field !== null) {
                field.insideChange('');
            }
        }
    };

    ComponentSettings.prototype.updateLayerProperty = function (name) {
        var value = this.currentForm[name].val();
        this.currentLayer.setProperty(name, value, 'manager');
    };

    ComponentSettings.prototype.startFeatures = function () {
        /**
         * @type {_N2.LayerFeatures}
         */
        this.layerFeatures = new _N2.LayerFeatures(this.forms.placement.absolute, this.fragmentEditor);
    };

    ComponentSettings.prototype.resetStyleMode = function (name, e) {
        this.currentLayer.resetStyleMode(name);
    };

    return ComponentSettings;
});_N2.d('ItemButton', ['$', 'Item'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments _N2.Item
     */
    function ItemButton() {
        this.type = 'button';
        _N2.Item.prototype.constructor.apply(this, arguments);
    }

    ItemButton.prototype = Object.create(_N2.Item.prototype);
    ItemButton.prototype.constructor = ItemButton;

    ItemButton.prototype.getLabel = function () {

        return n2_('Button');
    };

    ItemButton.prototype.added = function () {
        this.needFill = ['content', 'class'];
        this.addedFont('link', 'font');
        this.addedStyle('button', 'style');

        this.generator.registerFields(['#item_buttoncontent', '#item_buttonhref', '#item_buttonclass']);
    };

    ItemButton.prototype.parseAll = function (data) {

        data.classes = '';

        var isFullWidth = parseInt(data.fullwidth) === 1;
        this.layer.layer.toggleClass('n2-ss-layer--auto', !isFullWidth);
        if (!isFullWidth) {
            data.classes += ' n2-ss-button-container--non-full-width';
        }

        if (parseInt(data.nowrap)) {
            data.classes += ' n2-ss-nowrap';
        }

        _N2.Item.prototype.parseAll.apply(this, arguments);
    };

    ItemButton.prototype._render = function (data) {
        var $node = $('<div class="n2-ss-button-container n2-ow ' + data.fontclass + ' ' + data.classes + '"></div>'),
            $link = $('<a href="#" onclick="return false;" class="' + data.styleclass + ' ' + data.class + ' n2-ow"></a>').appendTo($node),
            $label = $('<div><div>' + _filter_allowed_html(data.content) + '</div></div>').appendTo($link);

        this.$item.append($node);
    };

    return ItemButton;
});_N2.d('ItemHeading', ['$', 'Item'], function () {
    "use strict";
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments _N2.Item
     */
    function ItemHeading() {
        this.type = 'heading';
        _N2.Item.prototype.constructor.apply(this, arguments);
    }

    ItemHeading.prototype = Object.create(_N2.Item.prototype);
    ItemHeading.prototype.constructor = ItemHeading;

    ItemHeading.prototype.getLabel = function () {

        return n2_('Heading');
    };

    ItemHeading.prototype.getDefault = function () {
        return {
            href: '',
            font: '',
            style: ''
        }
    };

    ItemHeading.prototype.added = function () {
        this.needFill = ['heading', 'class'];

        this.addedFont('hover', 'font');
        this.addedStyle('heading', 'style');

        this.generator.registerFields(['#item_headingheading', '#item_headinghref', '#item_headingclass']);
    };

    ItemHeading.prototype.parseAll = function (data) {
        data.uid = $.fn.uid();
        var isFullWidth = parseInt(data.fullwidth) === 1;
        this.layer.layer.toggleClass('n2-ss-layer--auto', !isFullWidth);
        if (isFullWidth) {
            data.display = 'block';
        } else {
            data.display = 'inline-block';
        }

        data.extrastyle = parseInt(data.nowrap) ? 'white-space: nowrap;' : '';

        data.heading = $('<div>' + _filter_allowed_html(data.heading) + '</div>').html().replace(/\n/g, '<br>');
        data.priority = 2;
        data.class = '';
    

        _N2.Item.prototype.parseAll.apply(this, arguments);

        if (data['href'] == '#' || data['href'] == '') {
            data['afontclass'] = '';
            data['astyleclass'] = '';
        } else {
            data['afontclass'] = data['fontclass'];
            data['fontclass'] = '';
            data['astyleclass'] = data['styleclass'];
            data['styleclass'] = '';
        }
    };

    ItemHeading.prototype._render = function (data) {
        var $node = $('<div class="n2-ow"></div>'),
            $heading = $('<div id="' + data.uid + '" style="' + data.extrastyle + '"></div>')
                .addClass('n2-ow ' + data.fontclass + ' ' + data.styleclass + ' ' + data.class)
                .css({
                    display: data.display
                }).appendTo($node);

        if (data['href'] == '#' || data['href'] == '') {
            $heading.html(data.heading);
        } else {
            $heading.append($('<a style="display:' + data.display + ';" href="#" class="' + data.afontclass + ' ' + data.astyleclass + ' n2-ow" onclick="return false;">' + data.heading + '</a>'));
        }

        this.$item.append($node);
    };

    return ItemHeading;
});_N2.d('ItemImage', ['$', 'Item'], function () {
    var $ = _N2.$;
    "use strict";

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments _N2.Item
     */
    function ItemImage() {
        this.type = 'image';
        _N2.Item.prototype.constructor.apply(this, arguments);
    }

    ItemImage.prototype = Object.create(_N2.Item.prototype);
    ItemImage.prototype.constructor = ItemImage;

    ItemImage.prototype.getLabel = function () {

        return n2_('Image');
    };

    ItemImage.prototype.hasTextScale = function () {

        return false;
    };

    ItemImage.prototype.getDefault = function () {
        return {
            size: 'auto|*|auto',
            href: '',
            style: ''
        }
    };

    ItemImage.prototype.added = function () {
        this.needFill = ['image', 'cssclass'];

        this.generator.registerFields(['#item_imageimage', '#item_imagealt', '#item_imagetitle', '#item_imagehref', '#item_imagecssclass']);
    };

    ItemImage.prototype.parseAll = function (data) {
        var size = data.size.split('|*|');
        data.width = size[0];
        data.height = size[1];
        delete data.size;

        _N2.Item.prototype.parseAll.apply(this, arguments);

        if (data.image !== this.fragmentEditor.editor.generator.fill(this.values.image)) {
            data.image = _N2._imageHelper.fixed(data.image);

            if (this.layer.placement.getType() === 'absolute') {
                this.resizeLayerToImage(_N2._imageHelper.fixed(data.image));
            }
        } else {
            data.image = _N2._imageHelper.fixed(data.image);
        }

    };

    ItemImage.prototype.fitLayer = function () {
        if (this.layer.placement.getType() === 'absolute') {
            this.resizeLayerToImage(_N2._imageHelper.fixed(this.values.image));
        }
        return true;
    };

    ItemImage.prototype._render = function (data) {
        data.styleclass = '';
    
        var $node = $('<div class="' + data.styleclass + ' n2-ss-item-image-content n2-ss-item-content n2-ow-all"></div>'),
            $a = $node,
            $link = '';

        if (data['href'] !== '#' && data['href'] !== '') {
            $a = $link = $('<a href="#" onclick="return false;" style="background: none !important;"></a>').appendTo($node);
        }


        var $img = $('<img class="' + data.cssclass + '" src="' + data.image + '">').appendTo($a);

        if (data.width && data.width !== 'auto') {
            $img.css('width', data.width);
            if ($link !== '' && data.width.slice(-1) === '%') {
                $link.css('width', '100%');
            }
        }
        if (data.height && data.height !== 'auto') {
            $img.css('height', data.height);
        }


        this.$item.append($node);
    };

    return ItemImage;
});_N2.d('ItemText', ['$', 'Item'], function () {
    "use strict";
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments _N2.Item
     */
    function ItemText() {
        this.type = 'text';
        _N2.Item.prototype.constructor.apply(this, arguments);
    }

    ItemText.prototype = Object.create(_N2.Item.prototype);
    ItemText.prototype.constructor = ItemText;

    ItemText.prototype.getLabel = function () {

        return n2_('Text');
    };

    ItemText.prototype.getDefault = function () {
        return {
            content: '',
            'content-mobile-enabled': 0,
            contentmobile: '',
            'content-tablet-enabled': 0,
            contenttablet: '',
            font: '',
            style: ''
        }
    };

    ItemText.prototype.added = function () {
        this.needFill = ['content', 'contenttablet', 'contentmobile'];

        this.addedFont('paragraph', 'font');
        this.addedStyle('heading', 'style');

        this.generator.registerFields(['#item_textcontent', '#item_textcontenttablet', '#item_textcontentmobile']);
    };

    ItemText.prototype.parseAll = function (data) {
        _N2.Item.prototype.parseAll.apply(this, arguments);

        data['p'] = _wp_Autop(_filter_allowed_html(data['content'], '<p>'));
        data['ptablet'] = _wp_Autop(_filter_allowed_html(data['contenttablet'], '<p>'));
        data['pmobile'] = _wp_Autop(_filter_allowed_html(data['contentmobile'], '<p>'));
    };


    ItemText.prototype._render = function (data) {
        var hasMobile = false,
            hasTablet = false,
            attributes;

        if (parseInt(data['content-mobile-enabled']) === 1) {
            hasMobile = true;
            $('<div class="n2-ow n2-ow-all">' + data.pmobile + '</div>')
                .attr({
                    'data-hide-desktoplandscape': 1,
                    'data-hide-desktopportrait': 1,
                    'data-hide-tabletlandscape': 1,
                    'data-hide-tabletportrait': 1
                })
                .appendTo(this.$item);
        }

        if (parseInt(data['content-tablet-enabled']) === 1) {
            hasTablet = true;
            attributes = {
                'data-hide-desktoplandscape': 1,
                'data-hide-desktopportrait': 1
            };
            if (hasMobile) {
                attributes['data-hide-mobilelandscape'] = 1;
                attributes['data-hide-mobileportrait'] = 1;
            } else {
                hasMobile = true;
            }
            $('<div class="n2-ow n2-ow-all">' + data.ptablet + '</div>')
                .attr(attributes)
                .appendTo(this.$item);
        }

        attributes = {};
        if (hasMobile) {
            attributes['data-hide-mobilelandscape'] = 1;
            attributes['data-hide-mobileportrait'] = 1;
        }

        if (hasTablet) {
            attributes['data-hide-tabletlandscape'] = 1;
            attributes['data-hide-tabletportrait'] = 1;
        }

        $('<div class="n2-ow n2-ow-all">' + data.p + '</div>')
            .attr(attributes)
            .appendTo(this.$item);

        this.$item.find('p').addClass(data.fontclass + ' ' + data.styleclass);

    };

    function _wp_Autop(text) {
        var br = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
        var preTags = [];

        if (text.trim() === '') {
            return '';
        } // Just to make things a little easier, pad the end.


        text = text + '\n';
        /*
         * Pre tags shouldn't be touched by autop.
         * Replace pre tags with placeholders and bring them back after autop.
         */

        if (text.indexOf('<pre') !== -1) {
            var textParts = text.split('</pre>');
            var lastText = textParts.pop();
            text = '';

            for (var i = 0; i < textParts.length; i++) {
                var textPart = textParts[i];
                var start = textPart.indexOf('<pre'); // Malformed html?

                if (start === -1) {
                    text += textPart;
                    continue;
                }

                var name = '<pre wp-pre-tag-' + i + '></pre>';
                preTags.push([name, textPart.substr(start) + '</pre>']);
                text += textPart.substr(0, start) + name;
            }

            text += lastText;
        } // Change multiple <br>s into two line breaks, which will turn into paragraphs.


        text = text.replace(/<br\s*\/?>\s*<br\s*\/?>/g, '\n\n');
        var allBlocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)'; // Add a double line break above block-level opening tags.

        text = text.replace(new RegExp('(<' + allBlocks + '[\\s/>])', 'g'), '\n\n$1'); // Add a double line break below block-level closing tags.

        text = text.replace(new RegExp('(</' + allBlocks + '>)', 'g'), '$1\n\n'); // Standardize newline characters to "\n".

        text = text.replace(/\r\n|\r/g, '\n'); // Find newlines in all elements and add placeholders.

        text = replaceInHtmlTags(text, {
            '\n': ' <!-- wpnl --> '
        }); // Collapse line breaks before and after <option> elements so they don't get autop'd.

        if (text.indexOf('<option') !== -1) {
            text = text.replace(/\s*<option/g, '<option');
            text = text.replace(/<\/option>\s*/g, '</option>');
        }
        /*
         * Collapse line breaks inside <object> elements, before <param> and <embed> elements
         * so they don't get autop'd.
         */


        if (text.indexOf('</object>') !== -1) {
            text = text.replace(/(<object[^>]*>)\s*/g, '$1');
            text = text.replace(/\s*<\/object>/g, '</object>');
            text = text.replace(/\s*(<\/?(?:param|embed)[^>]*>)\s*/g, '$1');
        }
        /*
         * Collapse line breaks inside <audio> and <video> elements,
         * before and after <source> and <track> elements.
         */


        if (text.indexOf('<source') !== -1 || text.indexOf('<track') !== -1) {
            text = text.replace(/([<\[](?:audio|video)[^>\]]*[>\]])\s*/g, '$1');
            text = text.replace(/\s*([<\[]\/(?:audio|video)[>\]])/g, '$1');
            text = text.replace(/\s*(<(?:source|track)[^>]*>)\s*/g, '$1');
        } // Collapse line breaks before and after <figcaption> elements.


        if (text.indexOf('<figcaption') !== -1) {
            text = text.replace(/\s*(<figcaption[^>]*>)/, '$1');
            text = text.replace(/<\/figcaption>\s*/, '</figcaption>');
        } // Remove more than two contiguous line breaks.


        text = text.replace(/\n\n+/g, '\n\n'); // Split up the contents into an array of strings, separated by double line breaks.

        var texts = text.split(/\n\s*\n/).filter(Boolean); // Reset text prior to rebuilding.

        text = ''; // Rebuild the content as a string, wrapping every bit with a <p>.

        texts.forEach(function (textPiece) {
            text += '<p>' + textPiece.replace(/^\n*|\n*$/g, '') + '</p>\n';
        }); // Under certain strange conditions it could create a P of entirely whitespace.

        text = text.replace(/<p>\s*<\/p>/g, ''); // Add a closing <p> inside <div>, <address>, or <form> tag if missing.

        text = text.replace(/<p>([^<]+)<\/(div|address|form)>/g, '<p>$1</p></$2>'); // If an opening or closing block element tag is wrapped in a <p>, unwrap it.

        text = text.replace(new RegExp('<p>\\s*(</?' + allBlocks + '[^>]*>)\\s*</p>', 'g'), '$1'); // In some cases <li> may get wrapped in <p>, fix them.

        text = text.replace(/<p>(<li.+?)<\/p>/g, '$1'); // If a <blockquote> is wrapped with a <p>, move it inside the <blockquote>.

        text = text.replace(/<p><blockquote([^>]*)>/gi, '<blockquote$1><p>');
        text = text.replace(/<\/blockquote><\/p>/g, '</p></blockquote>'); // If an opening or closing block element tag is preceded by an opening <p> tag, remove it.

        text = text.replace(new RegExp('<p>\\s*(</?' + allBlocks + '[^>]*>)', 'g'), '$1'); // If an opening or closing block element tag is followed by a closing <p> tag, remove it.

        text = text.replace(new RegExp('(</?' + allBlocks + '[^>]*>)\\s*</p>', 'g'), '$1'); // Optionally insert line breaks.

        if (br) {
            // Replace newlines that shouldn't be touched with a placeholder.
            text = text.replace(/<(script|style).*?<\/\\1>/g, function (match) {
                return match[0].replace(/\n/g, '<WPPreserveNewline />');
            }); // Normalize <br>

            text = text.replace(/<br>|<br\/>/g, '<br />'); // Replace any new line characters that aren't preceded by a <br /> with a <br />.

            text = text.replace(/(<br \/>)?\s*\n/g, function (a, b) {
                return b ? a : '<br />\n';
            }); // Replace newline placeholders with newlines.

            text = text.replace(/<WPPreserveNewline \/>/g, '\n');
        } // If a <br /> tag is after an opening or closing block tag, remove it.


        text = text.replace(new RegExp('(</?' + allBlocks + '[^>]*>)\\s*<br />', 'g'), '$1'); // If a <br /> tag is before a subset of opening or closing block tags, remove it.

        text = text.replace(/<br \/>(\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)/g, '$1');
        text = text.replace(/\n<\/p>$/g, '</p>'); // Replace placeholder <pre> tags with their original content.

        preTags.forEach(function (preTag) {
            var name = preTag[0],
                original = preTag[1];

            text = text.replace(name, original);
        }); // Restore newlines in all elements.

        if (-1 !== text.indexOf('<!-- wpnl -->')) {
            text = text.replace(/\s?<!-- wpnl -->\s?/g, '\n');
        }

        return text;
    }

    /**
     * The regular expression for an HTML element.
     *
     * @type {RegExp}
     */
    var htmlSplitRegex = (function () {
        /* eslint-disable no-multi-spaces */
        var comments =
            '!' + // Start of comment, after the <.
            '(?:' + // Unroll the loop: Consume everything until --> is found.
            '-(?!->)' + // Dash not followed by end of comment.
            '[^\\-]*' + // Consume non-dashes.
            ')*' + // Loop possessively.
            '(?:-->)?'; // End of comment. If not found, match all input.

        var cdata =
            '!\\[CDATA\\[' + // Start of comment, after the <.
            '[^\\]]*' + // Consume non-].
            '(?:' + // Unroll the loop: Consume everything until ]]> is found.
            '](?!]>)' + // One ] not followed by end of comment.
            '[^\\]]*' + // Consume non-].
            ')*?' + // Loop possessively.
            '(?:]]>)?'; // End of comment. If not found, match all input.

        var escaped =
            '(?=' + // Is the element escaped?
            '!--' +
            '|' +
            '!\\[CDATA\\[' +
            ')' +
            '((?=!-)' + // If yes, which type?
            comments +
            '|' +
            cdata +
            ')';

        var regex =
            '(' + // Capture the entire match.
            '<' + // Find start of element.
            '(' + // Conditional expression follows.
            escaped + // Find end of escaped element.
            '|' + // ... else ...
            '[^>]*>?' + // Find end of normal element.
            ')' +
            ')';

        return new RegExp(regex);
        /* eslint-enable no-multi-spaces */
    })();

    /**
     * Separate HTML elements and comments from the text.
     *
     * @param  {string} input The text which has to be formatted.
     * @return {string[]}        The formatted text.
     */
    function htmlSplit(input) {
        var parts = [];
        var workingInput = input;

        var match;
        while ((match = workingInput.match(htmlSplitRegex))) {
            // The `match` result, when invoked on a RegExp with the `g` flag (`/foo/g`) will not include `index`.
            // If the `g` flag is omitted, `index` is included.
            // `htmlSplitRegex` does not have the `g` flag so we can assert it will have an index number.
            // Assert `match.index` is a number.
            var index = /** @type {number} */ (match.index);

            parts.push(workingInput.slice(0, index));
            parts.push(match[0]);
            workingInput = workingInput.slice(index + match[0].length);
        }

        if (workingInput.length) {
            parts.push(workingInput);
        }

        return parts;
    }

    /**
     * Replace characters or phrases within HTML elements only.
     *
     * @param  {string}                haystack     The text which has to be formatted.
     * @param  {Record<string,string>} replacePairs In the form {from: 'to', …}.
     * @return {string}                             The formatted text.
     */
    function replaceInHtmlTags(haystack, replacePairs) {
        // Find all elements.
        var textArr = htmlSplit(haystack);
        var changed = false;

        // Extract all needles.
        var needles = Object.keys(replacePairs);

        // Loop through delimiters (elements) only.
        for (var i = 1; i < textArr.length; i += 2) {
            for (var j = 0; j < needles.length; j++) {
                var needle = needles[j];
                if (-1 !== textArr[i].indexOf(needle)) {
                    textArr[i] = textArr[i].replace(
                        new RegExp(needle, 'g'),
                        replacePairs[needle]
                    );
                    changed = true;
                    // After one strtr() break out of the foreach loop and look at next element.
                    break;
                }
            }
        }

        if (changed) {
            haystack = textArr.join('');
        }

        return haystack;
    }

    return ItemText;
});_N2.d('ItemVimeo', ['$', 'Item'], function () {
    "use strict";
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments _N2.Item
     */
    function ItemVimeo() {
        this.type = 'vimeo';
        _N2.Item.prototype.constructor.apply(this, arguments);
    }

    ItemVimeo.prototype = Object.create(_N2.Item.prototype);
    ItemVimeo.prototype.constructor = ItemVimeo;

    ItemVimeo.prototype.getLabel = function () {

        return 'Vimeo';
    };

    ItemVimeo.prototype.hasTextScale = function () {

        return false;
    };

    ItemVimeo.prototype.added = function () {
        this.needFill = ['vimeourl', 'image', 'alt', 'playbuttonalt'];

        this.generator.registerField('#item_vimeovimeourl');
        this.generator.registerField('#item_vimeoimage');
        this.generator.registerField('#item_vimeoalt');
        this.generator.registerField('#item_vimeoplaybuttonalt');
    };

    ItemVimeo.prototype.parseAll = function (data) {
        var vimeoChanged = this.values.vimeourl != data.vimeourl;

        _N2.Item.prototype.parseAll.apply(this, arguments);

        if (data.image == '') {
            data.image = '$ss3-frontend$/images/placeholder/video.png';
        }

        data.image = _N2._imageHelper.fixed(data.image);

        var urlPieces = data.vimeourl.split('?');

        if (vimeoChanged && urlPieces[0] != '') {
            var vimeoRegexp = /https?:\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)(?:$|\/|\?)/,
                vimeoMatch = urlPieces[0].match(vimeoRegexp);

            var videoCode = false;
            if (vimeoMatch) {
                videoCode = vimeoMatch[3];
            } else if (urlPieces[0].match(/^[0-9]+$/)) {
                videoCode = urlPieces[0];
            }

            if (videoCode) {
                var privateID = this.getPrivateID(data.vimeourl),
                    jsonUrl = 'https://vimeo.com/api/oembed.json?url=https://vimeo.com/' + encodeURI(videoCode);

                if (privateID !== '') {
                    jsonUrl += '/' + encodeURI(privateID);
                }
                jsonUrl += '?width=1920&height=1080';

                _N2.AjaxHelper.getJSON(jsonUrl).done((function (videoData) {
                    $('#item_vimeoimage').val(videoData.thumbnail_url).trigger('change');
                }).bind(this)).fail(function (videoData) {
                    _N2.Notification.error('Video not found.');
                });
            } else {
                _N2.Notification.error('The provided URL does not match any known Vimeo url or code.');
            }
        }
    };

    ItemVimeo.prototype.getPrivateID = function (vimeoUrl) {
        var privateID = '';

        if (vimeoUrl.includes('?')) {
            var url = '';
            if (!vimeoUrl.includes('//player.vimeo.com')) {
                url = new URL('https://player.vimeo.com/video/' + vimeoUrl);
            } else {
                url = new URL(vimeoUrl);
            }
            var param = url.searchParams.get('h');
            if (param !== null) {
                privateID = param;
            }
        } else if (vimeoUrl.includes('//vimeo.com')) {
            var urlPieces = vimeoUrl.split('/');
            if (urlPieces.length === 5) {
                privateID = urlPieces[urlPieces.length - 1];
            }
        }

        return privateID;
    }

    ItemVimeo.prototype._render = function (data) {
        var style = "";
        if (data['aspect-ratio'] === 'custom') {
            style = 'padding-top:' + (data['aspect-ratio-height'] / data['aspect-ratio-width'] * 100) + '%';
        }

        var $node = $('<div class="n2_ss_video_player n2-ow-all"><div class="n2_ss_video_player__placeholder" style="' + style + '"></div></div>')
            .attr('data-aspect-ratio', data['aspect-ratio'])
            .css({
                background: 'url(' + data.image + ') no-repeat 50% 50%',
                backgroundSize: 'cover'
            });

        if (parseInt(data.playbutton) || data.playbutton === undefined) {
            var playButtonSrc = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48Y2lyY2xlIGN4PSIyNCIgY3k9IjI0IiByPSIyNCIgZmlsbD0iIzAwMCIgb3BhY2l0eT0iLjYiLz48cGF0aCBmaWxsPSIjRkZGIiBkPSJNMTkuOCAzMmMtLjEyNCAwLS4yNDctLjAyOC0uMzYtLjA4LS4yNjQtLjExNi0uNDM2LS4zNzUtLjQ0LS42NjRWMTYuNzQ0Yy4wMDUtLjI5LjE3Ni0uNTUuNDQtLjY2Ni4yNzMtLjEyNi41OTItLjEuODQuMDdsMTAuNCA3LjI1N2MuMi4xMzIuMzIuMzU1LjMyLjU5NXMtLjEyLjQ2My0uMzIuNTk1bC0xMC40IDcuMjU2Yy0uMTQuMS0uMzEuMTUtLjQ4LjE1eiIvPjwvZz48L3N2Zz4=',
                playButtonStyle = '';

            if (data.playbuttonimage) {
                playButtonSrc = _N2._imageHelper.fixed(data.playbuttonimage);
            }

            if (data.playbuttonwidth) {
                playButtonStyle += 'width:' + data.playbuttonwidth + 'px;'
            }

            if (data.playbuttonheight) {
                playButtonStyle += 'height:' + data.playbuttonheight + 'px;'
            }

            $('<div class="n2_ss_video_player__cover"><img class="n2_ss_video_play_btn" style="' + playButtonStyle + '" src="' + playButtonSrc + '"></div>')
                .appendTo($node);
        }

        this.$item.append($node);
    };

    ItemVimeo.prototype.fitLayer = function () {
        return true;
    };

    return ItemVimeo;
});_N2.d('ItemYoutube', ['$', 'Item'], function () {
    "use strict";
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments _N2.Item
     */
    function ItemYoutube() {
        this.type = 'youtube';
        _N2.Item.prototype.constructor.apply(this, arguments);
    }

    ItemYoutube.prototype = Object.create(_N2.Item.prototype);
    ItemYoutube.prototype.constructor = ItemYoutube;

    ItemYoutube.prototype.getLabel = function () {

        return 'YouTube';
    };

    ItemYoutube.prototype.hasTextScale = function () {

        return false;
    };

    ItemYoutube.prototype.added = function () {
        this.needFill = ['youtubeurl', 'image', 'start', 'alt', 'playbuttonalt'];

        this.generator.registerFields(['#item_youtubeyoutubeurl', '#item_youtubeimage', '#item_youtubestart', '#item_youtubealt', '#item_youtubeplaybuttonalt']);
    };

    ItemYoutube.prototype.parseAll = function (data) {

        var youTubeChanged = this.values.youtubeurl !== data.youtubeurl;

        data.youtubeurl = data.youtubeurl.replaceAll(' ', '');

        _N2.Item.prototype.parseAll.apply(this, arguments);

        if (data.image === '') {
            data.image = '$ss3-frontend$/images/placeholder/video.png';
        }

        data.image = _N2._imageHelper.fixed(data.image);

        if (youTubeChanged) {
            var youtubeRegexp = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube(?:-nocookie)?\.com(?:\/embed\/|\/shorts\/|\/v\/|\/watch\?v=|\/watch\?.+&v=))([\w-]{11})(?:.+)?.*/,
                youtubeMatch = data.youtubeurl.match(youtubeRegexp);

            if (youtubeMatch) {
                var url = 'https://i.ytimg.com/vi/' + youtubeMatch[1] + '/hqdefault.jpg';
                if (this.values.youtubeurl === '{video_url}') {
                    url = 'https://i.ytimg.com/vi/{video_id}/hqdefault.jpg';
                }
                setTimeout(function () {
                    $('#item_youtubeimage').val(url).trigger('change');
                }, 100);

            } else if (data.youtubeurl.length === 11) {
                setTimeout(function () {
                    $('#item_youtubeimage').val('https://i.ytimg.com/vi/' + data.youtubeurl + '/hqdefault.jpg').trigger('change');
                }, 100);
            }
        }
    };

    ItemYoutube.prototype.fitLayer = function () {
        return true;
    };

    ItemYoutube.prototype._render = function (data) {
        var style = "";
        if (data['aspect-ratio'] === 'custom') {
            style = 'padding-top:' + (data['aspect-ratio-height'] / data['aspect-ratio-width'] * 100) + '%';
        }

        var $node = $('<div class="n2_ss_video_player n2-ow-all"><div class="n2_ss_video_player__placeholder" style="' + style + '"></div></div>')
            .attr('data-aspect-ratio', data['aspect-ratio'])
            .css({
                background: 'url(' + data.image + ') no-repeat 50% 50%',
                backgroundSize: 'cover'
            });

        if (parseInt(data.playbutton) || data.playbutton === undefined) {
            var playButtonSrc = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48Y2lyY2xlIGN4PSIyNCIgY3k9IjI0IiByPSIyNCIgZmlsbD0iIzAwMCIgb3BhY2l0eT0iLjYiLz48cGF0aCBmaWxsPSIjRkZGIiBkPSJNMTkuOCAzMmMtLjEyNCAwLS4yNDctLjAyOC0uMzYtLjA4LS4yNjQtLjExNi0uNDM2LS4zNzUtLjQ0LS42NjRWMTYuNzQ0Yy4wMDUtLjI5LjE3Ni0uNTUuNDQtLjY2Ni4yNzMtLjEyNi41OTItLjEuODQuMDdsMTAuNCA3LjI1N2MuMi4xMzIuMzIuMzU1LjMyLjU5NXMtLjEyLjQ2My0uMzIuNTk1bC0xMC40IDcuMjU2Yy0uMTQuMS0uMzEuMTUtLjQ4LjE1eiIvPjwvZz48L3N2Zz4=',
                playButtonStyle = '';

            if (data.playbuttonimage) {
                playButtonSrc = _N2._imageHelper.fixed(data.playbuttonimage);
            }

            if (data.playbuttonwidth) {
                playButtonStyle += 'width:' + data.playbuttonwidth + 'px;'
            }

            if (data.playbuttonheight) {
                playButtonStyle += 'height:' + data.playbuttonheight + 'px;'
            }

            $('<div class="n2_ss_video_player__cover"><img class="n2_ss_video_play_btn" style="' + playButtonStyle + '" src="' + playButtonSrc + '"></div>')
                .appendTo($node);
        }

        this.$item.append($node);
    };

    return ItemYoutube;
});_N2.d('BgAnimationEditor', ['$', 'NextendFragmentEditorControllerWithEditor'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     * @augments {_N2.NextendFragmentEditorControllerWithEditor}
     */
    function BgAnimationEditor() {
        this.parameters = {
            shiftedBackgroundAnimation: 0
        };

        this.$css = $('<style></style>').appendTo('head');

        this.preview = $('<div class="n2_form__table_preview n2_ss_background_animation_preview"></div>')
            .appendTo('.n2_form__table[data-field="table-background-animation-preview"]');

        var $sliderEl = $('<div class="n2_ss_background_animation_preview__slider"></div>')
            .appendTo(this.preview);

        this.bgAnimationElement = _CreateElementDiv();
        this.bgAnimationElement.className = 'n2_ss_background_animation_preview__animation_container';
        $sliderEl[0].appendChild(this.bgAnimationElement);

        var $slide1 = $('<div class="n2_ss_background_animation_preview__slide"></div>')
            .append($('<div class="n2_ss_background_animation_preview__slide_background"></div>')
                .append($('<div class="n2_ss_background_animation_preview__slide_background_image"></div>')))
            .appendTo($sliderEl);

        $slide1.clone().appendTo($sliderEl);

        this.backgroundAnimations = {
            color: 'eeeeeeff'
        };

        _N2.NextendFragmentEditorControllerWithEditor.prototype.constructor.call(this, false);

        this.slides = $sliderEl.find('.n2_ss_background_animation_preview__slide');
        this.slides.eq(1).css('opactiy', 0);
        this.bgImages = $sliderEl.find('.n2_ss_background_animation_preview__slide_background');

        var images = [
            _N2._imageHelper.fixed('$ss3-frontend$/images/placeholder/placeholder1.png'),
            _N2._imageHelper.fixed('$ss3-frontend$/images/placeholder/placeholder2.png')
        ];

        $sliderEl.find('.n2_ss_background_animation_preview__slide_background_image')
            .each(function (i) {

                $('<img src="' + images[i] + '" alt=""/>')
                    .css({
                        display: 'block',
                        objectFit: 'cover',
                        width: '100%',
                        height: '100%'
                    })
                    .appendTo(this);
            });

        this.$colorField = $('#n2-background-animationcolor')
            .on('nextendChange', this.changeColor.bind(this));
    }

    BgAnimationEditor.prototype = Object.create(_N2.NextendFragmentEditorControllerWithEditor.prototype);
    BgAnimationEditor.prototype.constructor = BgAnimationEditor;

    BgAnimationEditor.prototype.loadDefaults = function () {
        _N2.NextendFragmentEditorControllerWithEditor.prototype.loadDefaults.call(this);
        this.type = 'backgroundanimation';
        this.current = 0;
        this.animationProperties = false;
    };

    BgAnimationEditor.prototype.get = function () {
        return null;
    };

    BgAnimationEditor.prototype.load = function (visual, tabs, mode, preview) {

    };

    BgAnimationEditor.prototype.setTabs = function (labels) {

    };

    BgAnimationEditor.prototype.start = function (data) {
        if (data.color !== undefined) {
            this.$colorField.data('field').insideChange(data.color);
            this.backgroundAnimations.color = data.color;
        } else {
            $('#n2-tab-background-animation-form').remove();
            this.$css.html('.n2-bganim-side,.n2-bganim-tile-overlay-colored{background: ' + (_N2._currentEditor.frontend.parameters.bgAnimations.color || '#333333') + ';}');
        }
        if (this.animationProperties) {
            if (!this.timeline) {
                this.next();
            } else {
                this.timeline.play();
            }
        }
    };

    BgAnimationEditor.prototype.changeColor = function () {
        this.backgroundAnimations.color = this.$colorField.val();
        this.$css.html('.n2-bganim-side,.n2-bganim-tile-overlay-colored{background: ' + N2Color.hex2rgbaCSS(this.backgroundAnimations.color) + ';}');
    };

    BgAnimationEditor.prototype.pause = function () {
        if (this.timeline) {
            this.timeline.pause();
        }
    };

    BgAnimationEditor.prototype.next = function () {
        this.timeline = new _N2.___Timeline({
            paused: true,
            onComplete: this.ended.bind(this)
        });
        var current = this.bgImages[this.current],
            next = this.bgImages[1 - this.current];

        this.currentAnimation = new _N2['SmartSliderBackgroundAnimation' + this.animationProperties.type](this, new _N2.SmartSliderBackgroundAnimationBackgroundInterface(current), new _N2.SmartSliderBackgroundAnimationBackgroundInterface(next), this.animationProperties, 1, 0);

        var currentSMW = _N2.MW.___getSMW(this.slides[this.current]),
            nextSMW = _N2.MW.___getSMW(this.slides[1 - this.current]);
        currentSMW.zIndex = 2;
        nextSMW.zIndex = 3;

        this.timeline.to(currentSMW, 0.5, {
            opacity: 0
        }, this.currentAnimation.getExtraDelay());

        this.timeline.to(nextSMW, 0.5, {
            opacity: 1
        }, this.currentAnimation.getExtraDelay());


        this.currentAnimation.postSetup();

        this.timeline.set({}, {}, '+=0.5');

        this.current = 1 - this.current;
        this.timeline.play();
    };

    BgAnimationEditor.prototype.ended = function () {
        if (this.currentAnimation) {
            this.currentAnimation.ended();
        }
        this.next();
    };

    BgAnimationEditor.prototype.setAnimationProperties = function (animationProperties) {
        var lastAnimationProperties = this.animationProperties;

        if (animationProperties.displacementImage) {
            animationProperties.displacementImage = _N2._imageHelper.fixed(animationProperties.displacementImage);
        }
        this.animationProperties = animationProperties;
        if (!lastAnimationProperties) {
            this.next();
        }
    };

    return BgAnimationEditor;
});
_N2.d('BgAnimationManager', ['$', 'NextendVisualManagerMultipleSelection'], function () {
    var $ = _N2.$;

    /**
     * @memberOf _N2
     *
     * @constructor
     */
    function BgAnimationManager() {
        this.type = 'backgroundanimation';
        _N2.NextendVisualManagerMultipleSelection.prototype.constructor.apply(this, arguments);
    }

    BgAnimationManager.prototype = Object.create(_N2.NextendVisualManagerMultipleSelection.prototype);
    BgAnimationManager.prototype.constructor = BgAnimationManager;

    BgAnimationManager.prototype.loadDefaults = function () {
        _N2.NextendVisualManagerMultipleSelection.prototype.loadDefaults.apply(this, arguments);
        this.type = 'backgroundanimation';
        this.labels = {
            visual: 'Background animation',
            visuals: 'Background animations'
        };
    };

    BgAnimationManager.prototype.initController = function () {
        return new _N2.BgAnimationEditor();
    };

    BgAnimationManager.prototype.createVisual = function (visual, set) {
        return new _N2.NextendVisualWithSetRowMultipleSelection(visual, set, this);
    };

    BgAnimationManager.prototype.show = function (data, saveCallback) {
        var controllerParameters = {};

        if (_N2._currentEditor) {
            controllerParameters.color = _N2._currentEditor.fragmentEditor.activeLayer.getProperty('background-animation-color');
        } else {
            var $colorField = $('#sliderbackground-animation-color');
            if ($colorField.length) {
                controllerParameters.color = $colorField.val();
            }
        }

        _N2.NextendVisualManagerMultipleSelection.prototype.show.call(this, data, saveCallback, controllerParameters);
    };

    BgAnimationManager.prototype.getAsString = function () {

        var $colorField = $('#sliderbackground-animation-color,#layerslide-background-animation-color');
        if ($colorField.length) {
            $colorField.val($('#n2-background-animationcolor').val()).trigger('outsideChange');
        }
        return _N2.NextendVisualManagerMultipleSelection.prototype.getAsString.call(this);
    };

    return BgAnimationManager;
});
_N2.d('SmartSliderAdminWidgetArrowImage', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetArrowImage() {

    }

    SmartSliderAdminWidgetArrowImage.renderStyle = function (fieldID, data) {

        if (fieldID === 'sliderwidget-arrow-style') {
            var outerElement = _CreateElementDiv(),
                innerElement = _CreateElementDiv(),
                imageElement = _CreateElement('img'),
                identifier = 'sliderwidget-arrow-previous',
                src,
                imageField = document.getElementById(identifier + '-image');

            if (imageField && imageField.value) {
                src = imageField.value;
            } else {
                var mirrorElement = document.getElementById('sliderwidget-arrow-mirror');
                if (mirrorElement && !+mirrorElement.value) {
                    identifier = 'sliderwidget-arrow-next'
                    imageField = document.getElementById(identifier + '-image');
                    if (imageField) {
                        src = imageField.value;
                    }
                }
            }

            _NodeSetStyle(innerElement, 'display', 'inline-block');
            innerElement.className = data.styleClassName;
            outerElement.appendChild(innerElement);

            _NodeSetStyle(imageElement, 'display', 'block');
            innerElement.appendChild(imageElement);

            if (src) {
                _NodeSetAttribute(imageElement, 'src', _N2._imageHelper.fixed(src));
            } else {
                $('#' + identifier).data('field').getCurrentImage(document.getElementById(identifier + '-color').value)
                    .done(function (src) {
                        _NodeSetAttribute(imageElement, 'src', src);
                    })
            }

            return outerElement;
        }
    };


    return SmartSliderAdminWidgetArrowImage;
});_N2.d('SmartSliderAdminWidgetAutoplayImage', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetAutoplayImage() {

    }

    SmartSliderAdminWidgetAutoplayImage.renderStyle = function (fieldID, data) {
        if (fieldID === 'sliderwidget-autoplay-style') {
            var $outer = $('<div></div>')
                    .css('display', 'inline-block')
                    .addClass(data.styleClassName),
                $image = $('<img style="display: block;">')
                    .appendTo($outer),
                src = $('#sliderwidget-autoplay-play-image').val();

            if (src) {
                $image.attr('src', _N2._imageHelper.fixed(src));
            } else {
                $('#sliderwidget-autoplay-play').data('field').getCurrentImage($('#sliderwidget-autoplay-play-color').val())
                    .done(function (src) {
                        $image.attr('src', src);
                    })
            }

            return $outer;
        }
    };


    return SmartSliderAdminWidgetAutoplayImage;
});_N2.d('SmartSliderAdminWidgetBarHorizontal', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetBarHorizontal() {

    }

    SmartSliderAdminWidgetBarHorizontal.renderStyle = function (fieldID, data) {
        if (fieldID === 'sliderwidget-bar-style') {
            var $separator = $('#sliderwidget-bar-separator').val();

            var $outer = $('<div></div>')
                .css({
                    width: '100%',
                    boxSizing: 'border-box'
                })
                .addClass(data.styleClassName);
            $('<span>Slide title</span>')
                .addClass(data.fontClassName)
                .attr('href', '#')
                .appendTo($outer);
            $('<span>' + $separator + 'Slide description which is longer than the title</span>')
                .addClass(data.fontClassName2)
                .appendTo($outer);


            return $outer;
        }
    };

    SmartSliderAdminWidgetBarHorizontal.renderFont = function (fieldID, data) {
        var $outer;
        if (fieldID === 'sliderwidget-bar-font-title') {
            $outer = $('<div></div>')
                .css({
                    width: '100%',
                    boxSizing: 'border-box'
                })
                .addClass(data.styleClassName);
            $('<span>Slide title</span>')
                .addClass(data.fontClassName)
                .attr('href', '#')
                .appendTo($outer);


            return $outer;
        } else if (fieldID === 'sliderwidget-bar-font-description') {
            $outer = $('<div></div>')
                .css({
                    width: '100%',
                    boxSizing: 'border-box'
                })
                .addClass(data.styleClassName);
            $('<span>Slide description which is longer than the title</span>')
                .addClass(data.fontClassName)
                .attr('href', '#')
                .appendTo($outer);


            return $outer;
        }
    };

    return SmartSliderAdminWidgetBarHorizontal;
});_N2.d('SmartSliderAdminWidgetBulletTransition', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetBulletTransition() {

    }

    SmartSliderAdminWidgetBulletTransition.renderStyle = function (fieldID, data) {
        var $outer;

        if (fieldID === 'sliderwidget-bullet-style') {
            $outer = $('<div></div>')
                .css('display', 'inline-block')
                .addClass(data.styleClassName2);

            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName)
                .appendTo($outer);
            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName + ' n2-active')
                .appendTo($outer);
            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName)
                .appendTo($outer);

        } else if (fieldID === 'sliderwidget-bullet-bar') {
            $outer = $('<div></div>')
                .css('display', 'inline-block')
                .addClass(data.styleClassName);

            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName2)
                .appendTo($outer);
            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName2 + ' n2-active')
                .appendTo($outer);
            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName2)
                .appendTo($outer);
        }

        return $outer;
    };


    return SmartSliderAdminWidgetBulletTransition;
});_N2.d('SmartSliderAdminWidgetBulletThumbnail', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetBulletThumbnail() {

    }

    SmartSliderAdminWidgetBulletThumbnail.renderStyle = function (fieldID, data) {
        if (fieldID === 'sliderwidget-bullet-thumbnail-style') {
            var $outer = $('<div></div>')
                .css('display', 'inline-block')
                .addClass(data.styleClassName);

            $('<div></div>')
                .css({
                    'width': $('#sliderwidget-bullet-thumbnail-width').val() + 'px',
                    'height': $('#sliderwidget-bullet-thumbnail-height').val() + 'px',
                    'overflow': 'hidden',
                    'background': 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/image.png") + '")',
                    'background-size': 'cover'
                })
                .appendTo($outer);

            return $outer;
        }
    };


    return SmartSliderAdminWidgetBulletThumbnail;
});_N2.d('SmartSliderAdminWidgetThumbnailBasic', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetThumbnailBasic() {

    }

    SmartSliderAdminWidgetThumbnailBasic.renderStyle = function (fieldID, data) {
        var $outer,
            $inner,
            thumbnailWidth = $('#sliderwidget-thumbnail-width').val(),
            thumbnailHeight = $('#sliderwidget-thumbnail-height').val();

        if (fieldID === 'sliderwidget-thumbnail-style-bar') {
            $outer = $('<div></div>')
                .css({
                    overflow: 'hidden',
                    width: thumbnailWidth * 2.5 + 'px'
                })
                .addClass(data.styleClassName);
            $inner = $('<div></div>')
                .css('width', '200%')
                .appendTo($outer);


            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/image.png") + '")'
                })
                .addClass(data.styleClassName2)
                .appendTo($inner);

            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/video.png") + '")'
                })
                .addClass(data.styleClassName2 + ' n2-active')
                .appendTo($inner);

            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/image.png") + '")'
                })
                .addClass(data.styleClassName2)
                .appendTo($inner);


            return $outer;

        } else if (fieldID === 'sliderwidget-thumbnail-style-slides') {
            $outer = $('<div></div>')
                .css({
                    overflow: 'hidden',
                    width: '480px'
                })
                .addClass(data.styleClassName2);
            $inner = $('<div></div>')
                .css('width', '200%')
                .appendTo($outer);


            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/image.png") + '")'
                })
                .addClass(data.styleClassName)
                .appendTo($inner);

            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/video.png") + '")'
                })
                .addClass(data.styleClassName + ' n2-active')
                .appendTo($inner);

            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/image.png") + '")'
                })
                .addClass(data.styleClassName)
                .appendTo($inner);


            return $outer;

        } else if (fieldID === "sliderwidget-thumbnail-title-style") {
            return $('<span>Slide title</span>')
                .addClass(data.styleClassName + ' ' + data.fontClassName);
        }
    };

    SmartSliderAdminWidgetThumbnailBasic.renderFont = function (fieldID, data) {
        if (fieldID === 'sliderwidget-thumbnail-title-font') {
            return $('<span>Slide title</span>')
                .addClass(data.styleClassName + ' ' + data.fontClassName);
        } else if (fieldID === 'sliderwidget-thumbnail-description-font') {
            return $('<span>Slide description with long long text...</span>')
                .addClass(data.styleClassName + ' ' + data.fontClassName);
        }
    };


    return SmartSliderAdminWidgetThumbnailBasic;
});_N2.d('SmartSliderAdminWidgetArrowImage', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetArrowImage() {

    }

    SmartSliderAdminWidgetArrowImage.renderStyle = function (fieldID, data) {

        if (fieldID === 'sliderwidget-arrow-style') {
            var outerElement = _CreateElementDiv(),
                innerElement = _CreateElementDiv(),
                imageElement = _CreateElement('img'),
                identifier = 'sliderwidget-arrow-previous',
                src,
                imageField = document.getElementById(identifier + '-image');

            if (imageField && imageField.value) {
                src = imageField.value;
            } else {
                var mirrorElement = document.getElementById('sliderwidget-arrow-mirror');
                if (mirrorElement && !+mirrorElement.value) {
                    identifier = 'sliderwidget-arrow-next'
                    imageField = document.getElementById(identifier + '-image');
                    if (imageField) {
                        src = imageField.value;
                    }
                }
            }

            _NodeSetStyle(innerElement, 'display', 'inline-block');
            innerElement.className = data.styleClassName;
            outerElement.appendChild(innerElement);

            _NodeSetStyle(imageElement, 'display', 'block');
            innerElement.appendChild(imageElement);

            if (src) {
                _NodeSetAttribute(imageElement, 'src', _N2._imageHelper.fixed(src));
            } else {
                $('#' + identifier).data('field').getCurrentImage(document.getElementById(identifier + '-color').value)
                    .done(function (src) {
                        _NodeSetAttribute(imageElement, 'src', src);
                    })
            }

            return outerElement;
        }
    };


    return SmartSliderAdminWidgetArrowImage;
});_N2.d('SmartSliderAdminWidgetAutoplayImage', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetAutoplayImage() {

    }

    SmartSliderAdminWidgetAutoplayImage.renderStyle = function (fieldID, data) {
        if (fieldID === 'sliderwidget-autoplay-style') {
            var $outer = $('<div></div>')
                    .css('display', 'inline-block')
                    .addClass(data.styleClassName),
                $image = $('<img style="display: block;">')
                    .appendTo($outer),
                src = $('#sliderwidget-autoplay-play-image').val();

            if (src) {
                $image.attr('src', _N2._imageHelper.fixed(src));
            } else {
                $('#sliderwidget-autoplay-play').data('field').getCurrentImage($('#sliderwidget-autoplay-play-color').val())
                    .done(function (src) {
                        $image.attr('src', src);
                    })
            }

            return $outer;
        }
    };


    return SmartSliderAdminWidgetAutoplayImage;
});_N2.d('SmartSliderAdminWidgetBarHorizontal', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetBarHorizontal() {

    }

    SmartSliderAdminWidgetBarHorizontal.renderStyle = function (fieldID, data) {
        if (fieldID === 'sliderwidget-bar-style') {
            var $separator = $('#sliderwidget-bar-separator').val();

            var $outer = $('<div></div>')
                .css({
                    width: '100%',
                    boxSizing: 'border-box'
                })
                .addClass(data.styleClassName);
            $('<span>Slide title</span>')
                .addClass(data.fontClassName)
                .attr('href', '#')
                .appendTo($outer);
            $('<span>' + $separator + 'Slide description which is longer than the title</span>')
                .addClass(data.fontClassName2)
                .appendTo($outer);


            return $outer;
        }
    };

    SmartSliderAdminWidgetBarHorizontal.renderFont = function (fieldID, data) {
        var $outer;
        if (fieldID === 'sliderwidget-bar-font-title') {
            $outer = $('<div></div>')
                .css({
                    width: '100%',
                    boxSizing: 'border-box'
                })
                .addClass(data.styleClassName);
            $('<span>Slide title</span>')
                .addClass(data.fontClassName)
                .attr('href', '#')
                .appendTo($outer);


            return $outer;
        } else if (fieldID === 'sliderwidget-bar-font-description') {
            $outer = $('<div></div>')
                .css({
                    width: '100%',
                    boxSizing: 'border-box'
                })
                .addClass(data.styleClassName);
            $('<span>Slide description which is longer than the title</span>')
                .addClass(data.fontClassName)
                .attr('href', '#')
                .appendTo($outer);


            return $outer;
        }
    };

    return SmartSliderAdminWidgetBarHorizontal;
});_N2.d('SmartSliderAdminWidgetBulletTransition', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetBulletTransition() {

    }

    SmartSliderAdminWidgetBulletTransition.renderStyle = function (fieldID, data) {
        var $outer;

        if (fieldID === 'sliderwidget-bullet-style') {
            $outer = $('<div></div>')
                .css('display', 'inline-block')
                .addClass(data.styleClassName2);

            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName)
                .appendTo($outer);
            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName + ' n2-active')
                .appendTo($outer);
            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName)
                .appendTo($outer);

        } else if (fieldID === 'sliderwidget-bullet-bar') {
            $outer = $('<div></div>')
                .css('display', 'inline-block')
                .addClass(data.styleClassName);

            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName2)
                .appendTo($outer);
            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName2 + ' n2-active')
                .appendTo($outer);
            $('<div></div>')
                .css({
                    "display": "inline-block",
                    "vertical-align": "top"
                })
                .addClass(data.styleClassName2)
                .appendTo($outer);
        }

        return $outer;
    };


    return SmartSliderAdminWidgetBulletTransition;
});_N2.d('SmartSliderAdminWidgetBulletThumbnail', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetBulletThumbnail() {

    }

    SmartSliderAdminWidgetBulletThumbnail.renderStyle = function (fieldID, data) {
        if (fieldID === 'sliderwidget-bullet-thumbnail-style') {
            var $outer = $('<div></div>')
                .css('display', 'inline-block')
                .addClass(data.styleClassName);

            $('<div></div>')
                .css({
                    'width': $('#sliderwidget-bullet-thumbnail-width').val() + 'px',
                    'height': $('#sliderwidget-bullet-thumbnail-height').val() + 'px',
                    'overflow': 'hidden',
                    'background': 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/image.png") + '")',
                    'background-size': 'cover'
                })
                .appendTo($outer);

            return $outer;
        }
    };


    return SmartSliderAdminWidgetBulletThumbnail;
});_N2.d('SmartSliderAdminWidgetThumbnailBasic', ['$'], function () {
    var $ = _N2.$;

    function SmartSliderAdminWidgetThumbnailBasic() {

    }

    SmartSliderAdminWidgetThumbnailBasic.renderStyle = function (fieldID, data) {
        var $outer,
            $inner,
            thumbnailWidth = $('#sliderwidget-thumbnail-width').val(),
            thumbnailHeight = $('#sliderwidget-thumbnail-height').val();

        if (fieldID === 'sliderwidget-thumbnail-style-bar') {
            $outer = $('<div></div>')
                .css({
                    overflow: 'hidden',
                    width: thumbnailWidth * 2.5 + 'px'
                })
                .addClass(data.styleClassName);
            $inner = $('<div></div>')
                .css('width', '200%')
                .appendTo($outer);


            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/image.png") + '")'
                })
                .addClass(data.styleClassName2)
                .appendTo($inner);

            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/video.png") + '")'
                })
                .addClass(data.styleClassName2 + ' n2-active')
                .appendTo($inner);

            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/image.png") + '")'
                })
                .addClass(data.styleClassName2)
                .appendTo($inner);


            return $outer;

        } else if (fieldID === 'sliderwidget-thumbnail-style-slides') {
            $outer = $('<div></div>')
                .css({
                    overflow: 'hidden',
                    width: '480px'
                })
                .addClass(data.styleClassName2);
            $inner = $('<div></div>')
                .css('width', '200%')
                .appendTo($outer);


            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/image.png") + '")'
                })
                .addClass(data.styleClassName)
                .appendTo($inner);

            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/video.png") + '")'
                })
                .addClass(data.styleClassName + ' n2-active')
                .appendTo($inner);

            $('<div></div>')
                .css({
                    display: 'inline-block',
                    verticalAlign: 'top',
                    width: thumbnailWidth + 'px',
                    height: thumbnailHeight + 'px',
                    backgroundImage: 'url("' + _N2._imageHelper.fixed("$ss3-frontend$/images/placeholder/image.png") + '")'
                })
                .addClass(data.styleClassName)
                .appendTo($inner);


            return $outer;

        } else if (fieldID === "sliderwidget-thumbnail-title-style") {
            return $('<span>Slide title</span>')
                .addClass(data.styleClassName + ' ' + data.fontClassName);
        }
    };

    SmartSliderAdminWidgetThumbnailBasic.renderFont = function (fieldID, data) {
        if (fieldID === 'sliderwidget-thumbnail-title-font') {
            return $('<span>Slide title</span>')
                .addClass(data.styleClassName + ' ' + data.fontClassName);
        } else if (fieldID === 'sliderwidget-thumbnail-description-font') {
            return $('<span>Slide description with long long text...</span>')
                .addClass(data.styleClassName + ' ' + data.fontClassName);
        }
    };


    return SmartSliderAdminWidgetThumbnailBasic;
});_N2.d('smartslider-backend');})(window);