mirror of
https://github.com/kidoman/embd
synced 2025-02-11 13:13:18 +01:00
241 lines
6.2 KiB
JavaScript
241 lines
6.2 KiB
JavaScript
![]() |
/**
|
||
|
* Gumby Fixed
|
||
|
*/
|
||
|
!function($) {
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
function Fixed($el) {
|
||
|
|
||
|
Gumby.debug('Initializing Fixed Position', $el);
|
||
|
|
||
|
this.$el = $el;
|
||
|
|
||
|
this.$window = $(window);
|
||
|
this.fixedPoint = '';
|
||
|
this.pinPoint = false;
|
||
|
this.fixedPointjQ = false;
|
||
|
this.pinPointjQ = false;
|
||
|
this.offset = 0;
|
||
|
this.pinOffset = 0;
|
||
|
this.top = 0;
|
||
|
this.constrainEl = true;
|
||
|
this.state = false;
|
||
|
this.measurements = {
|
||
|
left: 0,
|
||
|
width: 0
|
||
|
};
|
||
|
|
||
|
// set up module based on attributes
|
||
|
this.setup();
|
||
|
|
||
|
var scope = this;
|
||
|
|
||
|
// monitor scroll and update fixed elements accordingly
|
||
|
this.$window.on('scroll load', function() {
|
||
|
scope.monitorScroll();
|
||
|
});
|
||
|
|
||
|
// reinitialize event listener
|
||
|
this.$el.on('gumby.initialize', function() {
|
||
|
Gumby.debug('Re-initializing Fixed Position', $el);
|
||
|
scope.setup();
|
||
|
scope.monitorScroll();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// set up module based on attributes
|
||
|
Fixed.prototype.setup = function() {
|
||
|
var scope = this;
|
||
|
|
||
|
this.fixedPoint = this.parseAttrValue(Gumby.selectAttr.apply(this.$el, ['fixed']));
|
||
|
|
||
|
// pin point is optional
|
||
|
this.pinPoint = Gumby.selectAttr.apply(this.$el, ['pin']) || false;
|
||
|
|
||
|
// offset from fixed point
|
||
|
this.offset = Number(Gumby.selectAttr.apply(this.$el, ['offset'])) || 0;
|
||
|
|
||
|
// offset from pin point
|
||
|
this.pinOffset = Number(Gumby.selectAttr.apply(this.$el, ['pinoffset'])) || 0;
|
||
|
|
||
|
// top position when fixed
|
||
|
this.top = Number(Gumby.selectAttr.apply(this.$el, ['top'])) || 0;
|
||
|
|
||
|
// constrain can be turned off
|
||
|
this.constrainEl = Gumby.selectAttr.apply(this.$el, ['constrain']) || true;
|
||
|
if(this.constrainEl === 'false') {
|
||
|
this.constrainEl = false;
|
||
|
}
|
||
|
|
||
|
// reference to the parent, row/column
|
||
|
this.$parent = this.$el.parents('.columns, .column, .row');
|
||
|
this.$parent = this.$parent.length ? this.$parent.first() : false;
|
||
|
this.parentRow = this.$parent ? !!this.$parent.hasClass('row') : false;
|
||
|
|
||
|
// if optional pin point set then parse now
|
||
|
if(this.pinPoint) {
|
||
|
this.pinPoint = this.parseAttrValue(this.pinPoint);
|
||
|
}
|
||
|
|
||
|
this.fixedPointjQ = this.fixedPoint instanceof jQuery;
|
||
|
this.pinPointjQ = this.pinPoint instanceof jQuery;
|
||
|
|
||
|
// if we have a parent constrain dimenions
|
||
|
if(this.$parent && this.constrainEl) {
|
||
|
// measure up
|
||
|
this.measure();
|
||
|
// and on resize reset measurement
|
||
|
this.$window.resize(function() {
|
||
|
if(scope.state) {
|
||
|
scope.measure();
|
||
|
scope.constrain();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// monitor scroll and trigger changes based on position
|
||
|
Fixed.prototype.monitorScroll = function() {
|
||
|
var scrollAmount = this.$window.scrollTop(),
|
||
|
// recalculate selector attributes as position may have changed
|
||
|
fixedPoint = this.fixedPointjQ ? this.fixedPoint.offset().top : this.fixedPoint,
|
||
|
pinPoint = false,
|
||
|
timer;
|
||
|
|
||
|
// if a pin point is set recalculate
|
||
|
if(this.pinPoint) {
|
||
|
pinPoint = this.pinPointjQ ? this.pinPoint.offset().top : this.pinPoint;
|
||
|
}
|
||
|
|
||
|
// apply offsets
|
||
|
if(this.offset) { fixedPoint -= this.offset; }
|
||
|
if(this.pinOffset) { pinPoint -= this.pinOffset; }
|
||
|
|
||
|
// fix it
|
||
|
if((scrollAmount >= fixedPoint) && this.state !== 'fixed') {
|
||
|
if(!pinPoint || scrollAmount < pinPoint) {
|
||
|
this.fix();
|
||
|
}
|
||
|
// unfix it
|
||
|
} else if(scrollAmount < fixedPoint && this.state === 'fixed') {
|
||
|
this.unfix();
|
||
|
|
||
|
// pin it
|
||
|
} else if(pinPoint && scrollAmount >= pinPoint && this.state !== 'pinned') {
|
||
|
this.pin();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// fix the element and update state
|
||
|
Fixed.prototype.fix = function() {
|
||
|
Gumby.debug('Element has been fixed', this.$el);
|
||
|
Gumby.debug('Triggering onFixed event', this.$el);
|
||
|
|
||
|
this.state = 'fixed';
|
||
|
this.$el.css({
|
||
|
'top' : this.top
|
||
|
}).addClass('fixed').removeClass('unfixed pinned').trigger('gumby.onFixed');
|
||
|
|
||
|
// if we have a parent constrain dimenions
|
||
|
if(this.$parent) {
|
||
|
this.constrain();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// unfix the element and update state
|
||
|
Fixed.prototype.unfix = function() {
|
||
|
Gumby.debug('Element has been unfixed', this.$el);
|
||
|
Gumby.debug('Triggering onUnfixed event', this.$el);
|
||
|
|
||
|
this.state = 'unfixed';
|
||
|
this.$el.addClass('unfixed').removeClass('fixed pinned').trigger('gumby.onUnfixed');
|
||
|
};
|
||
|
|
||
|
// pin the element in position
|
||
|
Fixed.prototype.pin = function() {
|
||
|
Gumby.debug('Element has been pinned', this.$el);
|
||
|
Gumby.debug('Triggering onPinned event', this.$el);
|
||
|
this.state = 'pinned';
|
||
|
this.$el.css({
|
||
|
'top' : this.$el.offset().top
|
||
|
}).addClass('pinned fixed').removeClass('unfixed').trigger('gumby.onPinned');
|
||
|
};
|
||
|
|
||
|
// constrain elements dimensions to match width/height
|
||
|
Fixed.prototype.constrain = function() {
|
||
|
Gumby.debug("Constraining element", this.$el);
|
||
|
this.$el.css({
|
||
|
left: this.measurements.left,
|
||
|
width: this.measurements.width
|
||
|
});
|
||
|
};
|
||
|
|
||
|
// measure up the parent for constraining
|
||
|
Fixed.prototype.measure = function() {
|
||
|
var parentPadding;
|
||
|
|
||
|
this.measurements.left = this.$parent.offset().left;
|
||
|
this.measurements.width = this.$parent.width();
|
||
|
|
||
|
// if element has a parent row then need to consider padding
|
||
|
if(this.parentRow) {
|
||
|
parentPadding = Number(this.$parent.css('paddingLeft').replace(/px/, ''));
|
||
|
if(parentPadding) {
|
||
|
this.measurements.left += parentPadding;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// parse attribute values, could be px, top, selector
|
||
|
Fixed.prototype.parseAttrValue = function(attr) {
|
||
|
// px value fixed point
|
||
|
if($.isNumeric(attr)) {
|
||
|
return Number(attr);
|
||
|
// 'top' string fixed point
|
||
|
} else if(attr === 'top') {
|
||
|
return this.$el.offset().top;
|
||
|
// selector specified
|
||
|
} else {
|
||
|
var $el = $(attr);
|
||
|
if(!$el.length) {
|
||
|
Gumby.error('Cannot find Fixed target: '+attr);
|
||
|
return false;
|
||
|
}
|
||
|
return $el;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// add initialisation
|
||
|
Gumby.addInitalisation('fixed', function(all) {
|
||
|
$('[data-fixed],[gumby-fixed],[fixed]').each(function() {
|
||
|
var $this = $(this);
|
||
|
|
||
|
// this element has already been initialized
|
||
|
// and we're only initializing new modules
|
||
|
if($this.data('isFixed') && !all) {
|
||
|
return true;
|
||
|
|
||
|
// this element has already been initialized
|
||
|
// and we need to reinitialize it
|
||
|
} else if($this.data('isFixed') && all) {
|
||
|
$this.trigger('gumby.initialize');
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// mark element as initialized
|
||
|
$this.data('isFixed', true);
|
||
|
new Fixed($this);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// register UI module
|
||
|
Gumby.UIModule({
|
||
|
module: 'fixed',
|
||
|
events: ['initialize', 'onFixed', 'onUnfixed'],
|
||
|
init: function() {
|
||
|
Gumby.initialize('fixed');
|
||
|
}
|
||
|
});
|
||
|
}(jQuery);
|