/**
* @name hoverdir
* @description description
* @version 1.0
* @options
* speed
* easing
* hoverDelay
* inverse
* hoverEle
* @methods
* init
* supportTransition
* loadEvents
* getDir
* getStyle
* showHover
* hideHover
* applyAnimation
* destroy
*/
;(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports !== 'undefined') {
module.exports = factory(require('jquery'));
} else {
factory(jQuery);
}
})(function($) {
'use strict';
var pluginName = 'hoverdir';
function HoverDir(element, options) {
this.element = $(element);
this.options = $.extend({}, $.fn[pluginName].defaults, this.element.data(), options);
this.init();
}
HoverDir.prototype = {
init: function() {
// initialize visibility to false for show and hide method
this.isVisible = false;
// transition properties
this.transitionProp = 'all ' + this.options.speed + 'ms ' + this.options.easing;
// get the hover element
this.$hoverEle = this.element.find(this.options.hoverEle);
// check CSS transition support
this.support = this.supportTransition;
// load the events
this.loadEvents();
},
/**
* Detect if CSS transition is supported
*
* @return {Boolean}
*/
supportTransition: function() {
var b = document.body || document.documentElement,
s = b.style,
p = 'transition';
if (typeof s[p] === 'string') {
return true;
}
var pfx = ['Moz', 'webkit', 'Webkit', 'Khtml', 'O', 'ms'];
p = p.replace(/\b\w/g, function(l) {
return l.toUpperCase();
});
for (var i in pfx) {
if (typeof s[pfx[i] + p] === 'string') {
console.log(s[pfx[i] + p]);
return true;
}
}
return false;
},
/**
* Bind the events to the element
*/
loadEvents: function() {
var that = this;
var $el = this.element;
$el.on('mouseenter.' + pluginName + ' mouseleave.' + pluginName, function(event) {
that.direction = that.getDir( { x : event.pageX, y : event.pageY } );
that.cssStyle = that.getStyle(that.direction);
if (event.type === 'mouseenter') {
that.showHover();
} else {
that.hideHover();
}
});
},
/**
* get the direction when the event is triggered
* credits : http://stackoverflow.com/a/3647634
*
* @param {Object} coordinates
* @returns {Interger}
*/
getDir: function(coordinates) {
// the width and height of the current element
var $el = this.element;
var w = $el.width(),
h = $el.height(),
// calculate the x and y to get an angle to the center of the div from that x and y.
// gets the x value relative to the center of the DIV and "normalize" it
x = (coordinates.x - $el.offset().left - (w / 2)) * (w > h ? (h / w) : 1),
y = (coordinates.y - $el.offset().top - (h / 2)) * (h > w ? (w / h) : 1),
// the angle and the direction from where the mouse came in/went out clockwise (TRBL=0123);
// first calculate the angle of the point,
// add 180 deg to get rid of the negative values
// divide by 90 to get the quadrant
// add 3 and do a modulo by 4 to shift the quadrants to a proper clockwise TRBL (top/right/bottom/left) **/
direction = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180) / 90) + 3) % 4;
return direction;
},
/**
* get the style when the event is triggered
*
* @param {Interger} direction
* @returns {Object}
*/
getStyle: function(direction) {
var from,
to,
fromTop = {'left': '0', 'top': '-100%'},
fromRight = {'left': '100%', 'top': '0'},
fromBottom = {'left': '0', 'top': '100%'},
fromLeft = {'left': '-100%', 'top': '0'},
toTop = {'top': '0'},
toLeft = {'left': '0'};
if (this.options.inverse) {
fromTop = [fromBottom, fromBottom = fromTop][0];
fromRight = [fromLeft, fromLeft = fromRight][0];
}
switch( direction ) {
case 0:
// from top
from = fromTop;
to = toTop;
break;
case 1:
// from right
from = fromRight;
to = toLeft;
break;
case 2:
// from bottom
from = fromBottom;
to = toTop;
break;
case 3:
// from left
from = fromLeft;
to = toLeft;
break;
}
return {from: from, to: to};
},
/**
* Show the hover of the element
*/
showHover: function() {
var that = this;
if (this.support) {
that.$hoverEle.css('transition', '');
}
this.$hoverEle.hide().css(this.cssStyle.from);
clearTimeout(this.tmhover);
this.tmhover = setTimeout(function() {
that.$hoverEle.show(0, function() {
if (this.support) {
that.$hoverEle.css('transition', that.transitionProp);
}
that.applyAnimation(that.cssStyle.to);
});
}, this.options.hoverDelay);
this.isVisible = true;
},
/**
* Hide the hover to the element
*/
hideHover: function() {
var that = this;
if (this.support) {
that.$hoverEle.css('transition', that.transitionProp);
}
clearTimeout(this.tmhover);
that.applyAnimation(that.cssStyle.from);
this.isVisible = false;
},
/**
* Apply a transition or fallback to jquery animate based on css transition supported
*
* @param {Object} styleCSS
*/
applyAnimation: function(style) {
$.fn.applyStyle = this.support ? $.fn.css : $.fn.animate;
this.$hoverEle.stop().applyStyle(style, $.extend(true, [], {duration: this.options.speed}));
},
/**
* Unbinds the plugin.
*/
destroy: function() {
$.removeData(this.element[0], pluginName);
}
};
$.fn[pluginName] = function(options, params) {
return this.each(function() {
var instance = $.data(this, pluginName);
if (!instance) {
$.data(this, pluginName, new HoverDir(this, options));
} else if (instance[options]) {
instance[options](params);
}
});
};
$.fn[pluginName].defaults = {
speed: 300,
easing: 'ease',
hoverDelay: 0,
inverse: false,
hoverEle: 'div'
};
$(function() {
$('[data-' + pluginName + ']').on('customEvent', function() {
});
$('[data-' + pluginName + ']')[pluginName]({
});
});
});