/** * jquery-match-height master by @liabru * http://brm.io/jquery-match-height/ * License: MIT */ ;(function(factory) { // eslint-disable-line no-extra-semi 'use strict'; if (typeof define === 'function' && define.amd) { // AMD define(['jquery'], factory); } else if (typeof module !== 'undefined' && module.exports) { // CommonJS module.exports = factory(require('jquery')); } else { // Global factory(jQuery); } })(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.version = 'master'; matchHeight._groups = []; matchHeight._throttle = 80; matchHeight._maintainScroll = false; matchHeight._beforeUpdate = null; matchHeight._afterUpdate = null; matchHeight._rows = _rows; matchHeight._parse = _parse; matchHeight._parseOptions = _parseOptions; /* * 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'); // temporarily force a usable display value if (display !== 'inline-block' && display !== 'flex' && display !== 'inline-flex') { display = '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', 'overflow': 'hidden' }); }); // 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), style = $that.attr('style'), display = $that.css('display'); // temporarily force a usable display value if (display !== 'inline-block' && display !== 'flex' && display !== 'inline-flex') { display = '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 styles if (style) { $that.attr('style', style); } else { $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) + 'px'); }); }); // 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); // use on or bind where supported var on = $.fn.on ? 'on' : 'bind'; // update heights on load and resize events $(window)[on]('load', function(event) { matchHeight._update(false, event); }); // throttled update heights on resize events $(window)[on]('resize orientationchange', function(event) { matchHeight._update(true, event); }); }); ; /*! * jquery.customSelect() - v0.5.1 * http://adam.co/lab/jquery/customselect/ * 2014-04-19 * * Copyright 2013 Adam Coulombe * @license http://www.opensource.org/licenses/mit-license.html MIT License * @license http://www.gnu.org/licenses/gpl.html GPL2 License */ (function(a){a.fn.extend({customSelect:function(c){if(typeof document.body.style.maxHeight==="undefined"){return this}var e={customClass:"customSelect",mapClass:true,mapStyle:true},c=a.extend(e,c),d=c.customClass,f=function(h,k){var g=h.find(":selected"),j=k.children(":first"),i=g.html()||" ";j.html(i);if(g.attr("disabled")){k.addClass(b("DisabledOption"))}else{k.removeClass(b("DisabledOption"))}setTimeout(function(){k.removeClass(b("Open"));a(document).bind("mouseup.customSelect")},60)},b=function(g){return d+g};return this.each(function(){var g=a(this),i=a("").addClass(b("Inner")),h=a("");g.after(h.append(i));h.addClass(d);if(c.mapClass){h.addClass(g.attr("class"))}if(c.mapStyle){h.attr("style",g.attr("style"))}g.addClass("hasCustomSelect").bind("render.customSelect",function(){f(g,h);g.css("width","");var k=parseInt(g.outerWidth(),10)-(parseInt(h.outerWidth(),10)-parseInt(h.width(),10));h.css({display:"inline-block"});var j=h.outerHeight();if(g.attr("disabled")){h.addClass(b("Disabled"))}else{h.removeClass(b("Disabled"))}i.css({width:k,display:"inline-block"});g.css({"-webkit-appearance":"menulist-button",width:h.outerWidth(),position:"absolute",opacity:0,height:j,fontSize:h.css("font-size")})}).bind("change.customSelect",function(){h.addClass(b("Changed"));f(g,h)}).bind("keyup.customSelect",function(j){if(!h.hasClass(b("Open"))){g.trigger("blur.customSelect");g.trigger("focus.customSelect")}else{if(j.which==13||j.which==27){f(g,h)}}}).bind("mousedown.customSelect",function(){h.removeClass(b("Changed"))}).bind("mouseup.customSelect",function(j){if(!h.hasClass(b("Open"))){if(a("."+b("Open")).not(h).length>0&&typeof InstallTrigger!=="undefined"){g.trigger("focus.customSelect")}else{h.addClass(b("Open"));j.stopPropagation();a(document).one("mouseup.customSelect",function(k){if(k.target!=g.get(0)&&a.inArray(k.target,g.find("*").get())<0){g.trigger("blur.customSelect")}else{f(g,h)}})}}}).bind("focus.customSelect",function(){h.removeClass(b("Changed")).addClass(b("Focus"))}).bind("blur.customSelect",function(){h.removeClass(b("Focus")+" "+b("Open"))}).bind("mouseenter.customSelect",function(){h.addClass(b("Hover"))}).bind("mouseleave.customSelect",function(){h.removeClass(b("Hover"))}).trigger("render.customSelect")})}})})(jQuery);; /*////////////////////////////////////////////////////////////////////////////// || Header Stack Component //////////////////////////////////////////////////////////////////////////////*/ HeaderStack = (function(window, document, $){ "use strict"; // "hide" is the method of hiding the stack item that is bound to the scroll // Options to build // options are commar seperated as the data-header-stack-item attribute value // hide if sibling - hide me when a subsequent item gets added - again bind this to the scroll // mobile squash - after a period of scrolling hide me on mobile /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ function HeaderStack(element, p_options){ this.elem = element; this.$elem = $(element); this.options = { zindex_start: 900 }; // Extend this.options = $.extend({}, this.options, p_options); // Variables this.$items = this.$elem.find("[data-header-stack-item]"); this.$intruders = this.$elem.find("[data-header-stack-intruder]:visible, #toolbar"); this.ghosts = []; this.$fixed = $([]); this.process_all = true; this.base_scroll_mod = 0; // Ini this.ini(); } /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ HeaderStack.prototype.ini = function(){ var obj = this; // Stack items should not be moved from the DOM, making them fixed rather than moving them from the dom allows the tabindex order to remain correct. // This way when reverse tabbing the tab will sort itself out and moving back up the page should dock the line back into flow. // This should actually be applied to the
tag, and should harvest stack items using data- attributes // Process Options this.$items.each(function(){ var options = $(this).attr("data-header-stack-item"); var options_obj = {}; if (options !== undefined){ var options = options.split("|"); for(var i = 0; i < options.length; i++){ if (options[i] !== ""){ options_obj[options[i]] = true; } } } $(this).data("header-stack-options", options_obj); //console.log("options", $(this).data("header-stack-options"), this); }); // Set base scroll mod based on intruders // If there are externally fixed items, they should adjust the starting point for the modifier this.base_scroll_mod = 0; this.$intruders.each(function(){ if($(this).css("position") == "fixed"){ obj.base_scroll_mod += $(this).outerHeight(); } }); // Setup breakpoint switching // Notifier.respond("enable-breakpoint", { obj: this }, function(e){ e.data.obj.switchedBreakpoint(); }); $(window).bind("resize", { obj: this }, function(e){ e.data.obj.switchedBreakpoint(); }); // Setup scroll $(window).bind("scroll.header-stack", { obj: this }, function(e){ e.data.obj.scroll(e); }); this.scroll(); this.process_all = false; }; HeaderStack.prototype.scroll = function(e){ var obj = this; var scroll_top = window.pageYOffset; var modifier = this.$fixed.length > 0 ? parseInt(this.$fixed.last().css("top")) + this.$fixed.last().outerHeight() : 0; var scroll_top_mod = scroll_top + modifier; // Loop all non-fixed items this.$items.filter(":not(.fixed)").each(function(){ var offset = $(this).offset(); if (offset.top < (scroll_top_mod + obj.base_scroll_mod)){ obj.stickItem(this); // Reset scroll top modifier modifier = obj.$fixed.length > 0 ? parseInt(obj.$fixed.last().css("top")) + obj.$fixed.last().outerHeight() : 0; scroll_top_mod = scroll_top + modifier; if (!obj.process_all){ return false; // if we're hitting an item to stick, the rest wont need unsticking ... potentially } }else{ // no need to continue if we're higher than the highest item return false; } }); // Loop all ghosts - backwards as we should hit ghosts in reverse order for (var i = this.ghosts.length-1; i >= 0; i--){ var go = this.ghosts[i]; var offset = go.$ghost.offset(); var ghost_bottom = offset.top + go.$item.outerHeight(); if (ghost_bottom >= scroll_top_mod){ obj.unstickItem(go, i); modifier = obj.$fixed.length > 0 ? parseInt(obj.$fixed.last().css("top")) + obj.$fixed.last().outerHeight() : 0; scroll_top_mod = scroll_top + modifier; //break; // if we're hitting an item to unstick, the rest wont need unsticking ... potentially }else{ // theres no need to continue if we're lower than the lowest ghost break; } } // Loop all items that are supposed to be hiding themselves //this.$fixed.filter(".hiding").each(function(){ // console.log("me me me", this); // var diff = scroll_top - $(this).data("hide-start-scroll-top"); // if (diff > $(this).outerHeight()){ // diff = $(this).outerHeight(); // $(this).removeClass("hiding"); // } // $(this).css({ top: -diff }); //}); }; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ HeaderStack.prototype.stickItem = function(item){ var $item = $(item); var height = $item.outerHeight(); var left = $item.offset().left; // Create Ghost Placeholder var $ghost = $("