sp-magic.de/bower_components/foundation-sites/js/foundation.equalizer.js
2016-10-28 19:56:41 +02:00

307 lines
8.4 KiB
JavaScript

'use strict';
!function($) {
/**
* Equalizer module.
* @module foundation.equalizer
*/
class Equalizer {
/**
* Creates a new instance of Equalizer.
* @class
* @fires Equalizer#init
* @param {Object} element - jQuery object to add the trigger to.
* @param {Object} options - Overrides to the default plugin settings.
*/
constructor(element, options){
this.$element = element;
this.options = $.extend({}, Equalizer.defaults, this.$element.data(), options);
this._init();
Foundation.registerPlugin(this, 'Equalizer');
}
/**
* Initializes the Equalizer plugin and calls functions to get equalizer functioning on load.
* @private
*/
_init() {
var eqId = this.$element.attr('data-equalizer') || '';
var $watched = this.$element.find(`[data-equalizer-watch="${eqId}"]`);
this.$watched = $watched.length ? $watched : this.$element.find('[data-equalizer-watch]');
this.$element.attr('data-resize', (eqId || Foundation.GetYoDigits(6, 'eq')));
this.hasNested = this.$element.find('[data-equalizer]').length > 0;
this.isNested = this.$element.parentsUntil(document.body, '[data-equalizer]').length > 0;
this.isOn = false;
this._bindHandler = {
onResizeMeBound: this._onResizeMe.bind(this),
onPostEqualizedBound: this._onPostEqualized.bind(this)
};
var imgs = this.$element.find('img');
var tooSmall;
if(this.options.equalizeOn){
tooSmall = this._checkMQ();
$(window).on('changed.zf.mediaquery', this._checkMQ.bind(this));
}else{
this._events();
}
if((tooSmall !== undefined && tooSmall === false) || tooSmall === undefined){
if(imgs.length){
Foundation.onImagesLoaded(imgs, this._reflow.bind(this));
}else{
this._reflow();
}
}
}
/**
* Removes event listeners if the breakpoint is too small.
* @private
*/
_pauseEvents() {
this.isOn = false;
this.$element.off({
'.zf.equalizer': this._bindHandler.onPostEqualizedBound,
'resizeme.zf.trigger': this._bindHandler.onResizeMeBound
});
}
/**
* function to handle $elements resizeme.zf.trigger, with bound this on _bindHandler.onResizeMeBound
* @private
*/
_onResizeMe(e) {
this._reflow();
}
/**
* function to handle $elements postequalized.zf.equalizer, with bound this on _bindHandler.onPostEqualizedBound
* @private
*/
_onPostEqualized(e) {
if(e.target !== this.$element[0]){ this._reflow(); }
}
/**
* Initializes events for Equalizer.
* @private
*/
_events() {
var _this = this;
this._pauseEvents();
if(this.hasNested){
this.$element.on('postequalized.zf.equalizer', this._bindHandler.onPostEqualizedBound);
}else{
this.$element.on('resizeme.zf.trigger', this._bindHandler.onResizeMeBound);
}
this.isOn = true;
}
/**
* Checks the current breakpoint to the minimum required size.
* @private
*/
_checkMQ() {
var tooSmall = !Foundation.MediaQuery.atLeast(this.options.equalizeOn);
if(tooSmall){
if(this.isOn){
this._pauseEvents();
this.$watched.css('height', 'auto');
}
}else{
if(!this.isOn){
this._events();
}
}
return tooSmall;
}
/**
* A noop version for the plugin
* @private
*/
_killswitch() {
return;
}
/**
* Calls necessary functions to update Equalizer upon DOM change
* @private
*/
_reflow() {
if(!this.options.equalizeOnStack){
if(this._isStacked()){
this.$watched.css('height', 'auto');
return false;
}
}
if (this.options.equalizeByRow) {
this.getHeightsByRow(this.applyHeightByRow.bind(this));
}else{
this.getHeights(this.applyHeight.bind(this));
}
}
/**
* Manually determines if the first 2 elements are *NOT* stacked.
* @private
*/
_isStacked() {
return this.$watched[0].getBoundingClientRect().top !== this.$watched[1].getBoundingClientRect().top;
}
/**
* Finds the outer heights of children contained within an Equalizer parent and returns them in an array
* @param {Function} cb - A non-optional callback to return the heights array to.
* @returns {Array} heights - An array of heights of children within Equalizer container
*/
getHeights(cb) {
var heights = [];
for(var i = 0, len = this.$watched.length; i < len; i++){
this.$watched[i].style.height = 'auto';
heights.push(this.$watched[i].offsetHeight);
}
cb(heights);
}
/**
* Finds the outer heights of children contained within an Equalizer parent and returns them in an array
* @param {Function} cb - A non-optional callback to return the heights array to.
* @returns {Array} groups - An array of heights of children within Equalizer container grouped by row with element,height and max as last child
*/
getHeightsByRow(cb) {
var lastElTopOffset = (this.$watched.length ? this.$watched.first().offset().top : 0),
groups = [],
group = 0;
//group by Row
groups[group] = [];
for(var i = 0, len = this.$watched.length; i < len; i++){
this.$watched[i].style.height = 'auto';
//maybe could use this.$watched[i].offsetTop
var elOffsetTop = $(this.$watched[i]).offset().top;
if (elOffsetTop!=lastElTopOffset) {
group++;
groups[group] = [];
lastElTopOffset=elOffsetTop;
}
groups[group].push([this.$watched[i],this.$watched[i].offsetHeight]);
}
for (var j = 0, ln = groups.length; j < ln; j++) {
var heights = $(groups[j]).map(function(){ return this[1]; }).get();
var max = Math.max.apply(null, heights);
groups[j].push(max);
}
cb(groups);
}
/**
* Changes the CSS height property of each child in an Equalizer parent to match the tallest
* @param {array} heights - An array of heights of children within Equalizer container
* @fires Equalizer#preequalized
* @fires Equalizer#postequalized
*/
applyHeight(heights) {
var max = Math.max.apply(null, heights);
/**
* Fires before the heights are applied
* @event Equalizer#preequalized
*/
this.$element.trigger('preequalized.zf.equalizer');
this.$watched.css('height', max);
/**
* Fires when the heights have been applied
* @event Equalizer#postequalized
*/
this.$element.trigger('postequalized.zf.equalizer');
}
/**
* Changes the CSS height property of each child in an Equalizer parent to match the tallest by row
* @param {array} groups - An array of heights of children within Equalizer container grouped by row with element,height and max as last child
* @fires Equalizer#preequalized
* @fires Equalizer#preequalizedRow
* @fires Equalizer#postequalizedRow
* @fires Equalizer#postequalized
*/
applyHeightByRow(groups) {
/**
* Fires before the heights are applied
*/
this.$element.trigger('preequalized.zf.equalizer');
for (var i = 0, len = groups.length; i < len ; i++) {
var groupsILength = groups[i].length,
max = groups[i][groupsILength - 1];
if (groupsILength<=2) {
$(groups[i][0][0]).css({'height':'auto'});
continue;
}
/**
* Fires before the heights per row are applied
* @event Equalizer#preequalizedRow
*/
this.$element.trigger('preequalizedrow.zf.equalizer');
for (var j = 0, lenJ = (groupsILength-1); j < lenJ ; j++) {
$(groups[i][j][0]).css({'height':max});
}
/**
* Fires when the heights per row have been applied
* @event Equalizer#postequalizedRow
*/
this.$element.trigger('postequalizedrow.zf.equalizer');
}
/**
* Fires when the heights have been applied
*/
this.$element.trigger('postequalized.zf.equalizer');
}
/**
* Destroys an instance of Equalizer.
* @function
*/
destroy() {
this._pauseEvents();
this.$watched.css('height', 'auto');
Foundation.unregisterPlugin(this);
}
}
/**
* Default settings for plugin
*/
Equalizer.defaults = {
/**
* Enable height equalization when stacked on smaller screens.
* @option
* @example true
*/
equalizeOnStack: true,
/**
* Enable height equalization row by row.
* @option
* @example false
*/
equalizeByRow: false,
/**
* String representing the minimum breakpoint size the plugin should equalize heights on.
* @option
* @example 'medium'
*/
equalizeOn: ''
};
// Window exports
Foundation.plugin(Equalizer, 'Equalizer');
}(jQuery);