diff --git a/bower.json b/bower.json index e4db9fc..d708a34 100644 --- a/bower.json +++ b/bower.json @@ -6,7 +6,7 @@ "Liam Brummitt (http://brm.io/)" ], "description": "a more robust, responsive equal heights plugin for jQuery", - "main": "jquery.matchHeight.js", + "main": "dist/jquery.matchHeight.js", "keywords": [ "matchHeight", "equal", diff --git a/dist/jquery.matchHeight-min.js b/dist/jquery.matchHeight-min.js new file mode 100644 index 0000000..f92f62c --- /dev/null +++ b/dist/jquery.matchHeight-min.js @@ -0,0 +1,11 @@ +/** +* jquery.matchHeight-min.js v0.6.0 +* http://brm.io/jquery-match-height/ +* License: MIT +*/ +(function(c){var n=-1,f=-1,g=function(a){return parseFloat(a)||0},r=function(a){var b=null,d=[];c(a).each(function(){var a=c(this),k=a.offset().top-g(a.css("margin-top")),l=0=Math.floor(Math.abs(b-k))?d[d.length-1]=l.add(a):d.push(a);b=k});return d},p=function(a){var b={byRow:!0,property:"height",target:null,remove:!1};if("object"===typeof a)return c.extend(b,a);"boolean"===typeof a?b.byRow=a:"remove"===a&&(b.remove=!0);return b},b=c.fn.matchHeight= +function(a){a=p(a);if(a.remove){var e=this;this.css(a.property,"");c.each(b._groups,function(a,b){b.elements=b.elements.not(e)});return this}if(1>=this.length&&!a.target)return this;b._groups.push({elements:this,options:a});b._apply(this,a);return this};b._groups=[];b._throttle=80;b._maintainScroll=!1;b._beforeUpdate=null;b._afterUpdate=null;b._apply=function(a,e){var d=p(e),h=c(a),k=[h],l=c(window).scrollTop(),f=c("html").outerHeight(!0),m=h.parents().filter(":hidden");m.each(function(){var a=c(this); +a.data("style-cache",a.attr("style"))});m.css("display","block");d.byRow&&!d.target&&(h.each(function(){var a=c(this),b="inline-block"===a.css("display")?"inline-block":"block";a.data("style-cache",a.attr("style"));a.css({display:b,"padding-top":"0","padding-bottom":"0","margin-top":"0","margin-bottom":"0","border-top-width":"0","border-bottom-width":"0",height:"100px"})}),k=r(h),h.each(function(){var a=c(this);a.attr("style",a.data("style-cache")||"")}));c.each(k,function(a,b){var e=c(b),f=0;if(d.target)f= +d.target.outerHeight(!1);else{if(d.byRow&&1>=e.length){e.css(d.property,"");return}e.each(function(){var a=c(this),b={display:"inline-block"===a.css("display")?"inline-block":"block"};b[d.property]="";a.css(b);a.outerHeight(!1)>f&&(f=a.outerHeight(!1));a.css("display","")})}e.each(function(){var a=c(this),b=0;d.target&&a.is(d.target)||("border-box"!==a.css("box-sizing")&&(b+=g(a.css("border-top-width"))+g(a.css("border-bottom-width")),b+=g(a.css("padding-top"))+g(a.css("padding-bottom"))),a.css(d.property, +f-b))})});m.each(function(){var a=c(this);a.attr("style",a.data("style-cache")||null)});b._maintainScroll&&c(window).scrollTop(l/f*c("html").outerHeight(!0));return this};b._applyDataApi=function(){var a={};c("[data-match-height], [data-mh]").each(function(){var b=c(this),d=b.attr("data-mh")||b.attr("data-match-height");a[d]=d in a?a[d].add(b):b});c.each(a,function(){this.matchHeight(!0)})};var q=function(a){b._beforeUpdate&&b._beforeUpdate(a,b._groups);c.each(b._groups,function(){b._apply(this.elements, +this.options)});b._afterUpdate&&b._afterUpdate(a,b._groups)};b._update=function(a,e){if(e&&"resize"===e.type){var d=c(window).width();if(d===n)return;n=d}a?-1===f&&(f=setTimeout(function(){q(e);f=-1},b._throttle)):q(e)};c(b._applyDataApi);c(window).bind("load",function(a){b._update(!1,a)});c(window).bind("resize orientationchange",function(a){b._update(!0,a)})})(jQuery); \ No newline at end of file diff --git a/dist/jquery.matchHeight.js b/dist/jquery.matchHeight.js new file mode 100644 index 0000000..701df19 --- /dev/null +++ b/dist/jquery.matchHeight.js @@ -0,0 +1,353 @@ +/** +* jquery.matchHeight.js v0.6.0 +* http://brm.io/jquery-match-height/ +* License: MIT +*/ + +;(function($) { + /* + * internal + */ + + var _previousResizeWidth = -1, + _updateTimeout = -1; + + /* + * _parse + * value parse utility function + */ + + var _parse = function(value) { + // parse value and convert NaN to 0 + return parseFloat(value) || 0; + }; + + /* + * _rows + * utility function returns array of jQuery selections representing each row + * (as displayed after float wrapping applied by browser) + */ + + var _rows = function(elements) { + var tolerance = 1, + $elements = $(elements), + lastTop = null, + rows = []; + + // group elements by their top position + $elements.each(function(){ + var $that = $(this), + top = $that.offset().top - _parse($that.css('margin-top')), + lastRow = rows.length > 0 ? rows[rows.length - 1] : null; + + if (lastRow === null) { + // first item on the row, so just push it + rows.push($that); + } else { + // if the row top is the same, add to the row group + if (Math.floor(Math.abs(lastTop - top)) <= tolerance) { + rows[rows.length - 1] = lastRow.add($that); + } else { + // otherwise start a new row group + rows.push($that); + } + } + + // keep track of the last row top + lastTop = top; + }); + + return rows; + }; + + /* + * _parseOptions + * handle plugin options + */ + + var _parseOptions = function(options) { + var opts = { + byRow: true, + property: 'height', + target: null, + remove: false + }; + + if (typeof options === 'object') { + return $.extend(opts, options); + } + + if (typeof options === 'boolean') { + opts.byRow = options; + } else if (options === 'remove') { + opts.remove = true; + } + + return opts; + }; + + /* + * matchHeight + * plugin definition + */ + + var matchHeight = $.fn.matchHeight = function(options) { + var opts = _parseOptions(options); + + // handle remove + if (opts.remove) { + var that = this; + + // remove fixed height from all selected elements + this.css(opts.property, ''); + + // remove selected elements from all groups + $.each(matchHeight._groups, function(key, group) { + group.elements = group.elements.not(that); + }); + + // TODO: cleanup empty groups + + return this; + } + + if (this.length <= 1 && !opts.target) { + return this; + } + + // keep track of this group so we can re-apply later on load and resize events + matchHeight._groups.push({ + elements: this, + options: opts + }); + + // match each element's height to the tallest element in the selection + matchHeight._apply(this, opts); + + return this; + }; + + /* + * plugin global options + */ + + matchHeight._groups = []; + matchHeight._throttle = 80; + matchHeight._maintainScroll = false; + matchHeight._beforeUpdate = null; + matchHeight._afterUpdate = null; + + /* + * matchHeight._apply + * apply matchHeight to given elements + */ + + matchHeight._apply = function(elements, options) { + var opts = _parseOptions(options), + $elements = $(elements), + rows = [$elements]; + + // take note of scroll position + var scrollTop = $(window).scrollTop(), + htmlHeight = $('html').outerHeight(true); + + // get hidden parents + var $hiddenParents = $elements.parents().filter(':hidden'); + + // cache the original inline style + $hiddenParents.each(function() { + var $that = $(this); + $that.data('style-cache', $that.attr('style')); + }); + + // temporarily must force hidden parents visible + $hiddenParents.css('display', 'block'); + + // get rows if using byRow, otherwise assume one row + if (opts.byRow && !opts.target) { + + // must first force an arbitrary equal height so floating elements break evenly + $elements.each(function() { + var $that = $(this), + display = $that.css('display') === 'inline-block' ? 'inline-block' : 'block'; + + // cache the original inline style + $that.data('style-cache', $that.attr('style')); + + $that.css({ + 'display': display, + 'padding-top': '0', + 'padding-bottom': '0', + 'margin-top': '0', + 'margin-bottom': '0', + 'border-top-width': '0', + 'border-bottom-width': '0', + 'height': '100px' + }); + }); + + // get the array of rows (based on element top position) + rows = _rows($elements); + + // revert original inline styles + $elements.each(function() { + var $that = $(this); + $that.attr('style', $that.data('style-cache') || ''); + }); + } + + $.each(rows, function(key, row) { + var $row = $(row), + targetHeight = 0; + + if (!opts.target) { + // skip apply to rows with only one item + if (opts.byRow && $row.length <= 1) { + $row.css(opts.property, ''); + return; + } + + // iterate the row and find the max height + $row.each(function(){ + var $that = $(this), + display = $that.css('display') === 'inline-block' ? 'inline-block' : 'block'; + + // ensure we get the correct actual height (and not a previously set height value) + var css = { 'display': display }; + css[opts.property] = ''; + $that.css(css); + + // find the max height (including padding, but not margin) + if ($that.outerHeight(false) > targetHeight) { + targetHeight = $that.outerHeight(false); + } + + // revert display block + $that.css('display', ''); + }); + } else { + // if target set, use the height of the target element + targetHeight = opts.target.outerHeight(false); + } + + // iterate the row and apply the height to all elements + $row.each(function(){ + var $that = $(this), + verticalPadding = 0; + + // don't apply to a target + if (opts.target && $that.is(opts.target)) { + return; + } + + // handle padding and border correctly (required when not using border-box) + if ($that.css('box-sizing') !== 'border-box') { + verticalPadding += _parse($that.css('border-top-width')) + _parse($that.css('border-bottom-width')); + verticalPadding += _parse($that.css('padding-top')) + _parse($that.css('padding-bottom')); + } + + // set the height (accounting for padding and border) + $that.css(opts.property, targetHeight - verticalPadding); + }); + }); + + // revert hidden parents + $hiddenParents.each(function() { + var $that = $(this); + $that.attr('style', $that.data('style-cache') || null); + }); + + // restore scroll position if enabled + if (matchHeight._maintainScroll) { + $(window).scrollTop((scrollTop / htmlHeight) * $('html').outerHeight(true)); + } + + return this; + }; + + /* + * matchHeight._applyDataApi + * applies matchHeight to all elements with a data-match-height attribute + */ + + matchHeight._applyDataApi = function() { + var groups = {}; + + // generate groups by their groupId set by elements using data-match-height + $('[data-match-height], [data-mh]').each(function() { + var $this = $(this), + groupId = $this.attr('data-mh') || $this.attr('data-match-height'); + + if (groupId in groups) { + groups[groupId] = groups[groupId].add($this); + } else { + groups[groupId] = $this; + } + }); + + // apply matchHeight to each group + $.each(groups, function() { + this.matchHeight(true); + }); + }; + + /* + * matchHeight._update + * updates matchHeight on all current groups with their correct options + */ + + var _update = function(event) { + if (matchHeight._beforeUpdate) { + matchHeight._beforeUpdate(event, matchHeight._groups); + } + + $.each(matchHeight._groups, function() { + matchHeight._apply(this.elements, this.options); + }); + + if (matchHeight._afterUpdate) { + matchHeight._afterUpdate(event, matchHeight._groups); + } + }; + + matchHeight._update = function(throttle, event) { + // prevent update if fired from a resize event + // where the viewport width hasn't actually changed + // fixes an event looping bug in IE8 + if (event && event.type === 'resize') { + var windowWidth = $(window).width(); + if (windowWidth === _previousResizeWidth) { + return; + } + _previousResizeWidth = windowWidth; + } + + // throttle updates + if (!throttle) { + _update(event); + } else if (_updateTimeout === -1) { + _updateTimeout = setTimeout(function() { + _update(event); + _updateTimeout = -1; + }, matchHeight._throttle); + } + }; + + /* + * bind events + */ + + // apply on DOM ready event + $(matchHeight._applyDataApi); + + // update heights on load and resize events + $(window).bind('load', function(event) { + matchHeight._update(false, event); + }); + + // throttled update heights on resize events + $(window).bind('resize orientationchange', function(event) { + matchHeight._update(true, event); + }); + +})(jQuery); diff --git a/gulpfile.js b/gulpfile.js index 66678e9..6c263b8 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -21,6 +21,7 @@ var staticTransform = require('connect-static-transform'); var pkg = require('./package.json'); var extend = require('util')._extend; var fs = require('fs'); +var buildDirectory = 'dist'; var server; gulp.task('release', function(callback) { @@ -33,7 +34,11 @@ gulp.task('release:push', function(callback) { }); gulp.task('release:push:github', function(callback) { - return gulp.src(['CHANGELOG.md', 'jquery.matchHeight-min.js', 'jquery.matchHeight.js']) + return gulp.src([ + 'CHANGELOG.md', + buildDirectory + '/jquery.matchHeight-min.js', + buildDirectory + '/jquery.matchHeight.js' + ]) .pipe(release({ tag: pkg.version, name: 'jquery.matchHeight.js ' + pkg.version @@ -51,12 +56,18 @@ gulp.task('release:push:npm', function(callback) { gulp.task('build', function() { var build = extend(pkg); build.version = process.argv[4] || pkg.version; - return gulp.src(pkg.main) + + gulp.src('jquery.matchHeight.js') + .pipe(replace("jquery-match-height master", "jquery-match-height " + build.version)) + .pipe(replace("version = 'master'", "version = '" + build.version + "'")) + .pipe(gulp.dest(buildDirectory)); + + return gulp.src('jquery.matchHeight.js') .pipe(replace("version = 'master'", "version = '" + build.version + "'")) .pipe(uglify({ output: { max_line_len: 500 } })) .pipe(header(banner, { build: build })) .pipe(rename({ suffix: '-min' })) - .pipe(gulp.dest('.')); + .pipe(gulp.dest(buildDirectory)); }); gulp.task('lint', function() { diff --git a/jquery.matchHeight.js b/jquery.matchHeight.js index b976f2d..ab24d45 100644 --- a/jquery.matchHeight.js +++ b/jquery.matchHeight.js @@ -1,5 +1,5 @@ /** -* jquery.matchHeight.js master +* jquery-match-height master by @liabru * http://brm.io/jquery-match-height/ * License: MIT */ diff --git a/package.json b/package.json index ac4b6e2..346b49b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "homepage": "http://brm.io/jquery-match-height/", "author": "Liam Brummitt (http://brm.io/)", "description": "a responsive equal heights plugin for jQuery", - "main": "jquery.matchHeight.js", + "main": "dist/jquery.matchHeight.js", "repository": { "type": "git", "url": "https://github.com/liabru/jquery-match-height.git"