Created
November 30, 2015 06:35
-
-
Save mathetos/f70513bfe71fef9c2306 to your computer and use it in GitHub Desktop.
Forked Codrops Grid.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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