/**!
 * MixItUp v2.1.5
 *
 * @copyright Copyright 2014 KunkaLabs Limited.
 * @author    KunkaLabs Limited.
 * @link      https://mixitup.kunkalabs.com
 *
 * @license   Commercial use requires a commercial license.
 *            https://mixitup.kunkalabs.com/licenses/
 *
 *            Non-commercial use permitted under terms of CC-BY-NC license.
 *            http://creativecommons.org/licenses/by-nc/3.0/
 */

(function($, undf) {

    /**
     * MixItUp Constructor Function
     * @constructor
     * @extends jQuery
     */

    $.MixItUp = function() {
        var self = this;

        self._execAction('_constructor', 0);

        $.extend(self, {
            /* Public Properties
             ---------------------------------------------------------------------- */

            selectors: {
                target: '.mix',
                filter: '.filter',
                sort: '.sort'
            },
            animation: {
                enable: true,
                effects: 'fade scale',
                duration: 600,
                easing: 'ease',
                perspectiveDistance: '3000',
                perspectiveOrigin: '50% 50%',
                queue: true,
                queueLimit: 1,
                animateChangeLayout: false,
                animateResizeContainer: true,
                animateResizeTargets: false,
                staggerSequence: false,
                reverseOut: false
            },
            callbacks: {
                onMixLoad: false,
                onMixStart: false,
                onMixBusy: false,
                onMixEnd: false,
                onMixFail: false,
                _user: false
            },
            controls: {
                enable: true,
                live: false,
                toggleFilterButtons: false,
                toggleLogic: 'or',
                activeClass: 'active'
            },
            layout: {
                display: 'inline-block',
                containerClass: '',
                containerClassFail: 'fail'
            },
            load: {
                filter: 'all',
                sort: false
            },
            /* Private Properties
             ---------------------------------------------------------------------- */

            _$body: null,
            _$container: null,
            _$targets: null,
            _$parent: null,
            _$sortButtons: null,
            _$filterButtons: null,
            _suckMode: false,
            _mixing: false,
            _sorting: false,
            _clicking: false,
            _loading: true,
            _changingLayout: false,
            _changingClass: false,
            _changingDisplay: false,
            _origOrder: [],
            _startOrder: [],
            _newOrder: [],
            _activeFilter: null,
            _toggleArray: [],
            _toggleString: '',
            _activeSort: 'default:asc',
            _newSort: null,
            _startHeight: null,
            _newHeight: null,
            _incPadding: true,
            _newDisplay: null,
            _newClass: null,
            _targetsBound: 0,
            _targetsDone: 0,
            _queue: [],
            _$show: $(),
            _$hide: $()
        });

        self._execAction('_constructor', 1);
    };

    /**
     * MixItUp Prototype
     * @override
     */

    $.MixItUp.prototype = {
        constructor: $.MixItUp,
        /* Static Properties
         ---------------------------------------------------------------------- */

        _instances: {},
        _handled: {
            _filter: {},
            _sort: {}
        },
        _bound: {
            _filter: {},
            _sort: {}
        },
        _actions: {},
        _filters: {},
        /* Static Methods
         ---------------------------------------------------------------------- */

        /**
         * Extend
         * @since 2.1.0
         * @param {object} new properties/methods
         * @extends {object} prototype
         */

        extend: function(extension) {
            for (var key in extension) {
                $.MixItUp.prototype[key] = extension[key];
            }
        },
        /**
         * Add Action
         * @since 2.1.0
         * @param {string} hook name
         * @param {string} namespace
         * @param {function} function to execute
         * @param {number} priority
         * @extends {object} $.MixItUp.prototype._actions
         */

        addAction: function(hook, name, func, priority) {
            $.MixItUp.prototype._addHook('_actions', hook, name, func, priority);
        },
        /**
         * Add Filter
         * @since 2.1.0
         * @param {string} hook name
         * @param {string} namespace
         * @param {function} function to execute
         * @param {number} priority
         * @extends {object} $.MixItUp.prototype._filters
         */

        addFilter: function(hook, name, func, priority) {
            $.MixItUp.prototype._addHook('_filters', hook, name, func, priority);
        },
        /**
         * Add Hook
         * @since 2.1.0
         * @param {string} type of hook
         * @param {string} hook name
         * @param {function} function to execute
         * @param {number} priority
         * @extends {object} $.MixItUp.prototype._filters
         */

        _addHook: function(type, hook, name, func, priority) {
            var collection = $.MixItUp.prototype[type],
                    obj = {};

            priority = (priority === 1 || priority === 'post') ? 'post' : 'pre';

            obj[hook] = {};
            obj[hook][priority] = {};
            obj[hook][priority][name] = func;

            $.extend(true, collection, obj);
      },
        /* Private Methods
         ---------------------------------------------------------------------- */

        /**
         * Initialise
         * @since 2.0.0
         * @param {object} domNode
         * @param {object} config
         */

        _init: function(domNode, config) {
            var self = this;

            self._execAction('_init', 0, arguments);

            config && $.extend(true, self, config);

            self._$body = $('body');
            self._domNode = domNode;
            self._$container = $(domNode);
            self._$container.addClass(self.layout.containerClass);
            self._id = domNode.id;

            self._platformDetect();

            self._brake = self._getPrefixedCSS('transition', 'none');

            self._refresh(true);

            self._$parent = self._$targets.parent().length ? self._$targets.parent() : self._$container;

            if (self.load.sort) {
                self._newSort = self._parseSort(self.load.sort);
                self._newSortString = self.load.sort;
                self._activeSort = self.load.sort;
                self._sort();
                self._printSort();
            }

            self._activeFilter = self.load.filter === 'all' ?
                    self.selectors.target :
                    self.load.filter === 'none' ?
                    '' :
                    self.load.filter;

            self.controls.enable && self._bindHandlers();

            if (self.controls.toggleFilterButtons) {
                self._buildToggleArray();

                for (var i = 0; i < self._toggleArray.length; i++) {
                    self._updateControls({filter: self._toggleArray[i], sort: self._activeSort}, true);
                }
                ;
            } else if (self.controls.enable) {
                self._updateControls({filter: self._activeFilter, sort: self._activeSort});
            }

            self._filter();

            self._init = true;

            self._$container.data('mixItUp', self);

            self._execAction('_init', 1, arguments);

            self._buildState();

            self._$targets.css(self._brake);

            self._goMix(self.animation.enable);
        },
        /**
         * Platform Detect
         * @since 2.0.0
         */

        _platformDetect: function() {
            var self = this,
                    vendorsTrans = ['Webkit', 'Moz', 'O', 'ms'],
                    vendorsRAF = ['webkit', 'moz'],
                    chrome = window.navigator.appVersion.match(/Chrome\/(\d+)\./) || false,
                    ff = typeof InstallTrigger !== 'undefined',
                    prefix = function(el) {
                        for (var i = 0; i < vendorsTrans.length; i++) {
                            if (vendorsTrans[i] + 'Transition' in el.style) {
                                return {
                                    prefix: '-' + vendorsTrans[i].toLowerCase() + '-',
                                    vendor: vendorsTrans[i]
                                };
                            }
                            ;
                        }
                        ;
                        return 'transition' in el.style ? '' : false;
                    },
                    transPrefix = prefix(self._domNode);

            self._execAction('_platformDetect', 0);

            self._chrome = chrome ? parseInt(chrome[1], 10) : false;
            self._ff = ff ? parseInt(window.navigator.userAgent.match(/rv:([^)]+)\)/)[1]) : false;
            self._prefix = transPrefix.prefix;
            self._vendor = transPrefix.vendor;
            self._suckMode = window.atob && self._prefix ? false : true;

            self._suckMode && (self.animation.enable = false);
            (self._ff && self._ff <= 4) && (self.animation.enable = false);

            /* Polyfills
             ---------------------------------------------------------------------- */

            /**
             * window.requestAnimationFrame
             */

            for (var x = 0; x < vendorsRAF.length && !window.requestAnimationFrame; x++) {
                window.requestAnimationFrame = window[vendorsRAF[x] + 'RequestAnimationFrame'];
            }

            /**
             * Object.getPrototypeOf
             */

            if (typeof Object.getPrototypeOf !== 'function') {
                if (typeof 'test'.__proto__ === 'object') {
                    Object.getPrototypeOf = function(object) {
                        return object.__proto__;
                    };
                } else {
                    Object.getPrototypeOf = function(object) {
                        return object.constructor.prototype;
                    };
                }
            }

            /**
             * Element.nextElementSibling
             */

            if (self._domNode.nextElementSibling === undf) {
                Object.defineProperty(Element.prototype, 'nextElementSibling', {
                    get: function() {
                        var el = this.nextSibling;

                        while (el) {
                            if (el.nodeType === 1) {
                                return el;
                            }
                            el = el.nextSibling;
                        }
                        return null;
                    }
                });
            }

            self._execAction('_platformDetect', 1);
        },
        /**
         * Refresh
         * @since 2.0.0
         * @param {boolean} init
         * @param {boolean} force
         */

        _refresh: function(init, force) {
            var self = this;

            self._execAction('_refresh', 0, arguments);

            self._$targets = self._$container.find(self.selectors.target);

            for (var i = 0; i < self._$targets.length; i++) {
                var target = self._$targets[i];

                if (target.dataset === undf || force) {

                    target.dataset = {};

                    for (var j = 0; j < target.attributes.length; j++) {

                        var attr = target.attributes[j],
                                name = attr.name,
                                val = attr.nodeValue;

                        if (name.indexOf('data-') > -1) {
                            var dataName = self._helpers._camelCase(name.substring(5, name.length));
                            target.dataset[dataName] = val;
                        }
                    }
                }

                if (target.mixParent === undf) {
                    target.mixParent = self._id;
                }
            }

            if (
                    (self._$targets.length && init) ||
                    (!self._origOrder.length && self._$targets.length)
                    ) {
                self._origOrder = [];

                for (var i = 0; i < self._$targets.length; i++) {
                    var target = self._$targets[i];

                    self._origOrder.push(target);
                }
            }

            self._execAction('_refresh', 1, arguments);
        },
        /**
         * Bind Handlers
         * @since 2.0.0
         */

        _bindHandlers: function() {
            var self = this,
                    filters = $.MixItUp.prototype._bound._filter,
                    sorts = $.MixItUp.prototype._bound._sort;

            self._execAction('_bindHandlers', 0);

            if (self.controls.live) {
                self._$body
                        .on('click.mixItUp.' + self._id, self.selectors.sort, function() {
                            self._processClick($(this), 'sort');
                        })
                        .on('click.mixItUp.' + self._id, self.selectors.filter, function() {
                            self._processClick($(this), 'filter');
                        });
            } else {
                self._$sortButtons = $(self.selectors.sort);
                self._$filterButtons = $(self.selectors.filter);

                self._$sortButtons.on('click.mixItUp.' + self._id, function() {
                    self._processClick($(this), 'sort');
                });

                self._$filterButtons.on('click.mixItUp.' + self._id, function() {
                    self._processClick($(this), 'filter');
                });
            }

            filters[self.selectors.filter] = (filters[self.selectors.filter] === undf) ? 1 : filters[self.selectors.filter] + 1;
            sorts[self.selectors.sort] = (sorts[self.selectors.sort] === undf) ? 1 : sorts[self.selectors.sort] + 1;

            self._execAction('_bindHandlers', 1);
        },
        /**
         * Process Click
         * @since 2.0.0
         * @param {object} $button
         * @param {string} type
         */

        _processClick: function($button, type) {
            var self = this,
                    trackClick = function($button, type, off) {
                        var proto = $.MixItUp.prototype;

                        proto._handled['_' + type][self.selectors[type]] = (proto._handled['_' + type][self.selectors[type]] === undf) ?
                                1 :
                                proto._handled['_' + type][self.selectors[type]] + 1;

                        if (proto._handled['_' + type][self.selectors[type]] === proto._bound['_' + type][self.selectors[type]]) {
                            $button[(off ? 'remove' : 'add') + 'Class'](self.controls.activeClass);
                            delete proto._handled['_' + type][self.selectors[type]];
                        }
                    };

            self._execAction('_processClick', 0, arguments);

            if (!self._mixing || (self.animation.queue && self._queue.length < self.animation.queueLimit)) {
                self._clicking = true;

                if (type === 'sort') {
                    var sort = $button.attr('data-sort');

                    if (!$button.hasClass(self.controls.activeClass) || sort.indexOf('random') > -1) {
                        $(self.selectors.sort).removeClass(self.controls.activeClass);
                        trackClick($button, type);
                        self.sort(sort);
                    }
                }

                if (type === 'filter') {
                    var filter = $button.attr('data-filter'),
                            ndx,
                            seperator = self.controls.toggleLogic === 'or' ? ',' : '';

                    if (!self.controls.toggleFilterButtons) {
                        if (!$button.hasClass(self.controls.activeClass)) {
                            $(self.selectors.filter).removeClass(self.controls.activeClass);
                            trackClick($button, type);
                            self.filter(filter);
                        }
                    } else {
                        self._buildToggleArray();

                        if (!$button.hasClass(self.controls.activeClass)) {
                            trackClick($button, type);

                            self._toggleArray.push(filter);
                        } else {
                            trackClick($button, type, true);
                            ndx = self._toggleArray.indexOf(filter);
                            self._toggleArray.splice(ndx, 1);
                        }

                        self._toggleArray = $.grep(self._toggleArray, function(n) {
                            return(n);
                        });

                        self._toggleString = self._toggleArray.join(seperator);

                        self.filter(self._toggleString);
                    }
                }

                self._execAction('_processClick', 1, arguments);
            } else {
                if (typeof self.callbacks.onMixBusy === 'function') {
                    self.callbacks.onMixBusy.call(self._domNode, self._state, self);
                }
                self._execAction('_processClickBusy', 1, arguments);
            }
        },
        /**
         * Build Toggle Array
         * @since 2.0.0
         */

        _buildToggleArray: function() {
            var self = this,
                    activeFilter = self._activeFilter.replace(/\s/g, '');

            self._execAction('_buildToggleArray', 0, arguments);

            if (self.controls.toggleLogic === 'or') {
                self._toggleArray = activeFilter.split(',');
            } else {
                self._toggleArray = activeFilter.split('.');

                !self._toggleArray[0] && self._toggleArray.shift();

                for (var i = 0, filter; filter = self._toggleArray[i]; i++) {
                    self._toggleArray[i] = '.' + filter;
                }
            }

            self._execAction('_buildToggleArray', 1, arguments);
        },
        /**
         * Update Controls
         * @since 2.0.0
         * @param {object} command
         * @param {boolean} multi
         */

        _updateControls: function(command, multi) {
            var self = this,
                    output = {
                        filter: command.filter,
                        sort: command.sort
                    },
            update = function($el, filter) {
                (multi && type == 'filter' && !(output.filter === 'none' || output.filter === '')) ?
                        $el.filter(filter).addClass(self.controls.activeClass) :
                        $el.removeClass(self.controls.activeClass).filter(filter).addClass(self.controls.activeClass);
            },
                    type = 'filter',
                    $el = null;

            self._execAction('_updateControls', 0, arguments);

            (command.filter === undf) && (output.filter = self._activeFilter);
            (command.sort === undf) && (output.sort = self._activeSort);
            (output.filter === self.selectors.target) && (output.filter = 'all');

            for (var i = 0; i < 2; i++) {
                $el = self.controls.live ? $(self.selectors[type]) : self['_$' + type + 'Buttons'];
                $el && update($el, '[data-' + type + '="' + output[type] + '"]');
                type = 'sort';
            }

            self._execAction('_updateControls', 1, arguments);
        },
        /**
         * Filter (private)
         * @since 2.0.0
         */

        _filter: function() {
            var self = this;

            self._execAction('_filter', 0);

            for (var i = 0; i < self._$targets.length; i++) {
                var $target = $(self._$targets[i]);

                if ($target.is(self._activeFilter)) {
                    self._$show = self._$show.add($target);
                } else {
                    self._$hide = self._$hide.add($target);
                }
            }

            self._execAction('_filter', 1);
        },
        /**
         * Sort (private)
         * @since 2.0.0
         */

        _sort: function() {
            var self = this,
                    arrayShuffle = function(oldArray) {
                        var newArray = oldArray.slice(),
                                len = newArray.length,
                                i = len;

                        while (i--) {
                            var p = parseInt(Math.random() * len);
                            var t = newArray[i];
                            newArray[i] = newArray[p];
                            newArray[p] = t;
                        }
                        ;
                        return newArray;
                    };

            self._execAction('_sort', 0);

            self._startOrder = [];

            for (var i = 0; i < self._$targets.length; i++) {
                var target = self._$targets[i];

                self._startOrder.push(target);
            }

            switch (self._newSort[0].sortBy) {
                case 'default':
                    self._newOrder = self._origOrder;
                    break;
                case 'random':
                    self._newOrder = arrayShuffle(self._startOrder);
                    break;
                case 'custom':
                    self._newOrder = self._newSort[0].order;
                    break;
                default:
                    self._newOrder = self._startOrder.concat().sort(function(a, b) {
                        return self._compare(a, b);
                    });
            }

            self._execAction('_sort', 1);
        },
        /**
         * Compare Algorithm
         * @since 2.0.0
         * @param {string|number} a
         * @param {string|number} b
         * @param {number} depth (recursion)
         * @return {number}
         */

        _compare: function(a, b, depth) {
            depth = depth ? depth : 0;

            var self = this,
                    order = self._newSort[depth].order,
                    getData = function(el) {
                        return el.dataset[self._newSort[depth].sortBy] || 0;
                    },
                    attrA = isNaN(getData(a) * 1) ? getData(a).toLowerCase() : getData(a) * 1,
                    attrB = isNaN(getData(b) * 1) ? getData(b).toLowerCase() : getData(b) * 1;

            if (attrA < attrB)
                return order == 'asc' ? -1 : 1;
            if (attrA > attrB)
                return order == 'asc' ? 1 : -1;
            if (attrA == attrB && self._newSort.length > depth + 1)
                return self._compare(a, b, depth + 1);

            return 0;
        },
        /**
         * Print Sort
         * @since 2.0.0
         * @param {boolean} reset
         */

        _printSort: function(reset) {
            var self = this,
                    order = reset ? self._startOrder : self._newOrder,
                    targets = self._$parent[0].querySelectorAll(self.selectors.target),
                    nextSibling = targets[targets.length - 1].nextElementSibling,
                    frag = document.createDocumentFragment();

            self._execAction('_printSort', 0, arguments);

            for (var i = 0; i < targets.length; i++) {
                var target = targets[i],
                        whiteSpace = target.nextSibling;

                if (target.style.position === 'absolute')
                    continue;

                if (whiteSpace && whiteSpace.nodeName == '#text') {
                    self._$parent[0].removeChild(whiteSpace);
                }

                self._$parent[0].removeChild(target);
            }

            for (var i = 0; i < order.length; i++) {
                var el = order[i];

                if (self._newSort[0].sortBy == 'default' && self._newSort[0].order == 'desc' && !reset) {
                    var firstChild = frag.firstChild;
                    frag.insertBefore(el, firstChild);
                    frag.insertBefore(document.createTextNode(' '), el);
                } else {
                    frag.appendChild(el);
                    frag.appendChild(document.createTextNode(' '));
                }
            }

            nextSibling ?
                    self._$parent[0].insertBefore(frag, nextSibling) :
                    self._$parent[0].appendChild(frag);

            self._execAction('_printSort', 1, arguments);
        },
        /**
         * Parse Sort
         * @since 2.0.0
         * @param {string} sortString
         * @return {array} newSort
         */

        _parseSort: function(sortString) {
            var self = this,
                    rules = typeof sortString === 'string' ? sortString.split(' ') : [sortString],
                    newSort = [];

            for (var i = 0; i < rules.length; i++) {
                var rule = typeof sortString === 'string' ? rules[i].split(':') : ['custom', rules[i]],
                        ruleObj = {
                            sortBy: self._helpers._camelCase(rule[0]),
                            order: rule[1] || 'asc'
                        };

                newSort.push(ruleObj);

                if (ruleObj.sortBy == 'default' || ruleObj.sortBy == 'random')
                    break;
            }

            return self._execFilter('_parseSort', newSort, arguments);
        },
        /**
         * Parse Effects
         * @since 2.0.0
         * @return {object} effects
         */

        _parseEffects: function() {
            var self = this,
                    effects = {
                        opacity: '',
                        transformIn: '',
                        transformOut: '',
                        filter: ''
                    },
            parse = function(effect, extract, reverse) {
                if (self.animation.effects.indexOf(effect) > -1) {
                    if (extract) {
                        var propIndex = self.animation.effects.indexOf(effect + '(');
                        if (propIndex > -1) {
                            var str = self.animation.effects.substring(propIndex),
                                    match = /\(([^)]+)\)/.exec(str),
                                    val = match[1];

                            return {val: val};
                        }
                    }
                    return true;
                } else {
                    return false;
                }
            },
                    negate = function(value, invert) {
                        if (invert) {
                            return value.charAt(0) === '-' ? value.substr(1, value.length) : '-' + value;
                        } else {
                            return value;
                        }
                    },
                    buildTransform = function(key, invert) {
                        var transforms = [
                            ['scale', '.01'],
                            ['translateX', '20px'],
                            ['translateY', '20px'],
                            ['translateZ', '20px'],
                            ['rotateX', '90deg'],
                            ['rotateY', '90deg'],
                            ['rotateZ', '180deg'],
                        ];

                        for (var i = 0; i < transforms.length; i++) {
                            var prop = transforms[i][0],
                                    def = transforms[i][1],
                                    inverted = invert && prop !== 'scale';

                            effects[key] += parse(prop) ? prop + '(' + negate(parse(prop, true).val || def, inverted) + ') ' : '';
                        }
                    };

            effects.opacity = parse('fade') ? parse('fade', true).val || '0' : '1';

            buildTransform('transformIn');

            self.animation.reverseOut ? buildTransform('transformOut', true) : (effects.transformOut = effects.transformIn);

            effects.transition = {};

            effects.transition = self._getPrefixedCSS('transition', 'all ' + self.animation.duration + 'ms ' + self.animation.easing + ', opacity ' + self.animation.duration + 'ms linear');

            self.animation.stagger = parse('stagger') ? true : false;
            self.animation.staggerDuration = parseInt(parse('stagger') ? (parse('stagger', true).val ? parse('stagger', true).val : 100) : 100);

            return self._execFilter('_parseEffects', effects);
        },
        /**
         * Build State
         * @since 2.0.0
         * @param {boolean} future
         * @return {object} futureState
         */

        _buildState: function(future) {
            var self = this,
                    state = {};

            self._execAction('_buildState', 0);

            state = {
                activeFilter: self._activeFilter === '' ? 'none' : self._activeFilter,
                activeSort: future && self._newSortString ? self._newSortString : self._activeSort,
                fail: !self._$show.length && self._activeFilter !== '',
                $targets: self._$targets,
                $show: self._$show,
                $hide: self._$hide,
                totalTargets: self._$targets.length,
                totalShow: self._$show.length,
                totalHide: self._$hide.length,
                display: future && self._newDisplay ? self._newDisplay : self.layout.display
            };

            if (future) {
                return self._execFilter('_buildState', state);
            } else {
                self._state = state;

                self._execAction('_buildState', 1);
            }
        },
        /**
         * Go Mix
         * @since 2.0.0
         * @param {boolean} animate
         */

        _goMix: function(animate) {
            var self = this,
                    phase1 = function() {
                        if (self._chrome && (self._chrome === 31)) {
                            chromeFix(self._$parent[0]);
                        }

                        self._setInter();

                        phase2();
                    },
                    phase2 = function() {
                        var scrollTop = window.pageYOffset,
                                scrollLeft = window.pageXOffset,
                                docHeight = document.documentElement.scrollHeight;

                        self._getInterMixData();

                        self._setFinal();

                        self._getFinalMixData();

                        (window.pageYOffset !== scrollTop) && window.scrollTo(scrollLeft, scrollTop);

                        self._prepTargets();

                        if (window.requestAnimationFrame) {
                            requestAnimationFrame(phase3);
                        } else {
                            setTimeout(function() {
                                phase3();
                            }, 20);
                        }
                    },
                    phase3 = function() {
                        self._animateTargets();

                        if (self._targetsBound === 0) {
                            self._cleanUp();
                        }
                    },
                    chromeFix = function(grid) {
                        var parent = grid.parentElement,
                                placeholder = document.createElement('div'),
                                frag = document.createDocumentFragment();

                        parent.insertBefore(placeholder, grid);
                        frag.appendChild(grid);
                        parent.replaceChild(grid, placeholder);
                    },
                    futureState = self._buildState(true);

            self._execAction('_goMix', 0, arguments);

            !self.animation.duration && (animate = false);

            self._mixing = true;

            self._$container.removeClass(self.layout.containerClassFail);

            if (typeof self.callbacks.onMixStart === 'function') {
                self.callbacks.onMixStart.call(self._domNode, self._state, futureState, self);
            }

            self._$container.trigger('mixStart', [self._state, futureState, self]);

            self._getOrigMixData();

            if (animate && !self._suckMode) {

                window.requestAnimationFrame ?
                        requestAnimationFrame(phase1) :
                        phase1();

            } else {
                self._cleanUp();
            }

            self._execAction('_goMix', 1, arguments);
        },
        /**
         * Get Target Data
         * @since 2.0.0
         */

        _getTargetData: function(el, stage) {
            var self = this,
                    elStyle;

            el.dataset[stage + 'PosX'] = el.offsetLeft;
            el.dataset[stage + 'PosY'] = el.offsetTop;

            if (self.animation.animateResizeTargets) {
                elStyle = window.getComputedStyle(el);

                el.dataset[stage + 'MarginBottom'] = parseInt(elStyle.marginBottom);
                el.dataset[stage + 'MarginRight'] = parseInt(elStyle.marginRight);
                el.dataset[stage + 'Width'] = el.offsetWidth;
                el.dataset[stage + 'Height'] = el.offsetHeight;
            }
        },
        /**
         * Get Original Mix Data
         * @since 2.0.0
         */

        _getOrigMixData: function() {
            var self = this,
                    parentStyle = !self._suckMode ? window.getComputedStyle(self._$parent[0]) : {boxSizing: ''},
            parentBS = parentStyle.boxSizing || parentStyle[self._vendor + 'BoxSizing'];

            self._incPadding = (parentBS === 'border-box');

            self._execAction('_getOrigMixData', 0);

            !self._suckMode && (self.effects = self._parseEffects());

            self._$toHide = self._$hide.filter(':visible');
            self._$toShow = self._$show.filter(':hidden');
            self._$pre = self._$targets.filter(':visible');

            self._startHeight = self._incPadding ?
                    self._$parent.outerHeight() :
                    self._$parent.height();

            for (var i = 0; i < self._$pre.length; i++) {
                var el = self._$pre[i];

                self._getTargetData(el, 'orig');
            }

            self._execAction('_getOrigMixData', 1);
        },
        /**
         * Set Intermediate Positions
         * @since 2.0.0
         */

        _setInter: function() {
            var self = this;

            self._execAction('_setInter', 0);

            if (self._changingLayout && self.animation.animateChangeLayout) {
                self._$toShow.css('display', self._newDisplay);

                if (self._changingClass) {
                    self._$container
                            .removeClass(self.layout.containerClass)
                            .addClass(self._newClass);
                }
            } else {
                self._$toShow.css('display', self.layout.display);
            }

            self._execAction('_setInter', 1);
        },
        /**
         * Get Intermediate Mix Data
         * @since 2.0.0
         */

        _getInterMixData: function() {
            var self = this;

            self._execAction('_getInterMixData', 0);

            for (var i = 0; i < self._$toShow.length; i++) {
                var el = self._$toShow[i];

                self._getTargetData(el, 'inter');
            }

            for (var i = 0; i < self._$pre.length; i++) {
                var el = self._$pre[i];

                self._getTargetData(el, 'inter');
            }

            self._execAction('_getInterMixData', 1);
        },
        /**
         * Set Final Positions
         * @since 2.0.0
         */

        _setFinal: function() {
            var self = this;

            self._execAction('_setFinal', 0);

            self._sorting && self._printSort();

            self._$toHide.removeStyle('display');

            if (self._changingLayout && self.animation.animateChangeLayout) {
                self._$pre.css('display', self._newDisplay);
            }

            self._execAction('_setFinal', 1);
        },
        /**
         * Get Final Mix Data
         * @since 2.0.0
         */

        _getFinalMixData: function() {
            var self = this;

            self._execAction('_getFinalMixData', 0);

            for (var i = 0; i < self._$toShow.length; i++) {
                var el = self._$toShow[i];

                self._getTargetData(el, 'final');
            }

            for (var i = 0; i < self._$pre.length; i++) {
                var el = self._$pre[i];

                self._getTargetData(el, 'final');
            }

            self._newHeight = self._incPadding ?
                    self._$parent.outerHeight() :
                    self._$parent.height();

            self._sorting && self._printSort(true);

            self._$toShow.removeStyle('display');

            self._$pre.css('display', self.layout.display);

            if (self._changingClass && self.animation.animateChangeLayout) {
                self._$container
                        .removeClass(self._newClass)
                        .addClass(self.layout.containerClass);
            }

            self._execAction('_getFinalMixData', 1);
        },
        /**
         * Prepare Targets
         * @since 2.0.0
         */

        _prepTargets: function() {
            var self = this,
                    transformCSS = {
                        _in: self._getPrefixedCSS('transform', self.effects.transformIn),
                        _out: self._getPrefixedCSS('transform', self.effects.transformOut)
                    };

            self._execAction('_prepTargets', 0);

            if (self.animation.animateResizeContainer) {
                self._$parent.css('height', self._startHeight + 'px');
            }

            for (var i = 0; i < self._$toShow.length; i++) {
                var el = self._$toShow[i],
                        $el = $(el);

                el.style.opacity = self.effects.opacity;
                el.style.display = (self._changingLayout && self.animation.animateChangeLayout) ?
                        self._newDisplay :
                        self.layout.display;

                $el.css(transformCSS._in);

                if (self.animation.animateResizeTargets) {
                    el.style.width = el.dataset.finalWidth + 'px';
                    el.style.height = el.dataset.finalHeight + 'px';
                    el.style.marginRight = -(el.dataset.finalWidth - el.dataset.interWidth) + (el.dataset.finalMarginRight * 1) + 'px';
                    el.style.marginBottom = -(el.dataset.finalHeight - el.dataset.interHeight) + (el.dataset.finalMarginBottom * 1) + 'px';
                }
            }

            for (var i = 0; i < self._$pre.length; i++) {
                var el = self._$pre[i],
                        $el = $(el),
                        translate = {
                            x: el.dataset.origPosX - el.dataset.interPosX,
                            y: el.dataset.origPosY - el.dataset.interPosY
                        },
                transformCSS = self._getPrefixedCSS('transform', 'translate(' + translate.x + 'px,' + translate.y + 'px)');

                $el.css(transformCSS);

                if (self.animation.animateResizeTargets) {
                    el.style.width = el.dataset.origWidth + 'px';
                    el.style.height = el.dataset.origHeight + 'px';

                    if (el.dataset.origWidth - el.dataset.finalWidth) {
                        el.style.marginRight = -(el.dataset.origWidth - el.dataset.interWidth) + (el.dataset.origMarginRight * 1) + 'px';
                    }

                    if (el.dataset.origHeight - el.dataset.finalHeight) {
                        el.style.marginBottom = -(el.dataset.origHeight - el.dataset.interHeight) + (el.dataset.origMarginBottom * 1) + 'px';
                    }
                }
            }

            self._execAction('_prepTargets', 1);
        },
        /**
         * Animate Targets
         * @since 2.0.0
         */

        _animateTargets: function() {
            var self = this;

            self._execAction('_animateTargets', 0);

            self._targetsDone = 0;
            self._targetsBound = 0;

            self._$parent
                    .css(self._getPrefixedCSS('perspective', self.animation.perspectiveDistance + 'px'))
                    .css(self._getPrefixedCSS('perspective-origin', self.animation.perspectiveOrigin));

            if (self.animation.animateResizeContainer) {
                self._$parent
                        .css(self._getPrefixedCSS('transition', 'height ' + self.animation.duration + 'ms ease'))
                        .css('height', self._newHeight + 'px');
            }

            for (var i = 0; i < self._$toShow.length; i++) {
                var el = self._$toShow[i],
                        $el = $(el),
                        translate = {
                            x: el.dataset.finalPosX - el.dataset.interPosX,
                            y: el.dataset.finalPosY - el.dataset.interPosY
                        },
                delay = self._getDelay(i),
                        toShowCSS = {};

                el.style.opacity = '';

                for (var j = 0; j < 2; j++) {
                    var a = j === 0 ? a = self._prefix : '';

                    if (self._ff && self._ff <= 20) {
                        toShowCSS[a + 'transition-property'] = 'all';
                        toShowCSS[a + 'transition-timing-function'] = self.animation.easing + 'ms';
                        toShowCSS[a + 'transition-duration'] = self.animation.duration + 'ms';
                    }

                    toShowCSS[a + 'transition-delay'] = delay + 'ms';
                    toShowCSS[a + 'transform'] = 'translate(' + translate.x + 'px,' + translate.y + 'px)';
                }

                if (self.effects.transform || self.effects.opacity) {
                    self._bindTargetDone($el);
                }

                (self._ff && self._ff <= 20) ?
                        $el.css(toShowCSS) :
                        $el.css(self.effects.transition).css(toShowCSS);
            }

            for (var i = 0; i < self._$pre.length; i++) {
                var el = self._$pre[i],
                        $el = $(el),
                        translate = {
                            x: el.dataset.finalPosX - el.dataset.interPosX,
                            y: el.dataset.finalPosY - el.dataset.interPosY
                        },
                delay = self._getDelay(i);

                if (!(
                        el.dataset.finalPosX === el.dataset.origPosX &&
                        el.dataset.finalPosY === el.dataset.origPosY
                        )) {
                    self._bindTargetDone($el);
                }

                $el.css(self._getPrefixedCSS('transition', 'all ' + self.animation.duration + 'ms ' + self.animation.easing + ' ' + delay + 'ms'));
                $el.css(self._getPrefixedCSS('transform', 'translate(' + translate.x + 'px,' + translate.y + 'px)'));

                if (self.animation.animateResizeTargets) {
                    if (el.dataset.origWidth - el.dataset.finalWidth && el.dataset.finalWidth * 1) {
                        el.style.width = el.dataset.finalWidth + 'px';
                        el.style.marginRight = -(el.dataset.finalWidth - el.dataset.interWidth) + (el.dataset.finalMarginRight * 1) + 'px';
                    }

                    if (el.dataset.origHeight - el.dataset.finalHeight && el.dataset.finalHeight * 1) {
                        el.style.height = el.dataset.finalHeight + 'px';
                        el.style.marginBottom = -(el.dataset.finalHeight - el.dataset.interHeight) + (el.dataset.finalMarginBottom * 1) + 'px';
                    }
                }
            }

            if (self._changingClass) {
                self._$container
                        .removeClass(self.layout.containerClass)
                        .addClass(self._newClass);
            }

            for (var i = 0; i < self._$toHide.length; i++) {
                var el = self._$toHide[i],
                        $el = $(el),
                        delay = self._getDelay(i),
                        toHideCSS = {};

                for (var j = 0; j < 2; j++) {
                    var a = j === 0 ? a = self._prefix : '';

                    toHideCSS[a + 'transition-delay'] = delay + 'ms';
                    toHideCSS[a + 'transform'] = self.effects.transformOut;
                    toHideCSS.opacity = self.effects.opacity;
                }

                $el.css(self.effects.transition).css(toHideCSS);

                if (self.effects.transform || self.effects.opacity) {
                    self._bindTargetDone($el);
                }
                ;
            }

            self._execAction('_animateTargets', 1);

        },
        /**
         * Bind Targets TransitionEnd
         * @since 2.0.0
         * @param {object} $el
         */

        _bindTargetDone: function($el) {
            var self = this,
                    el = $el[0];

            self._execAction('_bindTargetDone', 0, arguments);

            if (!el.dataset.bound) {

                el.dataset.bound = true;
                self._targetsBound++;

                $el.on('webkitTransitionEnd.mixItUp transitionend.mixItUp', function(e) {
                    if (
                            (e.originalEvent.propertyName.indexOf('transform') > -1 ||
                                    e.originalEvent.propertyName.indexOf('opacity') > -1) &&
                            $(e.originalEvent.target).is(self.selectors.target)
                            ) {
                        $el.off('.mixItUp');
                        delete el.dataset.bound;
                        self._targetDone();
                    }
                });
            }

            self._execAction('_bindTargetDone', 1, arguments);
        },
        /**
         * Target Done
         * @since 2.0.0
         */

        _targetDone: function() {
            var self = this;

            self._execAction('_targetDone', 0);

            self._targetsDone++;

            (self._targetsDone === self._targetsBound) && self._cleanUp();

            self._execAction('_targetDone', 1);
        },
        /**
         * Clean Up
         * @since 2.0.0
         */

        _cleanUp: function() {
            var self = this,
                    targetStyles = self.animation.animateResizeTargets ? 'transform opacity width height margin-bottom margin-right' : 'transform opacity';
            unBrake = function() {
                self._$targets.removeStyle('transition', self._prefix);
            };

            self._execAction('_cleanUp', 0);

            !self._changingLayout ?
                    self._$show.css('display', self.layout.display) :
                    self._$show.css('display', self._newDisplay);

            self._$targets.css(self._brake);

            self._$targets
                    .removeStyle(targetStyles, self._prefix)
                    .removeAttr('data-inter-pos-x data-inter-pos-y data-final-pos-x data-final-pos-y data-orig-pos-x data-orig-pos-y data-orig-height data-orig-width data-final-height data-final-width data-inter-width data-inter-height data-orig-margin-right data-orig-margin-bottom data-inter-margin-right data-inter-margin-bottom data-final-margin-right data-final-margin-bottom');

            self._$hide.removeStyle('display');

            self._$parent.removeStyle('height transition perspective-distance perspective perspective-origin-x perspective-origin-y perspective-origin perspectiveOrigin', self._prefix);

            if (self._sorting) {
                self._printSort();
                self._activeSort = self._newSortString;
                self._sorting = false;
            }

            if (self._changingLayout) {
                if (self._changingDisplay) {
                    self.layout.display = self._newDisplay;
                    self._changingDisplay = false;
                }

                if (self._changingClass) {
                    self._$parent.removeClass(self.layout.containerClass).addClass(self._newClass);
                    self.layout.containerClass = self._newClass;
                    self._changingClass = false;
                }

                self._changingLayout = false;
            }

            self._refresh();

            self._buildState();

            if (self._state.fail) {
                self._$container.addClass(self.layout.containerClassFail);
            }

            self._$show = $();
            self._$hide = $();

            if (window.requestAnimationFrame) {
                requestAnimationFrame(unBrake);
            }

            self._mixing = false;

            if (typeof self.callbacks._user === 'function') {
                self.callbacks._user.call(self._domNode, self._state, self);
            }

            if (typeof self.callbacks.onMixEnd === 'function') {
                self.callbacks.onMixEnd.call(self._domNode, self._state, self);
            }

            self._$container.trigger('mixEnd', [self._state, self]);

            if (self._state.fail) {
                (typeof self.callbacks.onMixFail === 'function') && self.callbacks.onMixFail.call(self._domNode, self._state, self);
                self._$container.trigger('mixFail', [self._state, self]);
            }

            if (self._loading) {
                (typeof self.callbacks.onMixLoad === 'function') && self.callbacks.onMixLoad.call(self._domNode, self._state, self);
                self._$container.trigger('mixLoad', [self._state, self]);
            }

            if (self._queue.length) {
                self._execAction('_queue', 0);

                self.multiMix(self._queue[0][0], self._queue[0][1], self._queue[0][2]);
                self._queue.splice(0, 1);
            }

            self._execAction('_cleanUp', 1);

            self._loading = false;
        },
        /**
         * Get Prefixed CSS
         * @since 2.0.0
         * @param {string} property
         * @param {string} value
         * @param {boolean} prefixValue
         * @return {object} styles
         */

        _getPrefixedCSS: function(property, value, prefixValue) {
            var self = this,
                    styles = {};

            for (i = 0; i < 2; i++) {
                var prefix = i === 0 ? self._prefix : '';
                prefixValue ? styles[prefix + property] = prefix + value : styles[prefix + property] = value;
            }

            return self._execFilter('_getPrefixedCSS', styles, arguments);
        },
        /**
         * Get Delay
         * @since 2.0.0
         * @param {number} i
         * @return {number} delay
         */

        _getDelay: function(i) {
            var self = this,
                    n = typeof self.animation.staggerSequence === 'function' ? self.animation.staggerSequence.call(self._domNode, i, self._state) : i,
                    delay = self.animation.stagger ? n * self.animation.staggerDuration : 0;

            return self._execFilter('_getDelay', delay, arguments);
        },
        /**
         * Parse MultiMix Arguments
         * @since 2.0.0
         * @param {array} args
         * @return {object} output
         */

        _parseMultiMixArgs: function(args) {
            var self = this,
                    output = {
                        command: null,
                        animate: self.animation.enable,
                        callback: null
                    };

            for (var i = 0; i < args.length; i++) {
                var arg = args[i];

                if (arg !== null) {
                    if (typeof arg === 'object' || typeof arg === 'string') {
                        output.command = arg;
                    } else if (typeof arg === 'boolean') {
                        output.animate = arg;
                    } else if (typeof arg === 'function') {
                        output.callback = arg;
                    }
                }
            }

            return self._execFilter('_parseMultiMixArgs', output, arguments);
        },
        /**
         * Parse Insert Arguments
         * @since 2.0.0
         * @param {array} args
         * @return {object} output
         */

        _parseInsertArgs: function(args) {
            var self = this,
                    output = {
                        index: 0,
                        $object: $(),
                        multiMix: {filter: self._state.activeFilter},
                        callback: null
                    };

            for (var i = 0; i < args.length; i++) {
                var arg = args[i];

                if (typeof arg === 'number') {
                    output.index = arg;
                } else if (typeof arg === 'object' && arg instanceof $) {
                    output.$object = arg;
                } else if (typeof arg === 'object' && self._helpers._isElement(arg)) {
                    output.$object = $(arg);
                } else if (typeof arg === 'object' && arg !== null) {
                    output.multiMix = arg;
                } else if (typeof arg === 'boolean' && !arg) {
                    output.multiMix = false;
                } else if (typeof arg === 'function') {
                    output.callback = arg;
                }
            }

            return self._execFilter('_parseInsertArgs', output, arguments);
        },
        /**
         * Execute Action
         * @since 2.0.0
         * @param {string} methodName
         * @param {boolean} isPost
         * @param {array} args
         */

        _execAction: function(methodName, isPost, args) {
            var self = this,
                    context = isPost ? 'post' : 'pre';

            if (!self._actions.isEmptyObject && self._actions.hasOwnProperty(methodName)) {
                for (var key in self._actions[methodName][context]) {
                    self._actions[methodName][context][key].call(self, args);
                }
            }
        },
        /**
         * Execute Filter
         * @since 2.0.0
         * @param {string} methodName
         * @param {mixed} value
         * @return {mixed} value
         */

        _execFilter: function(methodName, value, args) {
            var self = this;

            if (!self._filters.isEmptyObject && self._filters.hasOwnProperty(methodName)) {
                for (var key in self._filters[methodName]) {
                    return self._filters[methodName][key].call(self, args);
                }
            } else {
                return value;
            }
        },
        /* Helpers
         ---------------------------------------------------------------------- */

        _helpers: {
            /**
             * CamelCase
             * @since 2.0.0
             * @param {string}
             * @return {string}
             */

            _camelCase: function(string) {
                return string.replace(/-([a-z])/g, function(g) {
                    return g[1].toUpperCase();
                });
            },
            /**
             * Is Element
             * @since 2.1.3
             * @param {object} element to test
             * @return {boolean}
             */

            _isElement: function(el) {
                if (window.HTMLElement) {
                    return el instanceof HTMLElement;
                } else {
                    return (
                            el !== null &&
                            el.nodeType === 1 &&
                            el.nodeName === 'string'
                            );
                }
            }
        },
        /* Public Methods
         ---------------------------------------------------------------------- */

        /**
         * Is Mixing
         * @since 2.0.0
         * @return {boolean}
         */

        isMixing: function() {
            var self = this;

            return self._execFilter('isMixing', self._mixing);
        },
        /**
         * Filter (public)
         * @since 2.0.0
         * @param {array} arguments
         */

        filter: function() {
            var self = this,
                    args = self._parseMultiMixArgs(arguments);

            self._clicking && (self._toggleString = '');

            self.multiMix({filter: args.command}, args.animate, args.callback);
        },
        /**
         * Sort (public)
         * @since 2.0.0
         * @param {array} arguments
         */

        sort: function() {
            var self = this,
                    args = self._parseMultiMixArgs(arguments);

            self.multiMix({sort: args.command}, args.animate, args.callback);
        },
        /**
         * Change Layout (public)
         * @since 2.0.0
         * @param {array} arguments
         */

        changeLayout: function() {
            var self = this,
                    args = self._parseMultiMixArgs(arguments);

            self.multiMix({changeLayout: args.command}, args.animate, args.callback);
        },
        /**
         * MultiMix
         * @since 2.0.0
         * @param {array} arguments
         */

        multiMix: function() {
            var self = this,
                    args = self._parseMultiMixArgs(arguments);

            self._execAction('multiMix', 0, arguments);

            if (!self._mixing) {
                if (self.controls.enable && !self._clicking) {
                    self.controls.toggleFilterButtons && self._buildToggleArray();
                    self._updateControls(args.command, self.controls.toggleFilterButtons);
                }

                (self._queue.length < 2) && (self._clicking = false);

                delete self.callbacks._user;
                if (args.callback)
                    self.callbacks._user = args.callback;

                var sort = args.command.sort,
                        filter = args.command.filter,
                        changeLayout = args.command.changeLayout;

                self._refresh();

                if (sort) {
                    self._newSort = self._parseSort(sort);
                    self._newSortString = sort;

                    self._sorting = true;
                    self._sort();
                }

                if (filter !== undf) {
                    filter = (filter === 'all') ? self.selectors.target : filter;

                    self._activeFilter = filter;
                }

                self._filter();

                if (changeLayout) {
                    self._newDisplay = (typeof changeLayout === 'string') ? changeLayout : changeLayout.display || self.layout.display;
                    self._newClass = changeLayout.containerClass || '';

                    if (
                            self._newDisplay !== self.layout.display ||
                            self._newClass !== self.layout.containerClass
                            ) {
                        self._changingLayout = true;

                        self._changingClass = (self._newClass !== self.layout.containerClass);
                        self._changingDisplay = (self._newDisplay !== self.layout.display);
                    }
                }

                self._$targets.css(self._brake);

                self._goMix(args.animate ^ self.animation.enable ? args.animate : self.animation.enable);

                self._execAction('multiMix', 1, arguments);

            } else {
                if (self.animation.queue && self._queue.length < self.animation.queueLimit) {
                    self._queue.push(arguments);

                    (self.controls.enable && !self._clicking) && self._updateControls(args.command);

                    self._execAction('multiMixQueue', 1, arguments);

                } else {
                    if (typeof self.callbacks.onMixBusy === 'function') {
                        self.callbacks.onMixBusy.call(self._domNode, self._state, self);
                    }
                    self._$container.trigger('mixBusy', [self._state, self]);

                    self._execAction('multiMixBusy', 1, arguments);
                }
            }
        },
        /**
         * Insert
         * @since 2.0.0
         * @param {array} arguments
         */

        insert: function() {
            var self = this,
                    args = self._parseInsertArgs(arguments),
                    callback = (typeof args.callback === 'function') ? args.callback : null,
                    frag = document.createDocumentFragment(),
                    target = (function() {
                        self._refresh();

                        if (self._$targets.length) {
                            return (args.index < self._$targets.length || !self._$targets.length) ?
                                    self._$targets[args.index] :
                                    self._$targets[self._$targets.length - 1].nextElementSibling;
                        } else {
                            return self._$parent[0].children[0];
                        }
                    })();

            self._execAction('insert', 0, arguments);

            if (args.$object) {
                for (var i = 0; i < args.$object.length; i++) {
                    var el = args.$object[i];

                    frag.appendChild(el);
                    frag.appendChild(document.createTextNode(' '));
                }

                self._$parent[0].insertBefore(frag, target);
            }

            self._execAction('insert', 1, arguments);

            if (typeof args.multiMix === 'object') {
                self.multiMix(args.multiMix, callback);
            }
        },
        /**
         * Prepend
         * @since 2.0.0
         * @param {array} arguments
         */

        prepend: function() {
            var self = this,
                    args = self._parseInsertArgs(arguments);

            self.insert(0, args.$object, args.multiMix, args.callback);
        },
        /**
         * Append
         * @since 2.0.0
         * @param {array} arguments
         */

        append: function() {
            var self = this,
                    args = self._parseInsertArgs(arguments);

            self.insert(self._state.totalTargets, args.$object, args.multiMix, args.callback);
        },
        /**
         * Get Option
         * @since 2.0.0
         * @param {string} string
         * @return {mixed} value
         */

        getOption: function(string) {
            var self = this,
                    getProperty = function(obj, prop) {
                        var parts = prop.split('.'),
                                last = parts.pop(),
                                l = parts.length,
                                i = 1,
                                current = parts[0] || prop;

                        while ((obj = obj[current]) && i < l) {
                            current = parts[i];
                            i++;
                        }

                        if (obj !== undf) {
                            return obj[last] !== undf ? obj[last] : obj;
                        }
                    };

            return string ? self._execFilter('getOption', getProperty(self, string), arguments) : self;
        },
        /**
         * Set Options
         * @since 2.0.0
         * @param {object} config
         */

        setOptions: function(config) {
            var self = this;

            self._execAction('setOptions', 0, arguments);

            typeof config === 'object' && $.extend(true, self, config);

            self._execAction('setOptions', 1, arguments);
        },
        /**
         * Get State
         * @since 2.0.0
         * @return {object} state
         */

        getState: function() {
            var self = this;

            return self._execFilter('getState', self._state, self);
        },
        /**
         * Force Refresh
         * @since 2.1.2
         */

        forceRefresh: function() {
            var self = this;

            self._refresh(false, true);
        },
        /**
         * Destroy
         * @since 2.0.0
         * @param {boolean} hideAll
         */

        destroy: function(hideAll) {
            var self = this;

            self._execAction('destroy', 0, arguments);

            self._$body
                    .add($(self.selectors.sort))
                    .add($(self.selectors.filter))
                    .off('.mixItUp');

            for (var i = 0; i < self._$targets.length; i++) {
                var target = self._$targets[i];

                hideAll && (target.style.display = '');

                delete target.mixParent;
            }

            self._execAction('destroy', 1, arguments);

            delete $.MixItUp.prototype._instances[self._id];
        }

    };

    /* jQuery Methods
     ---------------------------------------------------------------------- */

    /**
     * jQuery .mixItUp() method
     * @since 2.0.0
     * @extends $.fn
     */

    $.fn.mixItUp = function() {
        var args = arguments,
                dataReturn = [],
                eachReturn,
                _instantiate = function(domNode, settings) {
                    var instance = new $.MixItUp(),
                            rand = function() {
                                return ('00000' + (Math.random() * 16777216 << 0).toString(16)).substr(-6).toUpperCase();
                            };

                    instance._execAction('_instantiate', 0, arguments);

                    domNode.id = !domNode.id ? 'MixItUp' + rand() : domNode.id;

                    if (!instance._instances[domNode.id]) {
                        instance._instances[domNode.id] = instance;
                        instance._init(domNode, settings);
                    }

                    instance._execAction('_instantiate', 1, arguments);
                };

        eachReturn = this.each(function() {
            if (args && typeof args[0] === 'string') {
                var instance = $.MixItUp.prototype._instances[this.id];
                if (args[0] == 'isLoaded') {
                    dataReturn.push(instance ? true : false);
                } else {
                    var data = instance[args[0]](args[1], args[2], args[3]);
                    if (data !== undf)
                        dataReturn.push(data);
                }
            } else {
                _instantiate(this, args[0]);
            }
        });

        if (dataReturn.length) {
            return dataReturn.length > 1 ? dataReturn : dataReturn[0];
        } else {
            return eachReturn;
        }
    };

    /**
     * jQuery .removeStyle() method
     * @since 2.0.0
     * @extends $.fn
     */

    $.fn.removeStyle = function(style, prefix) {
        prefix = prefix ? prefix : '';

        return this.each(function() {
            var el = this,
                    styles = style.split(' ');

            for (var i = 0; i < styles.length; i++) {
                for (var j = 0; j < 2; j++) {
                    var prop = j ? styles[i] : prefix + styles[i];
                    if (
                            el.style[prop] !== undf &&
                            typeof el.style[prop] !== 'unknown' &&
                            el.style[prop].length > 0
                            ) {
                        el.style[prop] = '';
                    }
                    if (!prefix)
                        break;
                }
            }

            if (el.attributes && el.attributes.style && el.attributes.style !== undf && el.attributes.style.nodeValue === '') {
                el.attributes.removeNamedItem('style');
            }
        });
    };

})(jQuery);