Skip to content

Instantly share code, notes, and snippets.

@mathetos
Created November 30, 2015 06:35
Show Gist options
  • Save mathetos/f70513bfe71fef9c2306 to your computer and use it in GitHub Desktop.
Save mathetos/f70513bfe71fef9c2306 to your computer and use it in GitHub Desktop.
Forked Codrops Grid.js
/*
* debouncedresize: special jQuery event that happens once after a window resize
*
* latest version and complete README available on Github:
* https://github.com/louisremi/jquery-smartresize
*
* Copyright 2012 @louis_remi
* Licensed under the MIT license.
*
* This saved you an hour of work?
* Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON
*/
(function($) {
var $event = $.event,
$special,
resizeTimeout;
$special = $event.special.debouncedresize = {
setup: function() {
$( this ).on( "resize", $special.handler );
},
teardown: function() {
$( this ).off( "resize", $special.handler );
},
handler: function( event, execAsap ) {
// Save the context
var context = this,
args = arguments,
dispatch = function() {
// set correct event type
event.type = "debouncedresize";
$event.dispatch.apply( context, args );
};
if ( resizeTimeout ) {
clearTimeout( resizeTimeout );
}
execAsap ?
dispatch() :
resizeTimeout = setTimeout( dispatch, $special.threshold );
},
threshold: 150
};
})(jQuery);
/*!
* imagesLoaded PACKAGED v3.2.0
* JavaScript is all like "You images are done yet or what?"
* MIT License
*/
(function(){"use strict";function e(){}function t(e,t){for(var n=e.length;n--;)if(e[n].listener===t)return n;return-1}function n(e){return function(){return this[e].apply(this,arguments)}}var i=e.prototype,r=this,s=r.EventEmitter;i.getListeners=function(e){var t,n,i=this._getEvents();if("object"==typeof e){t={};for(n in i)i.hasOwnProperty(n)&&e.test(n)&&(t[n]=i[n])}else t=i[e]||(i[e]=[]);return t},i.flattenListeners=function(e){var t,n=[];for(t=0;t<e.length;t+=1)n.push(e[t].listener);return n},i.getListenersAsObject=function(e){var t,n=this.getListeners(e);return n instanceof Array&&(t={},t[e]=n),t||n},i.addListener=function(e,n){var i,r=this.getListenersAsObject(e),s="object"==typeof n;for(i in r)r.hasOwnProperty(i)&&-1===t(r[i],n)&&r[i].push(s?n:{listener:n,once:!1});return this},i.on=n("addListener"),i.addOnceListener=function(e,t){return this.addListener(e,{listener:t,once:!0})},i.once=n("addOnceListener"),i.defineEvent=function(e){return this.getListeners(e),this},i.defineEvents=function(e){for(var t=0;t<e.length;t+=1)this.defineEvent(e[t]);return this},i.removeListener=function(e,n){var i,r,s=this.getListenersAsObject(e);for(r in s)s.hasOwnProperty(r)&&(i=t(s[r],n),-1!==i&&s[r].splice(i,1));return this},i.off=n("removeListener"),i.addListeners=function(e,t){return this.manipulateListeners(!1,e,t)},i.removeListeners=function(e,t){return this.manipulateListeners(!0,e,t)},i.manipulateListeners=function(e,t,n){var i,r,s=e?this.removeListener:this.addListener,o=e?this.removeListeners:this.addListeners;if("object"!=typeof t||t instanceof RegExp)for(i=n.length;i--;)s.call(this,t,n[i]);else for(i in t)t.hasOwnProperty(i)&&(r=t[i])&&("function"==typeof r?s.call(this,i,r):o.call(this,i,r));return this},i.removeEvent=function(e){var t,n=typeof e,i=this._getEvents();if("string"===n)delete i[e];else if("object"===n)for(t in i)i.hasOwnProperty(t)&&e.test(t)&&delete i[t];else delete this._events;return this},i.removeAllListeners=n("removeEvent"),i.emitEvent=function(e,t){var n,i,r,s,o=this.getListenersAsObject(e);for(r in o)if(o.hasOwnProperty(r))for(i=o[r].length;i--;)n=o[r][i],n.once===!0&&this.removeListener(e,n.listener),s=n.listener.apply(this,t||[]),s===this._getOnceReturnValue()&&this.removeListener(e,n.listener);return this},i.trigger=n("emitEvent"),i.emit=function(e){var t=Array.prototype.slice.call(arguments,1);return this.emitEvent(e,t)},i.setOnceReturnValue=function(e){return this._onceReturnValue=e,this},i._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},i._getEvents=function(){return this._events||(this._events={})},e.noConflict=function(){return r.EventEmitter=s,e},"function"==typeof define&&define.amd?define("eventEmitter/EventEmitter",[],function(){return e}):"object"==typeof module&&module.exports?module.exports=e:this.EventEmitter=e}).call(this),function(e){function t(t){var n=e.event;return n.target=n.target||n.srcElement||t,n}var n=document.documentElement,i=function(){};n.addEventListener?i=function(e,t,n){e.addEventListener(t,n,!1)}:n.attachEvent&&(i=function(e,n,i){e[n+i]=i.handleEvent?function(){var n=t(e);i.handleEvent.call(i,n)}:function(){var n=t(e);i.call(e,n)},e.attachEvent("on"+n,e[n+i])});var r=function(){};n.removeEventListener?r=function(e,t,n){e.removeEventListener(t,n,!1)}:n.detachEvent&&(r=function(e,t,n){e.detachEvent("on"+t,e[t+n]);try{delete e[t+n]}catch(i){e[t+n]=void 0}});var s={bind:i,unbind:r};"function"==typeof define&&define.amd?define("eventie/eventie",s):e.eventie=s}(this),function(e,t){"use strict";"function"==typeof define&&define.amd?define(["eventEmitter/EventEmitter","eventie/eventie"],function(n,i){return t(e,n,i)}):"object"==typeof module&&module.exports?module.exports=t(e,require("wolfy87-eventemitter"),require("eventie")):e.imagesLoaded=t(e,e.EventEmitter,e.eventie)}(window,function(e,t,n){function i(e,t){for(var n in t)e[n]=t[n];return e}function r(e){return"[object Array]"==f.call(e)}function s(e){var t=[];if(r(e))t=e;else if("number"==typeof e.length)for(var n=0;n<e.length;n++)t.push(e[n]);else t.push(e);return t}function o(e,t,n){if(!(this instanceof o))return new o(e,t,n);"string"==typeof e&&(e=document.querySelectorAll(e)),this.elements=s(e),this.options=i({},this.options),"function"==typeof t?n=t:i(this.options,t),n&&this.on("always",n),this.getImages(),u&&(this.jqDeferred=new u.Deferred);var r=this;setTimeout(function(){r.check()})}function h(e){this.img=e}function a(e,t){this.url=e,this.element=t,this.img=new Image}var u=e.jQuery,c=e.console,f=Object.prototype.toString;o.prototype=new t,o.prototype.options={},o.prototype.getImages=function(){this.images=[];for(var e=0;e<this.elements.length;e++){var t=this.elements[e];this.addElementImages(t)}},o.prototype.addElementImages=function(e){"IMG"==e.nodeName&&this.addImage(e),this.options.background===!0&&this.addElementBackgroundImages(e);var t=e.nodeType;if(t&&d[t]){for(var n=e.querySelectorAll("img"),i=0;i<n.length;i++){var r=n[i];this.addImage(r)}if("string"==typeof this.options.background){var s=e.querySelectorAll(this.options.background);for(i=0;i<s.length;i++){var o=s[i];this.addElementBackgroundImages(o)}}}};var d={1:!0,9:!0,11:!0};o.prototype.addElementBackgroundImages=function(e){for(var t=m(e),n=/url\(['"]*([^'"\)]+)['"]*\)/gi,i=n.exec(t.backgroundImage);null!==i;){var r=i&&i[1];r&&this.addBackground(r,e),i=n.exec(t.backgroundImage)}};var m=e.getComputedStyle||function(e){return e.currentStyle};return o.prototype.addImage=function(e){var t=new h(e);this.images.push(t)},o.prototype.addBackground=function(e,t){var n=new a(e,t);this.images.push(n)},o.prototype.check=function(){function e(e,n,i){setTimeout(function(){t.progress(e,n,i)})}var t=this;if(this.progressedCount=0,this.hasAnyBroken=!1,!this.images.length)return void this.complete();for(var n=0;n<this.images.length;n++){var i=this.images[n];i.once("progress",e),i.check()}},o.prototype.progress=function(e,t,n){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded,this.emit("progress",this,e,t),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,e),this.progressedCount==this.images.length&&this.complete(),this.options.debug&&c&&c.log("progress: "+n,e,t)},o.prototype.complete=function(){var e=this.hasAnyBroken?"fail":"done";if(this.isComplete=!0,this.emit(e,this),this.emit("always",this),this.jqDeferred){var t=this.hasAnyBroken?"reject":"resolve";this.jqDeferred[t](this)}},h.prototype=new t,h.prototype.check=function(){var e=this.getIsImageComplete();return e?void this.confirm(0!==this.img.naturalWidth,"naturalWidth"):(this.proxyImage=new Image,n.bind(this.proxyImage,"load",this),n.bind(this.proxyImage,"error",this),n.bind(this.img,"load",this),n.bind(this.img,"error",this),void(this.proxyImage.src=this.img.src))},h.prototype.getIsImageComplete=function(){return this.img.complete&&void 0!==this.img.naturalWidth},h.prototype.confirm=function(e,t){this.isLoaded=e,this.emit("progress",this,this.img,t)},h.prototype.handleEvent=function(e){var t="on"+e.type;this[t]&&this[t](e)},h.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindEvents()},h.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindEvents()},h.prototype.unbindEvents=function(){n.unbind(this.proxyImage,"load",this),n.unbind(this.proxyImage,"error",this),n.unbind(this.img,"load",this),n.unbind(this.img,"error",this)},a.prototype=new h,a.prototype.check=function(){n.bind(this.img,"load",this),n.bind(this.img,"error",this),this.img.src=this.url;var e=this.getIsImageComplete();e&&(this.confirm(0!==this.img.naturalWidth,"naturalWidth"),this.unbindEvents())},a.prototype.unbindEvents=function(){n.unbind(this.img,"load",this),n.unbind(this.img,"error",this)},a.prototype.confirm=function(e,t){this.isLoaded=e,this.emit("progress",this,this.element,t)},o.makeJQueryPlugin=function(t){t=t||e.jQuery,t&&(u=t,u.fn.imagesLoaded=function(e,t){var n=new o(this,e,t);return n.jqDeferred.promise(u(this))})},o.makeJQueryPlugin(),o});
var Grid = (function($) {
// grid selector
var selector = '#og-grid',
// list of items
grid = jQuery( selector ),
// the items
items = grid.children( 'li' ),
// current expanded item's index
current = -1,
// position (top) of the expanded item
// used to know if the preview will expand in a different row
previewPos = -1,
// extra amount of pixels to scroll the window
scrollExtra = 0,
// extra margin when expanded (between preview overlay and the next items)
marginExpanded = 10,
window = jQuery( window ), winsize,
body = jQuery( 'html, body' ),
// transitionend events
transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd',
'MozTransition' : 'transitionend',
'OTransition' : 'oTransitionEnd',
'msTransition' : 'MSTransitionEnd',
'transition' : 'transitionend'
},
transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ],
// support for csstransitions
support = Modernizr.csstransitions,
// default settings
settings = {
minHeight : 500,
speed : 350,
easing : 'ease',
showVisitButton : true
};
function init( config ) {
// the settings..
settings = jQuery.extend( true, {}, settings, config );
// preload all images
grid.imagesLoaded( function() {
// save item´s size and offset
saveItemInfo( true );
// get window´s size
getWinSize();
// initialize some events
initEvents();
} );
}
// add more items to the grid.
// the new items need to appended to the grid.
// after that call Grid.addItems(theItems);
function addItems( newitems ) {
items = items.add( newitems );
newitems.each( function() {
var item = jQuery( this );
item.data( {
offsetTop : item.offset().top,
height : item.height()
} );
} );
initItemsEvents( newitems );
}
// saves the item´s offset top and height (if saveheight is true)
function saveItemInfo( saveheight ) {
items.each( function() {
var item = jQuery( this );
item.data( 'offsetTop', item.offset().top );
if( saveheight ) {
item.data( 'height', item.height() );
}
} );
}
function initEvents() {
// when clicking an item, show the preview with the item´s info and large image.
// close the item if already expanded.
// also close if clicking on the item´s cross
initItemsEvents( items );
// on window resize get the window´s size again
// reset some values..
window.on( 'debouncedresize', function() {
scrollExtra = 0;
previewPos = -1;
// save item´s offset
saveItemInfo();
getWinSize();
var preview = jQuery.data( this, 'preview' );
if( typeof preview != 'undefined' ) {
hidePreview();
}
} );
}
function initItemsEvents( items ) {
items.on( 'click', 'span.og-close', function() {
hidePreview();
return false;
} ).children( 'a' ).on( 'click', function(e) {
var item = jQuery( this ).parent();
// check if item already opened
current === item.index() ? hidePreview() : showPreview( item );
return false;
} );
}
function getWinSize() {
winsize = { width : window.width(), height : window.height() };
}
function showPreview( item ) {
var preview = jQuery.data( this, 'preview' ),
// item´s offset top
position = item.data( 'offsetTop' );
scrollExtra = 0;
// if a preview exists and previewPos is different (different row) from item´s top then close it
if( typeof preview != 'undefined' ) {
// not in the same row
if( previewPos !== position ) {
// if position > previewPos then we need to take te current preview´s height in consideration when scrolling the window
if( position > previewPos ) {
scrollExtra = preview.height;
}
hidePreview();
}
// same row
else {
preview.update( item );
return false;
}
}
// update previewPos
previewPos = position;
// initialize new preview for the clicked item
preview = jQuery.data( this, 'preview', new Preview( item ) );
// expand preview overlay
preview.open();
}
function hidePreview() {
current = -1;
var preview = jQuery.data( this, 'preview' );
preview.close();
jQuery.removeData( this, 'preview' );
}
// the preview obj / overlay
function Preview( item ) {
this.item = item;
this.expandedIdx = this.item.index();
this.create();
this.update();
}
Preview.prototype = {
create : function() {
// create Preview structure:
this.title = jQuery( '<h3></h3>' );
this.description = jQuery( '<p></p>' );
var detailAppends = [this.title, this.description];
if (settings.showVisitButton === true) {
this.href = jQuery( '<a href="#">Visit website</a>' );
detailAppends.push(this.href);
}
this.details = jQuery( '<div class="og-details"></div>' ).append(detailAppends);
this.loading = jQuery( '<div class="og-loading"></div>' );
this.fullimage = jQuery( '<div class="og-fullimg"></div>' ).append( this.loading );
this.closePreview = jQuery( '<span class="og-close"></span>' );
this.previewInner = jQuery( '<div class="og-expander-inner"></div>' ).append( this.closePreview, this.fullimage, this.details );
this.previewEl = jQuery( '<div class="og-expander"></div>' ).append( this.previewInner );
// append preview element to the item
this.item.append( this.getEl() );
// set the transitions for the preview and the item
if( support ) {
this.setTransition();
}
},
update : function( item ) {
if( item ) {
this.item = item;
}
// if already expanded remove class "og-expanded" from current item and add it to new item
if( current !== -1 ) {
var currentItem = items.eq( current );
currentItem.removeClass( 'og-expanded' );
this.item.addClass( 'og-expanded' );
// position the preview correctly
this.positionPreview();
}
// update current value
current = this.item.index();
// update preview´s content
var itemEl = this.item.children( 'a' ),
eldata = {
href : itemEl.attr( 'href' ),
largesrc : itemEl.data( 'largesrc' ),
title : itemEl.data( 'title' ),
description : itemEl.data( 'description' )
};
this.title.html( eldata.title );
this.description.html( eldata.description );
if (settings.showVisitButton === true) {
this.href.attr( 'href', eldata.href );
}
var self = this;
// remove the current image in the preview
if( typeof self.largeImg != 'undefined' ) {
self.largeImg.remove();
}
// preload large image and add it to the preview
// for smaller screens we don´t display the large image (the media query will hide the fullimage wrapper)
if( self.fullimage.is( ':visible' ) ) {
this.loading.show();
jQuery( '<img/>' ).load( function() {
var img = jQuery( this );
if( img.attr( 'src' ) === self.item.children('a').data( 'largesrc' ) ) {
self.loading.hide();
self.fullimage.find( 'img' ).remove();
self.largeImg = img.fadeIn( 350 );
self.fullimage.append( self.largeImg );
}
} ).attr( 'src', eldata.largesrc );
}
},
open : function() {
setTimeout( jQuery.proxy( function() {
// set the height for the preview and the item
this.setHeights();
// scroll to position the preview in the right place
this.positionPreview();
}, this ), 25 );
},
close : function() {
var self = this,
onEndFn = function() {
if( support ) {
jQuery( this ).off( transEndEventName );
}
self.item.removeClass( 'og-expanded' );
self.previewEl.remove();
};
setTimeout( jQuery.proxy( function() {
if( typeof this.largeImg !== 'undefined' ) {
this.largeImg.fadeOut( 'fast' );
}
this.previewEl.css( 'height', 0 );
// the current expanded item (might be different from this.$item)
var expandedItem = items.eq( this.expandedIdx );
expandedItem.css( 'height', expandedItem.data( 'height' ) ).on( transEndEventName, onEndFn );
if( !support ) {
onEndFn.call();
}
}, this ), 25 );
return false;
},
calcHeight : function() {
var heightPreview = winsize.height - this.item.data( 'height' ) - marginExpanded,
itemHeight = winsize.height;
if( heightPreview < settings.minHeight ) {
heightPreview = settings.minHeight;
itemHeight = settings.minHeight + this.item.data( 'height' ) + marginExpanded;
}
this.height = heightPreview;
this.itemHeight = itemHeight;
},
setHeights : function() {
var self = this,
onEndFn = function() {
if( support ) {
self.item.off( transEndEventName );
}
self.item.addClass( 'og-expanded' );
};
this.calcHeight();
this.previewEl.css( 'height', this.height );
this.item.css( 'height', this.itemHeight ).on( transEndEventName, onEndFn );
if( !support ) {
onEndFn.call();
}
},
positionPreview : function() {
// scroll page
// case 1 : preview height + item height fits in window´s height
// case 2 : preview height + item height does not fit in window´s height and preview height is smaller than window´s height
// case 3 : preview height + item height does not fit in window´s height and preview height is bigger than window´s height
var position = this.item.data( 'offsetTop' ),
previewOffsetT = this.previewEl.offset().top - scrollExtra,
scrollVal = this.height + this.item.data( 'height' ) + marginExpanded <= winsize.height ? position : this.height < winsize.height ? previewOffsetT - ( winsize.height - this.height ) : previewOffsetT;
body.animate( { scrollTop : scrollVal }, settings.speed );
},
setTransition : function() {
this.previewEl.css( 'transition', 'height ' + settings.speed + 'ms ' + settings.easing );
this.item.css( 'transition', 'height ' + settings.speed + 'ms ' + settings.easing );
},
getEl : function() {
return this.previewEl;
}
}
return {
init : init,
addItems : addItems
};
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment