Remove jQuery dependency for page js.

Switch to <picture> for webp demo images instead of a js solution.
pull/111/head
Glen Cheney 8 years ago
parent d294a42913
commit 47556e0c15

@ -40,31 +40,24 @@ demos:
- url: 'demos/2013-05-01-basic'
slug: basic
label: Basic masonry layout
screenshot: basic.webp
- url: 'demos/2013-06-19-adding-removing'
slug: adding-removing
label: Adding and removing items
screenshot: removing.webp
- url: 'demos/2013-05-02-adaptive'
slug: adaptive
label: Adaptive bootstrap grid with compound filters
screenshot: adaptive.webp
- url: 'demos/2013-05-03-images'
slug: images
label: Using images
screenshot: images.webp
- url: 'demos/2013-08-25-animated'
slug: animated
label: Animated viewport entry
screenshot: animated.webp
- url: 'demos/2014-03-08-bootstrap3-grid'
slug: bootstrap3-grid
label: Bootstrap 3 Grid Demo
screenshot: bootstrap3grid.webp
- url: 'demos/2014-04-09-requirejs'
slug: requirejs
label: RequireJS with Shuffle
screenshot: requirejs.webp
shapes:
- shape: circle

@ -1,11 +1,12 @@
<h2>Changes<a href="#changelog"></a></h2>
<ul>
<li><code>v4.0.0</code> 4/2/16 - Rewrite in ES6 with babel. Remove jQuery and Modernizr dependencies. Remove support for IE&lt;11. Docs improvements. Switch to gulp build system with webpack.</li>
<li><code>v3.1.0</code> 3/23/15 - Allow zero speed option (<a href="https://github.com/Vestride/Shuffle/issues/64">#64</a>) and cancel previous animations instead of ignoring new ones (<a href="https://github.com/Vestride/Shuffle/issues/69">#69</a>). Handle non-integer columns better (<a href="https://github.com/Vestride/Shuffle/issues/46">#46</a>)</li>
<li><code>v3.0.4</code> 2/16/15 - Publish to NPM.</li>
<li><code>v3.0.2</code> 1/21/15 - Remove from jQuery plugins directory.</li>
<li><code>v3.0.1</code> 12/29/14 - Add CommonJS support.</li>
<li><code>v3.0.0</code> 10/06/14 - Refactored with improvements, added unit tests, more documentation. Removed some triggered events.</li>
<li><code>v2.1.2</code> 6/01/14 - Use <code>window.jQuery</code> instead of <code>window.$</code> to work better with noConflict. Fixed <a href="https://github.com/Vestride/Shuffle/issues/25">#25</a>.</li>
<li><code>v3.0.0</code> 10/6/14 - Refactored with improvements, added unit tests, more documentation. Removed some triggered events.</li>
<li><code>v2.1.2</code> 6/1/14 - Use <code>window.jQuery</code> instead of <code>window.$</code> to work better with noConflict. Fixed <a href="https://github.com/Vestride/Shuffle/issues/25">#25</a>.</li>
<li><code>v2.1.1</code> 4/16/14 - Fix items with zero opacity overlapping visible ones in IE&lt;10.</li>
<li><code>v2.1.0</code> 4/12/14 - Register with bower as <code>shufflejs</code>.</li>
<li>4/10/14 - Add AMD support.</li>

@ -4,7 +4,10 @@
<a href="{{ site.baseurl }}/{{ post.slug }}">
<figure>
<div class="keep-ratio four-three">
<img src="{{ site.baseurl }}/img/demos/{{ post.slug }}.webp" alt="{{ post.label }}" />
<picture>
<source srcset="{{ site.baseurl }}/img/demos/{{ post.slug }}.webp" type="image/webp">
<img src="{{ site.baseurl }}/img/demos/{{ post.slug }}.jpg" alt="{{ post.label }}">
</picture>
</div>
<figcaption>{{ post.label }}</figcaption>
</figure>

@ -1,7 +1,8 @@
@import "variables";
* {
-moz-box-sizing: border-box;
*,
*::before,
*::after {
box-sizing: border-box;
}
@ -9,7 +10,12 @@ pre {
max-height: 30em;
}
picture {
display: block;
}
img {
display: block;
max-width: 100%;
height: auto;
}

@ -11,8 +11,8 @@ prism: true
<h2>Install<a href="#install"></a></h2>
<div class="row-fluid">
<div class="span12">
<pre><code class="language">bower install shufflejs</code></pre>
<p>Shuffle is also available on NPM as <code>shufflejs</code>.</p>
<pre><code class="language">npm install shufflejs --save</code></pre>
<p>Shuffle is also available on bower as <code>shufflejs</code>.</p>
</div>
</div>
<div class="row-fluid">
@ -145,16 +145,13 @@ prism: true
<h2>Supported Browsers<a href="#browsers"></a></h2>
<ul>
<li>Chrome</li>
<li>Firefox</li>
<li>IE 7+</li>
<li>Opera</li>
<li>Safari</li>
<li>Chrome</li>
<li>Firefox</li>
<li>IE 11+</li>
<li>Safari</li>
</ul>
<p>
Browsers that don't support CSS transitions and transforms <strong>*cough*</strong> IE &lt;= 9 <strong>*cough*</strong> will see a less cool, javascript based version of the effect.
</p>
<p>Many other browsers will likely work as well and support for others may be added with polyfills for ES5 features. If you require broader browser support, use the most recent <code>v3</code> of Shuffle.</p>
</div>
</section>

@ -1,62 +1,67 @@
/*!
* jQuery Even Heights plugin
/**
* Even Heights plugin
* Author: Glen Cheney
* Modified: 2014-03-08
* Dependencies: jQuery 1.2.6+
* Sets a jQuery collection to all be the same height
* If you need to set multiple collection, please use `$.evenHeights( collectionsArray )`
* because it is much faster
* Modified: 2016-03-08
* Sets a collection to all be the same height.
*
* Usage:
*
* evenHeights([
* document.querySelectorAll('.foo'),
* ]);
*
*/
(function( $ ) {
window.evenHeights = (function () {
'use strict';
function getTallest( $elements ) {
function getTallest(elements) {
var tallest = 0;
$elements.each(function() {
if ( this.offsetHeight > tallest ) {
tallest = this.offsetHeight;
for (var i = elements.length - 1; i >= 0; i--) {
if (elements[i].offsetHeight > tallest) {
tallest = elements[i].offsetHeight;
}
});
}
return tallest;
}
$.fn.evenHeights = function() {
return this.css( 'height', '' ).css( 'height', getTallest( this ) );
};
function setAllHeights(elements, height) {
for (var i = elements.length - 1; i >= 0; i--) {
elements[i].style.height = height;
}
}
/**
* For groups of elements which should be the same height. Using this method
* will create far less style recalculations and layouts.
* @param {Array.<jQuery>} groups Array of jQuery collections.
* @param {ArrayLike.<ArrayLike.<Element>>} groups An array-like collection of
* an array-like collection of elements.
* @return {Array.<number>} An array containing the pixel value of the
* tallest element for each group.
*/
$.evenHeights = function( groups ) {
var winners = [];
function evenHeights(groups) {
groups = Array.prototype.slice.call(groups);
// First, reset the height for every element.
// This is done first, otherwise we dirty the DOM on each loop!
$.each(groups, function( i, $elements ) {
$elements.css( 'height', '' );
groups.forEach(function (elements) {
setAllHeights(elements, '');
});
// Now, measure heights in each group and save the tallest value. Instead of
// setting the height value for the entire group, save it. If it were set,
// the next iteration in the loop would have to recalculate styles in the DOM
$.each(groups, function( i, $elements ) {
winners.push( getTallest( $elements ) );
var tallests = groups.map(function (elements) {
return getTallest(elements);
});
// Lastly, set them all.
$.each(groups, function( i, $elements ) {
$elements.css( 'height', winners[ i ] );
groups.forEach(function (elements, i) {
setAllHeights(elements, tallests[i] + 'px');
});
}
return winners;
};
})(window.$);
return evenHeights;
})();

@ -1,6 +1,4 @@
/**
* @author Glen Cheney
*/
'use strict';
/*
* jQuery throttle / debounce - v1.1 - 3/7/2010
@ -10,120 +8,33 @@
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);
// (function (b, c) {
// var $ = b.jQuery || b.Cowboy || (b.Cowboy = {}), a;$.throttle = a = function (e, f, j, i) {
// var h, d = 0;if (typeof f !== 'boolean') {
// i = j;j = f;f = c;
// }function g() {
// var o = this, m = +new Date() - d, n = arguments;function l() {
// d = +new Date();j.apply(o, n);
// }function k() {h = c;}if (i && !h) {l();}h && clearTimeout(h);if (i === c && m > e) {l();}else {if (f !== true) {h = setTimeout(i ? k : l, i === c ? e - m : e);}}
// }if ($.guid) {g.guid = j.guid = j.guid || $.guid++;}return g;
// };$.debounce = function (d, e, f) {return f === c ? a(d, e, false) : a(d, f, e !== false);};
// })(this);
var Modules = {};
Modules.Support = (function( $ ) {
'use strict';
var self = {},
// some small (2x1 px) test images for each feature
webpImages = {
basic: 'data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACyAgCdASoCAAEALmk0mk0iIiIiIgBoSygABc6zbAAA/v56QAAAAA==',
lossless: 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA='
};
self.webp = function( feature ) {
var dfd = $.Deferred();
$('<img>')
.on('load', function() {
// the images should have these dimensions
if ( this.width === 2 && this.height === 1 ) {
dfd.resolve();
} else {
dfd.reject();
}
})
// Reject deferred on error
.on('error', function() {
dfd.reject();
})
// Set the image src
.attr('src', webpImages[ feature || 'basic' ]);
return dfd.promise();
};
// Fill rAF
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] ||
window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
return self;
}( jQuery ));
Modules.NavTray = (function () {
Modules.Polyfill = (function( $, Support ) {
'use strict';
var init = function() {
// If the deferred object in webp is rejected, call the webp polyfill function
Support.webp().fail( webp );
},
webp = function() {
$('img[src$=".webp"]').each(function() {
this.src = this.src.replace('.webp', '.jpg');
});
};
return {
init: init
};
}( jQuery, Modules.Support ));
Modules.Nav = (function( $ ) {
'use strict';
function NavTray( element ) {
this.$el = $( element );
function NavTray(element) {
this.element = element;
this.isOpen = false;
this.$window = $( window );
this.$trigger = this.$el.find('.js-nav-toggle');
this.$tray = this.$el.find('.js-tray');
this.trigger = element.querySelector('.js-nav-toggle');
this.tray = element.querySelector('.js-tray');
this.openLabel = this.$trigger.text();
this.closeLabel = this.$trigger.attr('data-close-label');
this.init();
}
this.openLabel = this.trigger.textContent;
this.closeLabel = this.trigger.getAttribute('data-close-label');
NavTray.prototype.init = function() {
this.setEvenHeights();
this.listen();
@ -132,253 +43,234 @@ Modules.Nav = (function( $ ) {
// I've changed the fallback font to Verdana, sans-serif to better
// represent Ubuntu's wideness.
// Also this isn't needed on load and could be done on window load
setTimeout( $.proxy( this.saveHeight, this ), 100 );
setTimeout(this.saveHeight.bind(this), 100);
}
NavTray.initialize = function () {
return new NavTray(document.getElementById('nav'));
};
NavTray.prototype.saveHeight = function() {
this.collapseHeight = this.$tray.children()[0].offsetHeight;
NavTray.prototype.saveHeight = function () {
this.collapseHeight = this.tray.children[0].offsetHeight;
};
NavTray.prototype.listen = function() {
this.$trigger.on( 'click', $.proxy( this.toggle, this ) );
this.$window.on( 'resize', $.proxy( this.onResize, this ) );
NavTray.prototype.listen = function () {
this.trigger.addEventListener('click', this.toggle.bind(this));
window.addEventListener('resize', this.onResize.bind(this));
};
NavTray.prototype.onResize = function() {
this.$tray.css( 'height', '' );
NavTray.prototype.onResize = function () {
this.tray.style.height = '';
this.setEvenHeights();
this.saveHeight();
if ( this.isOpen ) {
this.$tray.css( 'height', this.collapseHeight );
if (this.isOpen) {
this.tray.style.height = this.collapseHeight + 'px';
}
};
NavTray.prototype.toggle = function() {
NavTray.prototype.toggle = function () {
this.toggleBtnText();
if ( this.isOpen ) {
if (this.isOpen) {
this.close();
} else {
this.open();
}
};
NavTray.prototype.open = function() {
this.$el.removeClass('collapsed');
this.$tray.css( 'height', this.collapseHeight );
NavTray.prototype.open = function () {
this.element.classList.remove('collapsed');
this.tray.style.height = this.collapseHeight + 'px';
this.isOpen = true;
};
NavTray.prototype.close = function() {
this.$el.addClass('collapsed');
this.$tray.css( 'height', '' );
NavTray.prototype.close = function () {
this.element.classList.add('collapsed');
this.tray.style.height = '';
this.isOpen = false;
};
NavTray.prototype.toggleBtnText = function() {
NavTray.prototype.toggleBtnText = function () {
var label = this.isOpen ? this.openLabel : this.closeLabel;
this.$trigger.text( label );
this.trigger.textContent = label;
};
NavTray.prototype.setEvenHeights = function() {
var groups = [
this.$el.find('.js-demo'),
$('#main .js-demo')
];
$.evenHeights( groups );
NavTray.prototype.setEvenHeights = function () {
window.evenHeights([
this.element.querySelectorAll('.js-demo'),
document.querySelectorAll('#main .js-demo'),
]);
};
return {
init: function() {
return new NavTray( document.getElementById('nav') );
}
return NavTray;
}());
Modules.Sprite = (function () {
var Sprite = function (context, img, size) {
this.ctx = context;
this.img = img;
this.width = size;
this.height = size;
this.frameWidth = size;
this.frameHeight = size;
};
// Assuming horizontal sprite
Sprite.prototype.getFrame = function (frame) {
return {
x: frame * this.frameWidth,
y: 0,
};
};
}( jQuery ));
Sprite.prototype.clearCanvas = function () {
this.ctx.clearRect(0, 0, this.width, this.height);
};
Sprite.prototype.drawFrame = function (frameNumber) {
var frame = this.getFrame(frameNumber);
// Clear out the last frame
this.clearCanvas();
// Draw to the context. This method is really confusing...
this.ctx.drawImage(
this.img,
frame.x,
frame.y,
this.width,
this.height,
0,
0,
this.width,
this.height
);
};
return Sprite;
}());
Modules.Favicon = (function( doc ) {
'use strict';
Modules.Favicon = (function (doc) {
var Favicon = function( src, numFrames, framesPerAnimation, animationDelay ) {
var self = this;
var Favicon = function (src, numFrames, framesPerAnimation, animationDelay) {
// Variables based on params
self.src = src;
self.numFrames = numFrames;
self.framesPerAnimation = framesPerAnimation;
self.animationDelay = animationDelay;
this.src = src;
this.numFrames = numFrames;
this.framesPerAnimation = framesPerAnimation;
this.animationDelay = animationDelay;
// Elements
self.canvas = doc.createElement('canvas');
self.img = doc.createElement('img');
self.html = doc.documentElement;
this.canvas = doc.createElement('canvas');
this.img = doc.createElement('img');
this.html = doc.documentElement;
// Calculations
self.size = window.devicePixelRatio > 1 ? 32 : 16;
this.size = window.devicePixelRatio > 1 ? 32 : 16;
// If it's not a data url, pick apart the filename and add @2x for retina
if ( !self.src.match(/data:/) && window.devicePixelRatio > 1 ) {
var dot = self.src.lastIndexOf('.');
self.src = self.src.substring( 0, dot ) + '@2x' + self.src.substring( dot );
if (!this.src.match(/data:/) && window.devicePixelRatio > 1) {
var dot = this.src.lastIndexOf('.');
this.src = this.src.substring(0, dot) + '@2x' + this.src.substring(dot);
}
self.currentFrame = 0;
// Chrome chokes on this. It looks like it can handle 4 frames per second
self.fps = 24;
self.init();
};
this.currentFrame = 0;
Favicon.prototype.init = function() {
var self = this;
// Chrome chokes on this. It looks like it can handle 4 frames per second
this.fps = 24;
// No #favicon element or browser doesn't support canvas or < IE9, stop
if ( !doc.getElementById('favicon') || !self.canvas.getContext || self.html.className.indexOf('lt-ie9') > -1 ) {
if (!doc.getElementById('favicon') || !this.canvas.getContext || this.html.className.indexOf('lt-ie9') > -1) {
return;
}
// Save context
self.ctx = self.canvas.getContext('2d');
this.ctx = this.canvas.getContext('2d');
// Set canvas dimensions based on device DPI
self.canvas.height = self.canvas.width = self.size;
this.canvas.height = this.canvas.width = this.size;
// Create a new sprite 32x32 size with 32x32 sprites
self.sprite = new Sprite( self.ctx, self.img, self.size );
this.sprite = new Modules.Sprite(this.ctx, this.img, this.size);
// Bind the image load handler
self.img.onload = self.onSpriteLoaded.bind( self );
this.img.onload = this.onSpriteLoaded.bind(this);
// Trigger image to load
self.img.src = self.src;
this.img.src = this.src;
};
Favicon.prototype.getData = function() {
Favicon.prototype.getData = function () {
return this.canvas.toDataURL('image/png');
};
// Clone the current #favicon and replace it with a new element
// which has the updated data URI href
Favicon.prototype.setFavicon = function() {
var self = this,
data = self.getData(),
originalFavicon = doc.getElementById('favicon'),
clone = originalFavicon.cloneNode( true );
clone.setAttribute( 'href', data );
originalFavicon.parentNode.replaceChild( clone, originalFavicon );
Favicon.prototype.setFavicon = function () {
var data = this.getData();
var originalFavicon = doc.getElementById('favicon');
var clone = originalFavicon.cloneNode(true);
clone.setAttribute('href', data);
originalFavicon.parentNode.replaceChild(clone, originalFavicon);
};
// Request Animation Frame Loop
Favicon.prototype.loop = function( timestamp ) {
var self = this;
Favicon.prototype.loop = function (timestamp) {
// If not enough time has elapse since the last call
// immediately call the next rAF
if ( timestamp - self.lastExecuted < self.timeToElapse ) {
return requestAnimationFrame( self.loop.bind( self ) );
if (timestamp - this.lastExecuted < this.timeToElapse) {
return requestAnimationFrame(this.loop.bind(this));
}
// Increment current frame
self.currentFrame += 1;
if ( self.currentFrame === self.numFrames ) {
self.currentFrame = 0;
this.currentFrame += 1;
if (this.currentFrame === this.numFrames) {
this.currentFrame = 0;
}
// Completed an animation state
self.timeToElapse = self.currentFrame % self.framesPerAnimation === 0 ?
self.animationDelay :
1000 / self.fps;
this.timeToElapse = this.currentFrame % this.framesPerAnimation === 0 ?
this.animationDelay :
1000 / this.fps;
// Draw current frame from sprite
self.sprite.drawFrame( self.currentFrame );
this.sprite.drawFrame(this.currentFrame);
// Swap <link>
self.setFavicon();
this.setFavicon();
// Set a timeout to draw again
self.lastExecuted = timestamp;
this.lastExecuted = timestamp;
// Continue loop
return requestAnimationFrame( self.loop.bind( self ) );
return requestAnimationFrame(this.loop.bind(this));
};
// Sprite loaded
Favicon.prototype.onSpriteLoaded = function() {
var self = this;
Favicon.prototype.onSpriteLoaded = function () {
// Draw the first frame when the image loads
self.sprite.drawFrame( self.currentFrame );
this.sprite.drawFrame(this.currentFrame);
// Swap <link>
self.setFavicon();
this.setFavicon();
// Start loop
requestAnimationFrame( self.loop.bind( self ) );
};
var Sprite = function( context, img, size ) {
var self = this;
self.ctx = context;
self.img = img;
self.width = size;
self.height = size;
self.frameWidth = size;
self.frameHeight = size;
};
// Assuming horizontal sprite
Sprite.prototype.getFrame = function( frame ) {
return {
x: frame * this.frameWidth,
y: 0
};
};
Sprite.prototype.clearCanvas = function() {
this.ctx.clearRect( 0, 0, this.width, this.height );
};
Sprite.prototype.drawFrame = function( frameNumber ) {
var self = this;
var frame = self.getFrame( frameNumber );
// Clear out the last frame
self.clearCanvas();
// Draw to the context. This method is really confusing...
self.ctx.drawImage(
self.img,
frame.x,
frame.y,
self.width,
self.height,
0,
0,
self.width,
self.height
);
requestAnimationFrame(this.loop.bind(this));
};
return Favicon;
}( document ));
}(document));
$(document).ready(function() {
'use strict';
document.addEventListener('DOMContentLoaded', function () {
Modules.Nav.init();
Modules.Polyfill.init();
Modules.NavTray.initialize();
// Only animate the favicon on the homepage so that
// timeline tests aren't filled with junk
if ( window.location.pathname === '/Shuffle/' ) {
if (true || window.location.pathname === '/Shuffle/') {
var src = site_url + '/img/favicon-sprite.png';
new Modules.Favicon( src, 21, 7, 3000 * 1 );
new Modules.Favicon(src, 21, 7, 3000 * 1);
}
});

Loading…
Cancel
Save