sp-codes.de/bower_components/flip/src/flip.js

367 lines
10 KiB
JavaScript

(function( $ ) {
/*
* Private attributes and method
*/
// 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];
}
}
};
/*
* Model declaration
*/
var Flip = function($el, options, callback) {
// Define default setting
this.setting = {
axis: "y",
reverse: false,
trigger: "click",
speed: 500,
forceHeight: false,
forceWidth: false,
autoSize: true,
front: '.front',
back: '.back'
};
this.setting = $.extend(this.setting, options);
if (typeof options.axis === 'string' && (options.axis.toLowerCase() === 'x' || options.axis.toLowerCase() === 'y')) {
this.setting.axis = options.axis.toLowerCase();
}
if (typeof options.reverse === "boolean") {
this.setting.reverse = options.reverse;
}
if (typeof options.trigger === 'string') {
this.setting.trigger = options.trigger.toLowerCase();
}
var speed = parseInt(options.speed);
if (!isNaN(speed)) {
this.setting.speed = speed;
}
if (typeof options.forceHeight === "boolean") {
this.setting.forceHeight = options.forceHeight;
}
if (typeof options.forceWidth === "boolean") {
this.setting.forceWidth = options.forceWidth;
}
if (typeof options.autoSize === "boolean") {
this.setting.autoSize = options.autoSize;
}
if (typeof options.front === 'string' || options.front instanceof $) {
this.setting.front = options.front;
}
if (typeof options.back === 'string' || options.back instanceof $) {
this.setting.back = options.back;
}
// Other attributes
this.element = $el;
this.frontElement = this.getFrontElement();
this.backElement = this.getBackElement();
this.isFlipped = false;
this.init(callback);
};
/*
* Public methods
*/
$.extend(Flip.prototype, {
flipDone: function(callback) {
var self = this;
// Providing a nicely wrapped up callback because transform is essentially async
self.element.one(whichTransitionEvent(), function() {
self.element.trigger('flip:done');
if (typeof callback === 'function') {
callback.call(self.element);
}
});
},
flip: function(callback) {
if (this.isFlipped) {
return;
}
this.isFlipped = true;
var rotateAxis = "rotate" + this.setting.axis;
this.frontElement.css({
transform: rotateAxis + (this.setting.reverse ? "(-180deg)" : "(180deg)"),
"z-index": "0"
});
this.backElement.css({
transform: rotateAxis + "(0deg)",
"z-index": "1"
});
this.flipDone(callback);
},
unflip: function(callback) {
if (!this.isFlipped) {
return;
}
this.isFlipped = false;
var rotateAxis = "rotate" + this.setting.axis;
this.frontElement.css({
transform: rotateAxis + "(0deg)",
"z-index": "1"
});
this.backElement.css({
transform: rotateAxis + (this.setting.reverse ? "(180deg)" : "(-180deg)"),
"z-index": "0"
});
this.flipDone(callback);
},
getFrontElement: function() {
if (this.setting.front instanceof $) {
return this.setting.front;
} else {
return this.element.find(this.setting.front);
}
},
getBackElement: function() {
if (this.setting.back instanceof $) {
return this.setting.back;
} else {
return this.element.find(this.setting.back);
}
},
init: function(callback) {
var self = this;
var faces = self.frontElement.add(self.backElement);
var rotateAxis = "rotate" + self.setting.axis;
var perspective = self.element["outer" + (rotateAxis === "rotatex" ? "Height" : "Width")]() * 2;
var elementCss = {
'perspective': perspective,
'position': 'relative'
};
var backElementCss = {
"transform": rotateAxis + "(" + (self.setting.reverse ? "180deg" : "-180deg") + ")",
"z-index": "0",
"position": "relative"
};
var faceElementCss = {
"backface-visibility": "hidden",
"transform-style": "preserve-3d",
"position": "absolute",
"z-index": "1"
};
if (self.setting.forceHeight) {
faces.outerHeight(self.element.height());
} else if (self.setting.autoSize) {
faceElementCss.height = '100%';
}
if (self.setting.forceWidth) {
faces.outerWidth(self.element.width());
} else if (self.setting.autoSize) {
faceElementCss.width = '100%';
}
// Back face always visible on Chrome #39
if ((window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window) {
//Blink Engine, add preserve-3d to self.element
elementCss["-webkit-transform-style"] = "preserve-3d";
}
faces.css(faceElementCss).find('*').css({
"backface-visibility": "hidden"
});
self.element.css(elementCss);
self.backElement.css(backElementCss);
// #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 self 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.
var speedInSec = self.setting.speed / 1000 || 0.5;
faces.css({
"transition": "all " + speedInSec + "s ease-out"
});
// This allows flip to be called for setup with only a callback (default settings)
if (typeof callback === 'function') {
callback.call(self.element);
}
// 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);
self.attachEvents();
},
clickHandler: function(event) {
if (!event) { event = window.event; }
if (this.element.find($(event.target).closest('button, a, input[type="submit"]')).length) {
return;
}
if (this.isFlipped) {
this.unflip();
} else {
this.flip();
}
},
hoverHandler: function() {
var self = this;
self.element.off('mouseleave.flip');
self.flip();
setTimeout(function() {
self.element.on('mouseleave.flip', $.proxy(self.unflip, self));
if (!self.element.is(":hover")) {
self.unflip();
}
}, (self.setting.speed + 150));
},
attachEvents: function() {
var self = this;
if (self.setting.trigger === "click") {
self.element.on($.fn.tap ? "tap.flip" : "click.flip", $.proxy(self.clickHandler, self));
} else if (self.setting.trigger === "hover") {
self.element.on('mouseenter.flip', $.proxy(self.hoverHandler, self));
self.element.on('mouseleave.flip', $.proxy(self.unflip, self));
}
},
flipChanged: function(callback) {
this.element.trigger('flip:change');
if (typeof callback === 'function') {
callback.call(this.element);
}
},
changeSettings: function(options, callback) {
var self = this;
var changeNeeded = false;
if (options.axis !== undefined && self.setting.axis !== options.axis.toLowerCase()) {
self.setting.axis = options.axis.toLowerCase();
changeNeeded = true;
}
if (options.reverse !== undefined && self.setting.reverse !== options.reverse) {
self.setting.reverse = options.reverse;
changeNeeded = true;
}
if (changeNeeded) {
var faces = self.frontElement.add(self.backElement);
var savedTrans = faces.css(["transition-property", "transition-timing-function", "transition-duration", "transition-delay"]);
faces.css({
transition: "none"
});
// This sets up the first flip in the new direction automatically
var rotateAxis = "rotate" + self.setting.axis;
if (self.isFlipped) {
self.frontElement.css({
transform: rotateAxis + (self.setting.reverse ? "(-180deg)" : "(180deg)"),
"z-index": "0"
});
} else {
self.backElement.css({
transform: rotateAxis + (self.setting.reverse ? "(180deg)" : "(-180deg)"),
"z-index": "0"
});
}
// Providing a nicely wrapped up callback because transform is essentially async
setTimeout(function() {
faces.css(savedTrans);
self.flipChanged(callback);
}, 0);
} else {
// If we didnt have to set the axis we can just call back.
self.flipChanged(callback);
}
}
});
/*
* jQuery collection methods
*/
$.fn.flip = function (options, callback) {
if (typeof options === 'function') {
callback = options;
}
if (typeof options === "string" || typeof options === "boolean") {
this.each(function() {
var flip = $(this).data('flip-model');
if (options === "toggle") {
options = !flip.isFlipped;
}
if (options) {
flip.flip(callback);
} else {
flip.unflip(callback);
}
});
} else {
this.each(function() {
if ($(this).data('flip-model')) { // The element has been initiated, all we have to do is change applicable settings
var flip = $(this).data('flip-model');
if (options && (options.axis !== undefined || options.reverse !== undefined)) {
flip.changeSettings(options, callback);
}
} else { // Init
$(this).data('flip-model', new Flip($(this), (options || {}), callback));
}
});
}
return this;
};
}( jQuery ));