/*! flip - v1.0.19 - 2016-01-04
* https://github.com/nnattawat/flip
* Copyright (c) 2016 Nattawat Nonsung; Licensed MIT */
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {
    var flip = function ($dom, callback) {
        $dom.data("flipped", true);

        var rotateAxis = "rotate" + $dom.data("axis");
        $dom.find($dom.data("front")).css({
            transform: rotateAxis + ($dom.data("reverse") ? "(-180deg)" : "(180deg)"),
            "z-index": "0"
        });

        $dom.find($dom.data("back")).css({
            transform: rotateAxis + "(0deg)",
            "z-index": "1"
        });

        //Providing a nicely wrapped up callback because transform is essentially async
        $dom.one(whichTransitionEvent(), function () {
            $(this).trigger('flip:done');
            if (callback !== undefined) {
                callback.call(this);
            }
        });
    };

    var unflip = function ($dom, callback) {
        $dom.data("flipped", false);

        var rotateAxis = "rotate" + $dom.data("axis");
        $dom.find($dom.data("front")).css({
            transform: rotateAxis + "(0deg)",
            "z-index": "1"
        });

        $dom.find($dom.data("back")).css({
            transform: rotateAxis + ($dom.data("reverse") ? "(180deg)" : "(-180deg)"),
            "z-index": "0"
        });

        //Providing a nicely wrapped up callback because transform is essentially async
        $dom.one(whichTransitionEvent(), function () {
            $(this).trigger('flip:done');
            if (callback !== undefined) {
                callback.call(this);
            }
        });
    };
    // Function from David Walsh: http://davidwalsh.name/css-animation-callback licensed with http://opensource.org/licenses/MIT
    var whichTransitionEvent = function () {
        var t,
            el = document.createElement("fakeelement"),
            transitions = {
                "transition": "transitionend",
                "OTransition": "oTransitionEnd",
                "MozTransition": "transitionend",
                "WebkitTransition": "webkitTransitionEnd"
            };

        for (t in transitions) {
            if (el.style[t] !== undefined) {
                return transitions[t];
            }
        }
    };
    $.fn.flip = function (options, callback) {
        if (typeof options == 'function') {
            //This allows flip to be called for setup with only a callback (default settings)
            callback = options;
        }
        this.each(function () {
            var $dom = $(this);

            if (options !== undefined && (typeof(options) == "boolean" || typeof(options) == "string")) { // Force flip the DOM
                if (options == "toggle") {
                    options = !$dom.data("flipped");
                }
                if (options) {
                    flip($dom, callback);
                } else {
                    unflip($dom, callback);
                }
                // //Providing a nicely wrapped up callback because transform is essentially async
                //  $(this).one(whichTransitionEvent(), function(){
                //     $(this).trigger('flip:done');
                //     if (callback !== undefined){
                //       callback.call(this);
                //     }
                //   });
            } else if (!$dom.data("initiated")) { //Init flipable DOM
                $dom.data("initiated", true);

                var settings = $.extend({
                    axis: "y",
                    reverse: false,
                    trigger: "click",
                    speed: 500,
                    forceHeight: false,
                    forceWidth: false,
                    autoSize: true,
                    front: 'auto',
                    back: 'auto'
                }, options);

                //By defualt we first check for the old front and back selectors for backward compatibility
                //if they arent there we fall back to auto selecting the first and second div
                if (settings.front == "auto") {
                    settings.front = ($dom.find('.front').length > 0) ? '.front' : 'div:first-child';
                } else if (settings.front == "autostrict") {
                    settings.front = 'div:first-child';
                }
                if (settings.back == "auto") {
                    //Note, we must use the old 'div:first-child + div' for IE compatibility
                    settings.back = ($dom.find('.back').length > 0) ? '.back' : 'div:first-child + div';
                } else if (settings.back == "autostrict") {
                    settings.back = 'div:first-child + div';
                }
                // save reverse and axis css to DOM for performing flip
                $dom.data("reverse", settings.reverse);
                $dom.data("axis", settings.axis);
                $dom.data("front", settings.front);
                $dom.data("back", settings.back);

                var rotateAxis = "rotate" + (settings.axis.toLowerCase() == "x" ? "x" : "y"),
                    perspective = $dom["outer" + (rotateAxis == "rotatex" ? "Height" : "Width")]() * 2;

                $dom.find($dom.data("back")).css({
                    transform: rotateAxis + "(" + (settings.reverse ? "180deg" : "-180deg") + ")"
                });

                $dom.css({
                    perspective: perspective,
                    position: "relative"
                });

                var speedInSec = settings.speed / 1000 || 0.5;
                var faces = $dom.find(settings.front).add(settings.back, $dom);
                if (settings.forceHeight) {
                    faces.outerHeight($dom.height());
                } else if (settings.autoSize) {
                    faces.css({'height': '100%'});
                }
                if (settings.forceWidth) {
                    faces.outerWidth($dom.width());
                } else if (settings.autoSize) {
                    faces.css({'width': '100%'});
                }
                faces.css({
                    "backface-visibility": "hidden",
                    "transform-style": "preserve-3d",
                    position: "absolute",
                    "z-index": "1"
                });
                faces.find('*').css({
                    "backface-visibility": "hidden"
                });
                $dom.find($dom.data("back")).css({
                    transform: rotateAxis + "(" + (settings.reverse ? "180deg" : "-180deg") + ")",
                    "z-index": "0"
                });

                // Back face always visible on Chrome #39
                if ((window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window) {
                    //Blink Engine, add preserve-3d to $dom
                    $dom.css({"-webkit-transform-style": "preserve-3d"});
                }
                // /#39

                // not forcing width/height may cause an initial flip to show up on
                // page load when we apply the style to reverse the backface...
                // To prevent this we first apply the basic styles and then give the
                // browser a moment to apply them. Only afterwards do we add the transition.
                setTimeout(function () {
                    // By now the browser should have applied the styles, so the transition
                    // will only affect subsequent flips.
                    faces.css({
                        transition: "all " + speedInSec + "s ease-out"
                    });
                    if (callback !== undefined) {
                        callback.call(this);
                    }
                    //While this used to work with a setTimeout of zero, at some point that became
                    //unstable and the initial flip returned. The reason for this is unknown but we
                    //will temporarily use a short delay of 20 to mitigate this issue.
                }, 20);

                if (settings.trigger.toLowerCase() == "click") {
                    $dom.on($.fn.tap ? "tap.flip" : "click.flip", function (event) {
                        if (!event) {
                            event = window.event;
                        }
                        if ($dom.find($(event.target).closest('button, a, input[type="submit"]')).length) {
                            return;
                        }

                        if ($dom.data("flipped")) {
                            unflip($dom);
                        } else {
                            flip($dom);
                        }
                    });
                }
                else if (settings.trigger.toLowerCase() == "hover") {
                    var performFlip = function () {
                        $dom.off('mouseleave.flip');

                        flip($dom);

                        setTimeout(function () {
                            $dom.on('mouseleave.flip', performUnflip);
                            if (!$dom.is(":hover")) {
                                unflip($dom);
                            }
                        }, (settings.speed + 150));
                    };

                    var performUnflip = function () {
                        unflip($dom);
                    };

                    $dom.on('mouseenter.flip', performFlip);
                    $dom.on('mouseleave.flip', performUnflip);
                }
            } else {
                //The element has been initiated, all we have to do is change applicable settings
                if (options && (options.axis !== undefined || options.reverse !== undefined)) {
                    changeSettings.call(this, options, function () {
                        $dom.trigger('flip:change');
                        if (callback !== undefined) {
                            callback.call(this);
                        }
                    });
                }
            }
        });

        return this;
    };
    var changeSettings = function (options, callback) {
        var changeNeeded = false;
        if (options.axis !== undefined && $(this).data("axis") != options.axis.toLowerCase()) {
            $(this).data("axis", options.axis.toLowerCase());
            changeNeeded = true;
        }
        if (options.reverse !== undefined && $(this).data("reverse") != options.reverse) {
            $(this).data("reverse", options.reverse);
            changeNeeded = true;
        }
        if (changeNeeded) {
            var faces = $(this).find($(this).data("front")).add($(this).data("back"), $(this));
            var savedTrans = faces.css("transition");
            faces.css({
                transition: "none"
            });
            //Only setting the axis if it needs to be

            //options.axis = options.axis.toLowerCase();
            //$(this).data("axis", options.axis);

            //This sets up the first flip in the new direction automatically
            var rotateAxis = "rotate" + $(this).data("axis");
            if ($(this).data("flipped")) {
                $(this).find($(this).data("front")).css({
                    transform: rotateAxis + ($(this).data("reverse") ? "(-180deg)" : "(180deg)"),
                    "z-index": "0"
                });
            } else {
                $(this).find($(this).data("back")).css({
                    transform: rotateAxis + "(" + ($(this).data("reverse") ? "180deg" : "-180deg") + ")",
                    "z-index": "0"
                });
            }
            //Providing a nicely wrapped up callback because transform is essentially async
            setTimeout(function () {
                faces.css({
                    transition: savedTrans
                });
                callback.call(this);
            }.bind(this), 0);
        } else {
            //If we didnt have to set the axis we can just call back.
            setTimeout(callback.bind(this), 0);
        }
    };
}));
