(function(win, doc, $) {
var animationSupport = false,
animationString = 'animation',
vendorPrefix = prefix = '',
domPrefixes = ['Webkit', 'Moz', 'O', 'ms', 'Khtml'],
//match ie 10 & 11 exclusively
ie1011 = (win.matchMedia && win.matchMedia('all and (-ms-high-contrast: none), (-ms-high-contrast: active)').matches),
$win = $(win),
head = doc.getElementsByTagName('head')[0]
// $.fn.load is deprecated, https://api.jquery.com/load-event/, use '.on' instead
$win.on('load', function(){
var style = doc.body.style;
if( style.animationName !== undefined ) { animationSupport = true; }
if( animationSupport === false ) {
for( var i = 0; i < domPrefixes.length; i++ ) {
if( style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) {
prefix = domPrefixes[ i ];
animationString = prefix + 'Animation';
vendorPrefix = '-' + prefix.toLowerCase() + '-';
animationSupport = true;
break;
}
}
}
});
var
//update style tags with data-media attr
ie1011iterateStyles = function(){
$.each( $('[data-media]'), function(index,elem){
$el = $(elem);
if( win.matchMedia($el.attr('data-media')).matches ){
$el.html($el.data('keyframedata'));//set keyframe that should apply according to media query
} else {
$el.data('keyframedata', $el.html() );
$el.html('');//take back keyframe that should not apply according to media query
}
});
},
// debounce timeout
dbnTout,
// debounce ie1011iterateStyles function
ie1011iterateStylesProxy = function(){
win.clearTimeout(dbnTout);
dbnTout = win.setTimeout( ie1011iterateStyles, 34 );// wait approx two frames
},
$createKeyframeStyleTag = function(id, css, ie1011Media) {
if($.keyframe.debug){ win.console.log(id + " " + css); }
// issue #53
if(ie1011 && ie1011Media){
var $style = $('').attr({
"class": "keyframe-style",
id: id,
type: "text/css",
'data-media' : ie1011Media
});
if(win.matchMedia(ie1011Media).matches){
$style.html(css);
} else {
$style.data('keyframedata', css);
}
$win.on('resize', ie1011iterateStylesProxy)
.on('orientationchange', ie1011iterateStylesProxy);
return $style.appendTo(head);
}
return $("").attr({
"class": "keyframe-style",
id: id,
type: "text/css"
}).appendTo(head);
};
$.keyframe = {
debug: false,
getVendorPrefix: function() {
return vendorPrefix;
},
isSupported: function() {
return animationSupport;
},
generate: function(frameData) {
var frameName = frameData.name || "";
var css = "@" + vendorPrefix + "keyframes " + frameName + " {";
for (var key in frameData) {
if (key !== "name" && key !== "media" && key !== "complete") {
css += key + " {";
for (var property in frameData[key]) {
css += property + ":" + frameData[key][property] + ";";
}
css += "}";
}
}
if(win.PrefixFree)
css = win.PrefixFree.prefixCSS(css + "}");
else
css += "}";
// issue #53: if it's is responsive @keyframe, but NOT ie 10/11
if(frameData.media && !ie1011 ){
css = "@media " + frameData.media + "{" + css + "}";
}
var $frameStyle = $("style#" + frameData.name);
if ($frameStyle.length > 0) {
$frameStyle.html(css);
// this needs HUGE optimization, maybe to add specific class to animation targets, so
// we can retrieve them easely with .classname selector
var $elems = $("*").filter(function() {
return this.style[animationString + "Name"] === frameName;
});
$elems.each(function() {
var $el = $(this);
var options = $el.data("keyframeOptions");
$el.resetKeyframe(function() {
$el.playKeyframe(options);
});
});
} else {
// issue #53. pass frameData.media as third argument to @keyframe style tag creation mechanism...
if(ie1011 && frameData.media ){
$createKeyframeStyleTag(frameName, css, frameData.media);
} else {
$createKeyframeStyleTag(frameName, css);
}
}
},
define: function(frameData) {
if (frameData.length) {
for (var i = 0; i < frameData.length; i++) {
var frame = frameData[i];
this.generate(frame);
}
} else {
this.generate(frameData);
}
}
};
var animationPlayState = "animation-play-state";
var playStateRunning = "running";
$.fn.resetKeyframe = function(callback) {
var $el = $(this).css(vendorPrefix + animationPlayState, playStateRunning).css(vendorPrefix + "animation", "none");
if (callback) {
win.setTimeout(callback, 1);
}
};
$.fn.pauseKeyframe = function() {
$(this).css(vendorPrefix + animationPlayState, "paused");
};
$.fn.resumeKeyframe = function() {
$(this).css(vendorPrefix + animationPlayState, playStateRunning);
};
$.fn.playKeyframe = function(frameOptions, callback) {
var animObjToStr = function(obj){
obj = $.extend({
duration: '0s',
timingFunction: "ease",
delay: '0s',
iterationCount: 1,
direction: "normal",
fillMode: "forwards"
}, obj);
return [obj.name, obj.duration, obj.timingFunction, obj.delay, obj.iterationCount, obj.direction, obj.fillMode].join(" ");
};
var animationcss = "";
if($.isArray(frameOptions)){
var frameOptionsStrings = [];
for(var i = 0; i < frameOptions.length; i++){
if (typeof frameOptions[i] === 'string') {
frameOptionsStrings.push(frameOptions[i]);
}else{
frameOptionsStrings.push(animObjToStr(frameOptions[i]));
}
}
animationcss = frameOptionsStrings.join(", ");
}else if (typeof frameOptions === 'string') {
animationcss = frameOptions;
}else{
animationcss = animObjToStr(frameOptions);
}
var animationkey = vendorPrefix + "animation";
var pfx = ["webkit", "moz", "MS", "o", ""];
if(!callback && frameOptions.complete){
callback = frameOptions.complete;
}
var _prefixEvent = function(element, type, callback) {
for(var i = 0; i < pfx.length; i++){
if (!pfx[i]) {
type = type.toLowerCase();
}
var evt = pfx[i] + type;
element.off(evt).on(evt, callback);
}
};
this.each(function() {
var $el = $(this).addClass("boostKeyframe").css(vendorPrefix + animationPlayState, playStateRunning).css(animationkey, animationcss).data("keyframeOptions", frameOptions);
if($.keyframe.debug){
console.group();
if(vendorPrefix){ console.log("Vendor Prefix: " + vendorPrefix); }
console.log("Style Applied: " + animationcss);
var testCss = $el.css(animationkey);
console.log("Rendered Style: " + (testCss ? testCss : $el[0].style.animation));
console.groupEnd();
}
if (callback) {
_prefixEvent($el, 'AnimationIteration', callback);
_prefixEvent($el, 'AnimationEnd', callback);
}
});
return this;
};
$createKeyframeStyleTag("boost-keyframe", " .boostKeyframe{" + vendorPrefix + "transform:scale3d(1,1,1);}");
})(window,document,jQuery);