/*! flip - v1.0.18 - 2015-11-12 * https://github.com/nnattawat/flip * Copyright (c) 2015 Nattawat Nonsung; Licensed MIT */ (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" : "click", 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.unbind('mouseleave', performUnflip); flip($dom); setTimeout(function () { $dom.bind('mouseleave', performUnflip); if (!$dom.is(":hover")) { unflip($dom); } }, (settings.speed + 150)); }; var performUnflip = function () { unflip($dom); }; $dom.mouseenter(performFlip); $dom.mouseleave(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); } }; }(jQuery));