/**
 *
 * jPanelMenu 1.4.1 (http://jpanelmenu.com)
 * By Anthony Colangelo (http://acolangelo.com)
 *
 * */

(function ($) {
    $.jPanelMenu = function (options) {
        if (typeof (options) == "undefined" || options == null) {
            options = {};
        }

        var jP = {
            options: $.extend({
                menu: '#menu',
                panel: 'body',
                trigger: '.menu-trigger',
                excludedPanelContent: 'style, script',
                clone: true,
                keepEventHandlers: false,
                direction: 'left',
                openPosition: '250px',
                animated: true,
                closeOnContentClick: true,
                keyboardShortcuts: [
                    {
                        code: 27,
                        open: false,
                        close: true
                    },
                    {
                        code: 37,
                        open: false,
                        close: true
                    },
                    {
                        code: 39,
                        open: true,
                        close: true
                    },
                    {
                        code: 77,
                        open: true,
                        close: true
                    }
                ],
                duration: 150,
                openDuration: options.duration || 150,
                closeDuration: options.duration || 150,
                easing: 'ease-in-out',
                openEasing: options.easing || 'ease-in-out',
                closeEasing: options.easing || 'ease-in-out',
                before: function () {
                },
                beforeOpen: function () {
                },
                beforeClose: function () {
                },
                after: function () {
                },
                afterOpen: function () {
                },
                afterClose: function () {
                },
                beforeOn: function () {
                },
                afterOn: function () {
                },
                beforeOff: function () {
                },
                afterOff: function () {
                }
            }, options),
            settings: {
                transitionsSupported: 
                    'WebkitTransition' in document.body.style ||
                    'MozTransition' in document.body.style ||
                    'msTransition' in document.body.style ||
                    'OTransition' in document.body.style ||
                    'Transition' in document.body.style,
                transformsSupported:
                    'WebkitTransform' in document.body.style ||
                    'MozTransform' in document.body.style ||
                    'msTransform' in document.body.style ||
                    'OTransform' in document.body.style ||
                    'Transform' in document.body.style,
                cssPrefix: '',
                panelPosition: 'static',
                positionUnits: 'px'
            },
            menu: '#jPanelMenu-menu',
            panel: '.jPanelMenu-panel',
            timeouts: {},
            clearTimeouts: function () {
                clearTimeout(jP.timeouts.open);
                clearTimeout(jP.timeouts.afterOpen);
                clearTimeout(jP.timeouts.afterClose);
            },
            setPositionUnits: function () {
                var foundUnit = false,
                    allowedUnits = ['%', 'px', 'em'];

                for (var unitID = 0; unitID < allowedUnits.length; unitID++) {
                    var unit = allowedUnits[unitID];
                    if (jP.options.openPosition.toString().substr(-unit.length) == unit)
                    {
                        foundUnit = true;
                        jP.settings.positionUnits = unit;
                    }
                }

                if (!foundUnit) {
                    jP.options.openPosition = parseInt(jP.options.openPosition) + jP.settings.positionUnits;
                }
            },
            computePositionStyle: function (open, string) {
                var position = (open) ? jP.options.openPosition : '0' + jP.settings.positionUnits;
                var property = {};
                if (jP.settings.transformsSupported) {
                    var direction = (open && jP.options.direction == 'right') ? '-' : '';
                    var translate = 'translate3d(' + direction + position + ',0,0)';
                    var transform = 'transform';

                    if (string) {
                        property = '';
                        if (jP.settings.cssPrefix != '') {
                            property = jP.settings.cssPrefix + transform + ':' + translate + ';';
                        }
                        property += transform + ':' + translate + ';';
                    } else {
                        if (jP.settings.cssPrefix != '') {
                            property[jP.settings.cssPrefix + transform] = translate;
                        }
                        property[transform] = translate;
                    }
                } else {
                    if (string) {
                        property = '';
                        property = jP.options.direction + ': ' + position + ';';
                    } else {
                        property[jP.options.direction] = position;
                    }
                }
                return property;
            },
            setCSSPrefix: function () {
                jP.settings.cssPrefix = jP.getCSSPrefix();
            },
            setjPanelMenuStyles: function () {
                var bg = 'background:#fff',
                    htmlBG = $('html').css('background-color'),
                    bodyBG = $('body').css('background-color');

                var backgroundGenerator = function (element) {
                    var bgs = [];
                    $.each(['background-color', 'background-image', 'background-position', 'background-repeat', 'background-attachment', 'background-size', 'background-clip'], function (i, value) {
                        if (element.css(value) !== '') {
                            bgs.push(value + ':' + element.css(value));
                        }
                    });
                    return bgs.join(';');
                };

                if (bodyBG !== 'transparent' && bodyBG !== "rgba(0, 0, 0, 0)") {
                    bg = backgroundGenerator($('body'));
                } else if (htmlBG !== 'transparent' && htmlBG !== "rgba(0, 0, 0, 0)") {
                    bg = backgroundGenerator($('html'));
                }

                if ($('#jPanelMenu-style-master').length == 0) {
                    $('body').append('<style id="jPanelMenu-style-master">body{width:100%}.jPanelMenu,body{overflow-x:hidden}#jPanelMenu-menu{display:block;position:fixed;top:0;' + jP.options.direction + ':0;height:100%;z-index:-1;overflow-x:hidden;overflow-y:scroll;-webkit-overflow-scrolling:touch}.jPanelMenu-panel{position:static;' + jP.options.direction + ':0;top:0;z-index:2;width:100%;min-height:100%;' + bg + ';}</style>');
                }
            },
            setMenuState: function (open) {
                var position = (open) ? 'open' : 'closed';
                $(jP.options.panel).attr('data-menu-position', position);
            },
            getMenuState: function () {
                return $(jP.options.panel).attr('data-menu-position');
            },
            menuIsOpen: function () {
                if (jP.getMenuState() == 'open')
                    return true;
                else
                    return false;
            },
            setMenuStyle: function (styles) {
                $(jP.menu).css(styles);
            },
            setPanelStyle: function (styles) {
                $(jP.panel).css(styles);
            },
            showMenu: function () {
                jP.setMenuStyle({
                    display: 'block'
                });
                jP.setMenuStyle({
                    'z-index': '1'
                });
            },
            hideMenu: function () {
                jP.setMenuStyle({
                    'z-index': '-1'
                });
                jP.setMenuStyle({
                    display: 'none'
                });
            },
            enableTransitions: function (duration, easing) {
                var formattedDuration = duration / 1000;
                var formattedEasing = jP.getCSSEasingFunction(easing);
                jP.disableTransitions();
                $('body').append('<style id="jPanelMenu-style-transitions">.jPanelMenu-panel{' + jP.settings.cssPrefix + 'transition: all ' + formattedDuration + 's ' + formattedEasing + '; transition: all ' + formattedDuration + 's ' + formattedEasing + ';}</style>');
            },
            disableTransitions: function () {
                $('#jPanelMenu-style-transitions').remove();
            },
            getCSSEasingFunction: function (name) {
                switch (name)
                {
                    case 'linear':
                        return name;
                        break;

                    case 'ease':
                        return name;
                        break;

                    case 'ease-in':
                        return name;
                        break;

                    case 'ease-out':
                        return name;
                        break;

                    case 'ease-in-out':
                        return name;
                        break;

                    default:
                        return 'ease-in-out';
                        break;
                }
            },
            getJSEasingFunction: function (name) {
                switch (name)
                {
                    case 'linear':
                        return name;
                        break;

                    default:
                        return 'swing';
                        break;
                }
            },
            getVendorPrefix: function () {
                // Thanks to Lea Verou for this beautiful function. (http://lea.verou.me/2009/02/find-the-vendor-prefix-of-the-current-browser)
                if ('result' in arguments.callee)
                    return arguments.callee.result;

                var regex = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/;

                var someScript = document.getElementsByTagName('script')[0];

                for (var prop in someScript.style)
                {
                    if (regex.test(prop))
                    {
                        // test is faster than match, so it's better to perform
                        // that on the lot and match only when necessary
                        return arguments.callee.result = prop.match(regex)[0];
                    }

                }

                // Nothing found so far? Webkit does not enumerate over the CSS properties of the style object.
                // However (prop in style) returns the correct value, so we'll have to test for
                // the precence of a specific property
                if ('WebkitOpacity' in someScript.style)
                    return arguments.callee.result = 'Webkit';
                if ('KhtmlOpacity' in someScript.style)
                    return arguments.callee.result = 'Khtml';

                return arguments.callee.result = '';
            },
            getCSSPrefix: function () {
                var prefix = jP.getVendorPrefix();
                if (prefix != '') {
                    return '-' + prefix.toLowerCase() + '-';
                }
                return '';
            },
            openMenu: function (animated) {
                if (typeof (animated) == "undefined" || animated == null) {
                    animated = jP.options.animated
                }

                jP.clearTimeouts();

                jP.options.before();
                jP.options.beforeOpen();

                jP.setMenuState(true);

                jP.showMenu();

                var animationChecks = {
                    none: (!animated) ? true : false,
                    transitions: (animated && jP.settings.transitionsSupported) ? true : false
                };

                if (animationChecks.transitions || animationChecks.none) {
                    if (animationChecks.none)
                        jP.disableTransitions();
                    if (animationChecks.transitions)
                        jP.enableTransitions(jP.options.openDuration, jP.options.openEasing);

                    var newPanelStyle = jP.computePositionStyle(true);
                    jP.setPanelStyle(newPanelStyle);

                    jP.timeouts.afterOpen = setTimeout(function () {
                        jP.options.after();
                        jP.options.afterOpen();
                        jP.initiateContentClickListeners();
                    }, jP.options.openDuration);
                }
                else {
                    var formattedEasing = jP.getJSEasingFunction(jP.options.openEasing);

                    var animationOptions = {};
                    animationOptions[jP.options.direction] = jP.options.openPosition;
                    $(jP.panel).stop().animate(animationOptions, jP.options.openDuration, formattedEasing, function () {
                        jP.options.after();
                        jP.options.afterOpen();
                        jP.initiateContentClickListeners();
                    });
                }
            },
            closeMenu: function (animated) {
                if (typeof (animated) == "undefined" || animated == null) {
                    animated = jP.options.animated;
                }

                jP.clearTimeouts();

                jP.options.before();
                jP.options.beforeClose();

                jP.setMenuState(false);

                var animationChecks = {
                    none: (!animated) ? true : false,
                    transitions: (animated && jP.settings.transitionsSupported) ? true : false
                };

                if (animationChecks.transitions || animationChecks.none) {
                    if (animationChecks.none)
                        jP.disableTransitions();
                    if (animationChecks.transitions)
                        jP.enableTransitions(jP.options.closeDuration, jP.options.closeEasing);

                    var newPanelStyle = jP.computePositionStyle();
                    jP.setPanelStyle(newPanelStyle);

                    jP.timeouts.afterClose = setTimeout(function () {
                        jP.disableTransitions();

                        jP.hideMenu();
                        jP.options.after();
                        jP.options.afterClose();
                        jP.destroyContentClickListeners();
                    }, jP.options.closeDuration);
                }
                else {
                    var formattedEasing = jP.getJSEasingFunction(jP.options.closeEasing);

                    var animationOptions = {};
                    animationOptions[jP.options.direction] = 0 + jP.settings.positionUnits;
                    $(jP.panel).stop().animate(animationOptions, jP.options.closeDuration, formattedEasing, function () {
                        jP.hideMenu();
                        jP.options.after();
                        jP.options.afterClose();
                        jP.destroyContentClickListeners();
                    });
                }
            },
            triggerMenu: function (animated) {
                if (jP.menuIsOpen())
                    jP.closeMenu(animated);
                else
                    jP.openMenu(animated);
            },
            initiateClickListeners: function () {
                $(document).on('click touchend', jP.options.trigger, function (e) {
                    jP.triggerMenu(jP.options.animated);
                    e.preventDefault();
                });
            },
            destroyClickListeners: function () {
                $(document).off('click touchend', jP.options.trigger, null);
            },
            initiateContentClickListeners: function () {
                if (!jP.options.closeOnContentClick)
                    return false;

                $(document).on('click touchend', jP.panel, function (e) {
                    if (jP.menuIsOpen())
                        jP.closeMenu(jP.options.animated);
                    e.preventDefault();
                });
            },
            destroyContentClickListeners: function () {
                if (!jP.options.closeOnContentClick)
                    return false;

                $(document).off('click touchend', jP.panel, null);
            },
            initiateKeyboardListeners: function () {
                var preventKeyListeners = ['input', 'textarea', 'select'];
                $(document).on('keydown', function (e) {
                    var target = $(e.target),
                            prevent = false;

                    $.each(preventKeyListeners, function () {
                        if (target.is(this.toString())) {
                            prevent = true;
                        }
                    });

                    if (prevent)
                        return true;

                    for (mapping in jP.options.keyboardShortcuts) {
                        if (e.which == jP.options.keyboardShortcuts[mapping].code) {
                            var key = jP.options.keyboardShortcuts[mapping];

                            if (key.open && key.close) {
                                jP.triggerMenu(jP.options.animated);
                            }
                            else if ((key.open && !key.close) && !jP.menuIsOpen()) {
                                jP.openMenu(jP.options.animated);
                            }
                            else if ((!key.open && key.close) && jP.menuIsOpen()) {
                                jP.closeMenu(jP.options.animated);
                            }

                            e.preventDefault();
                        }
                    }
                });
            },
            destroyKeyboardListeners: function () {
                $(document).off('keydown', null);
            },
            setupMarkup: function () {
                $('html').addClass('jPanelMenu');
                $(jP.options.panel + ' > *').not(jP.menu + ', ' + jP.options.excludedPanelContent).wrapAll('<div class="' + jP.panel.replace('.', '') + '"/>');
                var menu = (jP.options.clone) ? $(jP.options.menu).clone(jP.options.keepEventHandlers) : $(jP.options.menu);
                menu.attr('id', jP.menu.replace('#', '')).insertAfter(jP.options.panel + ' > ' + jP.panel);
            },
            resetMarkup: function () {
                $('html').removeClass('jPanelMenu');
                $(jP.options.panel + ' > ' + jP.panel + ' > *').unwrap();
                $(jP.menu).remove();
            },
            init: function () {
                jP.options.beforeOn();

                jP.setPositionUnits();
                jP.setCSSPrefix();
                jP.initiateClickListeners();
                if (Object.prototype.toString.call(jP.options.keyboardShortcuts) === '[object Array]') {
                    jP.initiateKeyboardListeners();
                }

                jP.setjPanelMenuStyles();
                jP.setMenuState(false);
                jP.setupMarkup();

                jP.setPanelStyle({position: ((jP.options.animated && jP.settings.panelPosition === 'static') ? 'relative' : jP.settings.panelPosition)});
                jP.setMenuStyle({width: jP.options.openPosition});

                jP.closeMenu(false);

                jP.options.afterOn();
            },
            destroy: function () {
                jP.options.beforeOff();

                jP.closeMenu();
                jP.destroyClickListeners();
                if (Object.prototype.toString.call(jP.options.keyboardShortcuts) === '[object Array]') {
                    jP.destroyKeyboardListeners();
                }

                jP.resetMarkup();
                var childrenStyles = {};
                childrenStyles[jP.options.direction] = 'auto';

                jP.options.afterOff();
            }
        };

        return {
            on: jP.init,
            off: jP.destroy,
            trigger: jP.triggerMenu,
            open: jP.openMenu,
            close: jP.closeMenu,
            isOpen: jP.menuIsOpen,
            menu: jP.menu,
            getMenu: function () {
                return $(jP.menu);
            },
            panel: jP.panel,
            getPanel: function () {
                return $(jP.panel);
            },
            setPosition: function (position) {
                if (typeof (position) == "undefined" || position == null) {
                    position = jP.options.openPosition;
                }
                jP.options.openPosition = position;
                jP.setMenuStyle({width: jP.options.openPosition});
            }
        };
    };
})(jQuery);