You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

350 lines
9.9 KiB

/* ******************************
Nag
Author: Jack Lukic
Notes: First Commit July 19, 2012
Simple fixed position nag
****************************** */
;(function ($, window, document, undefined) {
$.fn.nag = function(parameters) {
var
settings = $.extend(true, {}, $.fn.nag.settings, parameters),
// hoist arguments
moduleArguments = arguments || false
;
$(this)
.each(function() {
var
$module = $(this),
$close = $module.find(settings.selector.close),
$context = $(settings.context),
instance = $module.data('module'),
className = settings.className,
moduleOffset,
moduleHeight,
contextWidth,
contextHeight,
contextOffset,
yOffset,
yPosition,
timer,
module,
requestAnimationFrame = window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame
|| function(callback) { setTimeout(callback, 0); }
;
module = {
initialize: function() {
// calculate module offset once
moduleOffset = $module.offset();
moduleHeight = $module.outerHeight();
contextWidth = $context.outerWidth();
contextHeight = $context.outerHeight();
contextOffset = $context.offset();
$module
.data('module', module)
;
$close
.on('mouseenter mouseleave', module.event.hover)
.on('click', module.dismiss)
;
// lets avoid javascript if we dont need to reposition
if(settings.context == window && settings.position == 'fixed') {
$module
.addClass(className.fixed)
;
}
if(settings.sticky) {
// retrigger on scroll for absolute
if(settings.position == 'absolute') {
$context
.on('scroll resize', module.event.scroll)
;
}
// fixed is always relative to window
else {
$(window)
.on('scroll resize', module.event.scroll)
;
}
// fire once to position on init
$.proxy(module.event.scroll, this)();
}
if(settings.followLink) {
$module
.on('mouseenter mouseleave', module.event.hover)
.on('click', module.followLink)
;
}
if(settings.displayTime > 0) {
setTimeout(module.hide, settings.displayTime);
}
if(module.should.show()) {
if( !$module.is(':visible') ) {
module.show();
}
}
else {
module.hide();
}
},
refresh: function() {
moduleOffset = $module.offset();
moduleHeight = $module.outerHeight();
contextWidth = $context.outerWidth();
contextHeight = $context.outerHeight();
contextOffset = $context.offset();
},
show: function() {
if($.fn.popIn !== undefined) {
$module
.popIn(settings.duration)
;
}
else {
$module
.fadeIn(settings.duration, settings.easing)
;
}
},
hide: function() {
$module
.fadeOut(settings.duration, settings.easing)
;
},
stick: function() {
module.refresh();
if(settings.position == 'fixed') {
var
windowScroll = $(window).prop('pageYOffset') || $(window).scrollTop(),
fixedOffset = ( $module.hasClass(className.bottom) )
? contextOffset.top + (contextHeight - moduleHeight) - windowScroll
: contextOffset.top - windowScroll
;
$module
.css({
position : 'fixed',
top : fixedOffset,
left : contextOffset.left,
width : contextWidth - settings.scrollBarWidth
})
;
}
else {
$module
.css({
top : yPosition
})
;
}
},
unStick: function() {
$module
.css({
top : ''
})
;
},
dismiss: function() {
if(settings.storageMethod) {
module.storage.set(settings.storedKey, settings.storedValue);
}
module.hide();
},
should: {
show: function() {
if( module.storage.get(settings.storedKey) == settings.storedValue) {
return false;
}
return true;
},
stick: function() {
yOffset = $context.prop('pageYOffset') || $context.scrollTop();
yPosition = ( $module.hasClass(className.bottom) )
? (contextHeight - $module.outerHeight() ) + yOffset
: yOffset
;
// absolute position calculated when y offset met
if(yPosition > moduleOffset.top) {
return true;
}
else if(settings.position == 'fixed') {
return true;
}
return false;
}
},
followLink: function() {
if($.fn.followLink !== undefined) {
$module
.followLink()
;
}
},
storage: {
set: function(key, value) {
if(settings.storageMethod == 'local' && store !== undefined) {
store.set(key, value);
}
// store by cookie
else if($.cookie !== undefined) {
$.cookie(key, value);
}
else {
module.error(settings.errors.noStorage);
}
},
get: function(key) {
if(settings.storageMethod == 'local' && store !== undefined) {
return store.get(key);
}
// get by cookie
else if($.cookie !== undefined) {
return $.cookie(key);
}
else {
module.error(settings.errors.noStorage);
}
}
},
event: {
hover: function() {
$(this)
.toggleClass(className.hover)
;
},
scroll: function() {
if(timer !== undefined) {
clearTimeout(timer);
}
timer = setTimeout(function() {
if(module.should.stick() ) {
requestAnimationFrame(module.stick);
}
else {
module.unStick();
}
}, settings.lag);
}
},
error: function(error) {
console.log('Nag Module:' + error);
},
// allows for dot notation method calls
invoke: function(methodName, context, methodArguments) {
var
method
;
methodArguments = methodArguments || Array.prototype.slice.call( arguments, 2 );
if(typeof methodName == 'string' && instance !== undefined) {
methodName = methodName.split('.');
$.each(methodName, function(index, name) {
if( $.isPlainObject( instance[name] ) ) {
instance = instance[name];
return true;
}
else if( $.isFunction( instance[name] ) ) {
method = instance[name];
return true;
}
module.error(settings.errors.method);
return false;
});
}
if ( $.isFunction( method ) ) {
return method.apply(context, methodArguments);
}
// return retrieved variable or chain
return method;
}
};
if(instance !== undefined && moduleArguments) {
if(moduleArguments[0] == 'invoke') {
moduleArguments = Array.prototype.slice.call( moduleArguments, 1 );
}
return module.invoke(moduleArguments[0], this, Array.prototype.slice.call( moduleArguments, 1 ) );
}
module.initialize();
})
;
return this;
};
$.fn.nag.settings = {
// set to zero to manually dismiss, otherwise hides on its own
displayTime : 0,
// if there is a link to follow
followLink : true,
// method of stickyness
position : 'fixed',
scrollBarWidth : 18,
// type of storage to use
storageMethod : 'cookie',
// value to store in dismissed localstorage/cookie
storedKey : 'nag',
storedValue : 'dismiss',
// need to calculate stickyness on scroll
sticky : true,
// how often to check scroll event
lag : 0,
// context for scroll event
context : window,
errors: {
noStorage : 'Neither $.cookie or store is defined. A storage solution is required for storing state',
followLink : 'Follow link is set but the plugin is not included'
},
className : {
bottom : 'bottom',
hover : 'hover',
fixed : 'fixed'
},
selector : {
close: '.icon.close'
},
speed : 500,
easing : 'easeOutQuad'
};
})( jQuery, window , document );