/*! * classie - class helper functions * from bonzo https://github.com/ded/bonzo * * classie.has( elem, 'my-class' ) -> true/false * classie.add( elem, 'my-new-class' ) * classie.remove( elem, 'my-unwanted-class' ) * classie.toggle( elem, 'my-class' ) */ /*jshint browser: true, strict: true, undef: true */ /*global define: false */ ( function( window ) { 'use strict'; // class helper functions from bonzo https://github.com/ded/bonzo function classReg( className ) { return new RegExp("(^|\\s+)" + className + "(\\s+|$)"); } // classList support for class management // altho to be fair, the api sucks because it won't accept multiple classes at once var hasClass, addClass, removeClass; if ( 'classList' in document.documentElement ) { hasClass = function( elem, c ) { return elem.classList.contains( c ); }; addClass = function( elem, c ) { elem.classList.add( c ); }; removeClass = function( elem, c ) { elem.classList.remove( c ); }; } else { hasClass = function( elem, c ) { return classReg( c ).test( elem.className ); }; addClass = function( elem, c ) { if ( !hasClass( elem, c ) ) { elem.className = elem.className + ' ' + c; } }; removeClass = function( elem, c ) { elem.className = elem.className.replace( classReg( c ), ' ' ); }; } function toggleClass( elem, c ) { var fn = hasClass( elem, c ) ? removeClass : addClass; fn( elem, c ); } var classie = { // full names hasClass: hasClass, addClass: addClass, removeClass: removeClass, toggleClass: toggleClass, // short names has: hasClass, add: addClass, remove: removeClass, toggle: toggleClass }; // transport if ( typeof define === 'function' && define.amd ) { // AMD define( classie ); } else { // browser global window.classie = classie; } })( window ); (function ($) { $.fn.countTo = function (options) { options = options || {}; return $(this).each(function () { // set options for current element var settings = $.extend({}, $.fn.countTo.defaults, { from: $(this).data('from'), to: $(this).data('to'), speed: $(this).data('speed'), refreshInterval: $(this).data('refresh-interval'), decimals: $(this).data('decimals') }, options); // how many times to update the value, and how much to increment the value on each update var loops = Math.ceil(settings.speed / settings.refreshInterval), increment = (settings.to - settings.from) / loops; // references & variables that will change with each update var self = this, $self = $(this), loopCount = 0, value = settings.from, data = $self.data('countTo') || {}; $self.data('countTo', data); // if an existing interval can be found, clear it first if (data.interval) { clearInterval(data.interval); } data.interval = setInterval(updateTimer, settings.refreshInterval); // initialize the element with the starting value render(value); function updateTimer() { value += increment; loopCount++; render(value); if (typeof(settings.onUpdate) == 'function') { settings.onUpdate.call(self, value); } if (loopCount >= loops) { // remove the interval $self.removeData('countTo'); clearInterval(data.interval); value = settings.to; if (typeof(settings.onComplete) == 'function') { settings.onComplete.call(self, value); } } } function render(value) { var formattedValue = settings.formatter.call(self, value, settings); $self.text(formattedValue); } }); }; $.fn.countTo.defaults = { from: 0, // the number the element should start at to: 0, // the number the element should end at speed: 1000, // how long it should take to count between the target numbers refreshInterval: 100, // how often the element should be updated decimals: 0, // the number of decimal places to show formatter: formatter, // handler for formatting the value before rendering onUpdate: null, // callback method for every time the element is updated onComplete: null // callback method for when the element finishes updating }; function formatter(value, settings) { return value.toFixed(settings.decimals); } }(jQuery)); (function ($) { $.fn.directionalHover = function(options) { // Extend default plugin options var opts = $.extend({}, $.fn.directionalHover.defaults, options); // Create bit flags var FLAG_T = 1, // top FLAG_R = 2, // right FLAG_B = 4, // bottom FLAG_L = 8; // left // Create bit masks var tlMask = FLAG_T | FLAG_L, trMask = FLAG_T | FLAG_R, blMask = FLAG_B | FLAG_L, brMask = FLAG_B | FLAG_R; function slideOverlay(overlay, direction, px, py, ew, eh, ex, ey) { var cornerFlags = 0; // top|right|bottom|left if (py - ey <= eh / 2) cornerFlags ^= FLAG_T; if (px - ex >= ew / 2) cornerFlags ^= FLAG_R; if (py - ey > eh / 2) cornerFlags ^= FLAG_B; if (px - ex < ew / 2) cornerFlags ^= FLAG_L; findSide(cornerFlags, overlay, direction, px-ex, py-ey, ew/2, eh/2); } function findSide(flags, overlay, direction, x, y, w, h) { if (testMask(flags, tlMask)) { testTopLeftToBottomRight(x, y, w, h) ? setOverlayPosition(overlay, direction, 0, -w*2) : setOverlayPosition(overlay, direction, -h*2, 0); } else if (testMask(flags, trMask)) { testBottomRightToTopLeft(x, y, w, h) ? setOverlayPosition(overlay, direction, -h*2, 0) : setOverlayPosition(overlay, direction, 0, w*2); } else if (testMask(flags, blMask)) { testBottomRightToTopLeft(x, y, w, h) ? setOverlayPosition(overlay, direction, 0, -w*2) : setOverlayPosition(overlay, direction, h*2, 0); } else if (testMask(flags, brMask)) { testTopLeftToBottomRight(x, y, w, h) ? setOverlayPosition(overlay, direction, h*2, 0) : setOverlayPosition(overlay, direction, 0, w*2); } } function testMask(flags, mask) { return (flags & mask) === mask; } function testTopLeftToBottomRight(x, y, w, h) { return (h * x - w * y) < 0; } function testBottomRightToTopLeft(x, y, w, h) { return (w * (y-h) + h * x - w * h) < 0; } function setOverlayPosition(overlay, direction, top, left) { if (direction === 'in') { overlay.animate({ top: top, left: left }, 0, function() { overlay.stop().animate({ top: 0, left: 0 }, opts.speed, opts.easing); }); } else if (direction === 'out') { overlay.animate({ top: 0, left: 0 }, 0, function() { overlay.stop().animate({ top: top, left: left }, opts.speed, opts.easing); }); } } this.css({ position: 'relative', overflow: 'hidden' }); this.find('.' + opts.overlay).css({ position: 'absolute', top: '-100%' }); return this.each(function() { var container = $(this); container.hover(function(e) { slideOverlay( container.find('.' + opts.overlay), 'in', e.pageX, e.pageY, container.width(), container.height(), Math.floor(container.offset().left), container.offset().top ); }, function(e) { slideOverlay( container.find('.' + opts.overlay), 'out', e.pageX, e.pageY, container.width(), container.height(), Math.floor(container.offset().left), container.offset().top ); }); }); }; // Plugin default options $.fn.directionalHover.defaults = { overlay: "dh-overlay", easing: "swing", speed: 400 }; }(jQuery)); /* * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ * * Uses the built in easing capabilities added In jQuery 1.1 * to offer multiple easing options * * TERMS OF USE - EASING EQUATIONS * * Open source under the BSD License. * * Copyright © 2001 Robert Penner * All rights reserved. * * TERMS OF USE - jQuery Easing * * Open source under the BSD License. * * Copyright © 2008 George McGinley Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the author nor the names of contributors may be used to endorse * or promote products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(gt;t+=1)n.push(e[t].listener);return n},n.getListenersAsObject=function(e){var t,n=this.getListeners(e);return n instanceof Array&&(t={},t[e]=n),t||n},n.addListener=function(e,n){var i,r=this.getListenersAsObject(e),o="object"==typeof n;for(i in r)r.hasOwnProperty(i)&&-1===t(r[i],n)&&r[i].push(o?n:{listener:n,once:!1});return this},n.on=n.addListener,n.addOnceListener=function(e,t){return this.addListener(e,{listener:t,once:!0})},n.once=n.addOnceListener,n.defineEvent=function(e){return this.getListeners(e),this},n.defineEvents=function(e){for(var t=0;e.length>t;t+=1)this.defineEvent(e[t]);return this},n.removeListener=function(e,n){var i,r,o=this.getListenersAsObject(e);for(r in o)o.hasOwnProperty(r)&&(i=t(o[r],n),-1!==i&&o[r].splice(i,1));return this},n.off=n.removeListener,n.addListeners=function(e,t){return this.manipulateListeners(!1,e,t)},n.removeListeners=function(e,t){return this.manipulateListeners(!0,e,t)},n.manipulateListeners=function(e,t,n){var i,r,o=e?this.removeListener:this.addListener,s=e?this.removeListeners:this.addListeners;if("object"!=typeof t||t instanceof RegExp)for(i=n.length;i--;)o.call(this,t,n[i]);else for(i in t)t.hasOwnProperty(i)&&(r=t[i])&&("function"==typeof r?o.call(this,i,r):s.call(this,i,r));return this},n.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},n.emitEvent=function(e,t){var n,i,r,o,s=this.getListenersAsObject(e);for(r in s)if(s.hasOwnProperty(r))for(i=s[r].length;i--;)n=s[r][i],o=n.listener.apply(this,t||[]),(o===this._getOnceReturnValue()||n.once===!0)&&this.removeListener(e,s[r][i].listener);return this},n.trigger=n.emitEvent,n.emit=function(e){var t=Array.prototype.slice.call(arguments,1);return this.emitEvent(e,t)},n.setOnceReturnValue=function(e){return this._onceReturnValue=e,this},n._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},n._getEvents=function(){return this._events||(this._events={})},"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof module&&module.exports?module.exports=e:this.EventEmitter=e}).call(this),function(e){"use strict";var t=document.documentElement,n=function(){};t.addEventListener?n=function(e,t,n){e.addEventListener(t,n,!1)}:t.attachEvent&&(n=function(t,n,i){t[n+i]=i.handleEvent?function(){var t=e.event;t.target=t.target||t.srcElement,i.handleEvent.call(i,t)}:function(){var n=e.event;n.target=n.target||n.srcElement,i.call(t,n)},t.attachEvent("on"+n,t[n+i])});var i=function(){};t.removeEventListener?i=function(e,t,n){e.removeEventListener(t,n,!1)}:t.detachEvent&&(i=function(e,t,n){e.detachEvent("on"+t,e[t+n]);try{delete e[t+n]}catch(i){e[t+n]=void 0}});var r={bind:n,unbind:i};"function"==typeof define&&define.amd?define(r):e.eventie=r}(this),function(e){"use strict";function t(e,t){for(var n in t)e[n]=t[n];return e}function n(e){return"[object Array]"===c.call(e)}function i(e){var t=[];if(n(e))t=e;else if("number"==typeof e.length)for(var i=0,r=e.length;r>i;i++)t.push(e[i]);else t.push(e);return t}function r(e,n){function r(e,n,s){if(!(this instanceof r))return new r(e,n);"string"==typeof e&&(e=document.querySelectorAll(e)),this.elements=i(e),this.options=t({},this.options),"function"==typeof n?s=n:t(this.options,n),s&&this.on("always",s),this.getImages(),o&&(this.jqDeferred=new o.Deferred);var a=this;setTimeout(function(){a.check()})}function c(e){this.img=e}r.prototype=new e,r.prototype.options={},r.prototype.getImages=function(){this.images=[];for(var e=0,t=this.elements.length;t>e;e++){var n=this.elements[e];"IMG"===n.nodeName&&this.addImage(n);for(var i=n.querySelectorAll("img"),r=0,o=i.length;o>r;r++){var s=i[r];this.addImage(s)}}},r.prototype.addImage=function(e){var t=new c(e);this.images.push(t)},r.prototype.check=function(){function e(e,r){return t.options.debug&&a&&s.log("confirm",e,r),t.progress(e),n++,n===i&&t.complete(),!0}var t=this,n=0,i=this.images.length;if(this.hasAnyBroken=!1,!i)return this.complete(),void 0;for(var r=0;i>r;r++){var o=this.images[r];o.on("confirm",e),o.check()}},r.prototype.progress=function(e){this.hasAnyBroken=this.hasAnyBroken||!e.isLoaded;var t=this;setTimeout(function(){t.emit("progress",t,e),t.jqDeferred&&t.jqDeferred.notify(t,e)})},r.prototype.complete=function(){var e=this.hasAnyBroken?"fail":"done";this.isComplete=!0;var t=this;setTimeout(function(){if(t.emit(e,t),t.emit("always",t),t.jqDeferred){var n=t.hasAnyBroken?"reject":"resolve";t.jqDeferred[n](t)}})},o&&(o.fn.imagesLoaded=function(e,t){var n=new r(this,e,t);return n.jqDeferred.promise(o(this))});var f={};return c.prototype=new e,c.prototype.check=function(){var e=f[this.img.src];if(e)return this.useCached(e),void 0;if(f[this.img.src]=this,this.img.complete&&void 0!==this.img.naturalWidth)return this.confirm(0!==this.img.naturalWidth,"naturalWidth"),void 0;var t=this.proxyImage=new Image;n.bind(t,"load",this),n.bind(t,"error",this),t.src=this.img.src},c.prototype.useCached=function(e){if(e.isConfirmed)this.confirm(e.isLoaded,"cached was confirmed");else{var t=this;e.on("confirm",function(e){return t.confirm(e.isLoaded,"cache emitted confirmed"),!0})}},c.prototype.confirm=function(e,t){this.isConfirmed=!0,this.isLoaded=e,this.emit("confirm",this,t)},c.prototype.handleEvent=function(e){var t="on"+e.type;this[t]&&this[t](e)},c.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindProxyEvents()},c.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindProxyEvents()},c.prototype.unbindProxyEvents=function(){n.unbind(this.proxyImage,"load",this),n.unbind(this.proxyImage,"error",this)},r}var o=e.jQuery,s=e.console,a=s!==void 0,c=Object.prototype.toString;"function"==typeof define&&define.amd?define(["eventEmitter/EventEmitter","eventie/eventie"],r):e.imagesLoaded=r(e.EventEmitter,e.eventie)}(window); // IMPORTANT! // If you're already using Modernizr, delete it from this file. If you don't know what Modernizr is, leave it :) /* Modernizr 2.6.2 (Custom Build) | MIT & BSD * Build: http://modernizr.com/download/#-csstransforms-csstransforms3d-csstransitions-cssclasses-prefixed-teststyles-testprop-testallprops-prefixes-domprefixes */ ;window.Modernizr=function(a,b,c){function z(a){j.cssText=a}function A(a,b){return z(m.join(a+";")+(b||""))}function B(a,b){return typeof a===b}function C(a,b){return!!~(""+a).indexOf(b)}function D(a,b){for(var d in a){var e=a[d];if(!C(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function E(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:B(f,"function")?f.bind(d||b):f}return!1}function F(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+o.join(d+" ")+d).split(" ");return B(b,"string")||B(b,"undefined")?D(e,b):(e=(a+" "+p.join(d+" ")+d).split(" "),E(e,b,c))}var d="2.6.2",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n="Webkit Moz O ms",o=n.split(" "),p=n.toLowerCase().split(" "),q={},r={},s={},t=[],u=t.slice,v,w=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["­",'"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},x={}.hasOwnProperty,y;!B(x,"undefined")&&!B(x.call,"undefined")?y=function(a,b){return x.call(a,b)}:y=function(a,b){return b in a&&B(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=u.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(u.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(u.call(arguments)))};return e}),q.csstransforms=function(){return!!F("transform")},q.csstransforms3d=function(){var a=!!F("perspective");return a&&"webkitPerspective"in g.style&&w("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},q.csstransitions=function(){return F("transition")};for(var G in q)y(q,G)&&(v=G.toLowerCase(),e[v]=q[G](),t.push((e[v]?"":"no-")+v));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)y(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},z(""),i=k=null,e._version=d,e._prefixes=m,e._domPrefixes=p,e._cssomPrefixes=o,e.testProp=function(a){return D([a])},e.testAllProps=F,e.testStyles=w,e.prefixed=function(a,b,c){return b?F(a,b,c):F(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+t.join(" "):""),e}(this,this.document); /*! * Shuffle.js by @Vestride * Categorize, sort, and filter a responsive grid of items. * Dependencies: jQuery 1.9+, Modernizr 2.6.2+ * @license MIT license * @version 3.1.1 */ (function (factory) { if (typeof define === 'function' && define.amd) { define(['jquery', 'modernizr'], factory); } else if (typeof exports === 'object') { module.exports = factory(require('jquery'), window.Modernizr); } else { window.Shuffle = factory(window.jQuery, window.Modernizr); } })(function($, Modernizr, undefined) { 'use strict'; // Validate Modernizr exists. // Shuffle requires `csstransitions`, `csstransforms`, `csstransforms3d`, // and `prefixed` to exist on the Modernizr object. if (typeof Modernizr !== 'object') { throw new Error('Shuffle.js requires Modernizr.\n' + 'http://vestride.github.io/Shuffle/#dependencies'); } /** * Returns css prefixed properties like `-webkit-transition` or `box-sizing` * from `transition` or `boxSizing`, respectively. * @param {(string|boolean)} prop Property to be prefixed. * @return {string} The prefixed css property. */ function dashify( prop ) { if (!prop) { return ''; } // Replace upper case with dash-lowercase, // then fix ms- prefixes because they're not capitalized. return prop.replace(/([A-Z])/g, function( str, m1 ) { return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-'); } // Constant, prefixed variables. var TRANSITION = Modernizr.prefixed('transition'); var TRANSITION_DELAY = Modernizr.prefixed('transitionDelay'); var TRANSITION_DURATION = Modernizr.prefixed('transitionDuration'); // Note(glen): Stock Android 4.1.x browser will fail here because it wrongly // says it supports non-prefixed transitions. // https://github.com/Modernizr/Modernizr/issues/897 var TRANSITIONEND = { 'WebkitTransition' : 'webkitTransitionEnd', 'transition' : 'transitionend' }[ TRANSITION ]; var TRANSFORM = Modernizr.prefixed('transform'); var CSS_TRANSFORM = dashify(TRANSFORM); // Constants var CAN_TRANSITION_TRANSFORMS = Modernizr.csstransforms && Modernizr.csstransitions; var HAS_TRANSFORMS_3D = Modernizr.csstransforms3d; var HAS_COMPUTED_STYLE = !!window.getComputedStyle; var SHUFFLE = 'shuffle'; // Configurable. You can change these constants to fit your application. // The default scale and concealed scale, however, have to be different values. var ALL_ITEMS = 'all'; var FILTER_ATTRIBUTE_KEY = 'groups'; var DEFAULT_SCALE = 1; var CONCEALED_SCALE = 0.001; // Underscore's throttle function. function throttle(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; options = options || {}; var later = function() { previous = options.leading === false ? 0 : $.now(); timeout = null; result = func.apply(context, args); context = args = null; }; return function() { var now = $.now(); if (!previous && options.leading === false) { previous = now; } var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; } function each(obj, iterator, context) { for (var i = 0, length = obj.length; i < length; i++) { if (iterator.call(context, obj[i], i, obj) === {}) { return; } } } function defer(fn, context, wait) { return setTimeout( $.proxy( fn, context ), wait ); } function arrayMax( array ) { return Math.max.apply( Math, array ); } function arrayMin( array ) { return Math.min.apply( Math, array ); } /** * Always returns a numeric value, given a value. * @param {*} value Possibly numeric value. * @return {number} `value` or zero if `value` isn't numeric. * @private */ function getNumber(value) { return $.isNumeric(value) ? value : 0; } var getStyles = window.getComputedStyle || function() {}; /** * Represents a coordinate pair. * @param {number} [x=0] X. * @param {number} [y=0] Y. */ var Point = function(x, y) { this.x = getNumber( x ); this.y = getNumber( y ); }; /** * Whether two points are equal. * @param {Point} a Point A. * @param {Point} b Point B. * @return {boolean} */ Point.equals = function(a, b) { return a.x === b.x && a.y === b.y; }; var COMPUTED_SIZE_INCLUDES_PADDING = (function() { if (!HAS_COMPUTED_STYLE) { return false; } var parent = document.body || document.documentElement; var e = document.createElement('div'); e.style.cssText = 'width:10px;padding:2px;' + '-webkit-box-sizing:border-box;box-sizing:border-box;'; parent.appendChild(e); var width = getStyles(e, null).width; var ret = width === '10px'; parent.removeChild(e); return ret; }()); // Used for unique instance variables var id = 0; var $window = $( window ); /** * Categorize, sort, and filter a responsive grid of items. * * @param {Element} element An element which is the parent container for the grid items. * @param {Object} [options=Shuffle.options] Options object. * @constructor */ var Shuffle = function( element, options ) { options = options || {}; $.extend( this, Shuffle.options, options, Shuffle.settings ); this.$el = $(element); this.element = element; this.unique = 'shuffle_' + id++; this._fire( Shuffle.EventType.LOADING ); this._init(); // Dispatch the done event asynchronously so that people can bind to it after // Shuffle has been initialized. defer(function() { this.initialized = true; this._fire( Shuffle.EventType.DONE ); }, this, 16); }; /** * Events the container element emits with the .shuffle namespace. * For example, "done.shuffle". * @enum {string} */ Shuffle.EventType = { LOADING: 'loading', DONE: 'done', LAYOUT: 'layout', REMOVED: 'removed' }; /** @enum {string} */ Shuffle.ClassName = { BASE: SHUFFLE, SHUFFLE_ITEM: 'shuffle-item', FILTERED: 'filtered', CONCEALED: 'concealed' }; // Overrideable options Shuffle.options = { group: ALL_ITEMS, // Initial filter group. speed: 250, // Transition/animation speed (milliseconds). easing: 'ease-out', // CSS easing function to use. itemSelector: '', // e.g. '.picture-item'. sizer: null, // Sizer element. Use an element to determine the size of columns and gutters. gutterWidth: 0, // A static number or function that tells the plugin how wide the gutters between columns are (in pixels). columnWidth: 0, // A static number or function that returns a number which tells the plugin how wide the columns are (in pixels). delimeter: null, // If your group is not json, and is comma delimeted, you could set delimeter to ','. buffer: 0, // Useful for percentage based heights when they might not always be exactly the same (in pixels). columnThreshold: HAS_COMPUTED_STYLE ? 0.01 : 0.1, // Reading the width of elements isn't precise enough and can cause columns to jump between values. initialSort: null, // Shuffle can be initialized with a sort object. It is the same object given to the sort method. throttle: throttle, // By default, shuffle will throttle resize events. This can be changed or removed. throttleTime: 300, // How often shuffle can be called on resize (in milliseconds). sequentialFadeDelay: 150, // Delay between each item that fades in when adding items. supported: CAN_TRANSITION_TRANSFORMS // Whether to use transforms or absolute positioning. }; // Not overrideable Shuffle.settings = { useSizer: false, itemCss : { // default CSS for each item position: 'absolute', top: 0, left: 0, visibility: 'visible' }, revealAppendedDelay: 300, lastSort: {}, lastFilter: ALL_ITEMS, enabled: true, destroyed: false, initialized: false, _animations: [], _transitions: [], _isMovementCanceled: false, styleQueue: [] }; // Expose for testing. Shuffle.Point = Point; /** * Static methods. */ /** * If the browser has 3d transforms available, build a string with those, * otherwise use 2d transforms. * @param {Point} point X and Y positions. * @param {number} scale Scale amount. * @return {string} A normalized string which can be used with the transform style. * @private */ Shuffle._getItemTransformString = function(point, scale) { if ( HAS_TRANSFORMS_3D ) { return 'translate3d(' + point.x + 'px, ' + point.y + 'px, 0) scale3d(' + scale + ', ' + scale + ', 1)'; } else { return 'translate(' + point.x + 'px, ' + point.y + 'px) scale(' + scale + ')'; } }; /** * Retrieve the computed style for an element, parsed as a float. * @param {Element} element Element to get style for. * @param {string} style Style property. * @param {CSSStyleDeclaration} [styles] Optionally include clean styles to * use instead of asking for them again. * @return {number} The parsed computed value or zero if that fails because IE * will return 'auto' when the element doesn't have margins instead of * the computed style. * @private */ Shuffle._getNumberStyle = function( element, style, styles ) { if ( HAS_COMPUTED_STYLE ) { styles = styles || getStyles( element, null ); var value = Shuffle._getFloat( styles[ style ] ); // Support IE<=11 and W3C spec. if ( !COMPUTED_SIZE_INCLUDES_PADDING && style === 'width' ) { value += Shuffle._getFloat( styles.paddingLeft ) + Shuffle._getFloat( styles.paddingRight ) + Shuffle._getFloat( styles.borderLeftWidth ) + Shuffle._getFloat( styles.borderRightWidth ); } else if ( !COMPUTED_SIZE_INCLUDES_PADDING && style === 'height' ) { value += Shuffle._getFloat( styles.paddingTop ) + Shuffle._getFloat( styles.paddingBottom ) + Shuffle._getFloat( styles.borderTopWidth ) + Shuffle._getFloat( styles.borderBottomWidth ); } return value; } else { return Shuffle._getFloat( $( element ).css( style ) ); } }; /** * Parse a string as an float. * @param {string} value String float. * @return {number} The string as an float or zero. * @private */ Shuffle._getFloat = function(value) { return getNumber( parseFloat( value ) ); }; /** * Returns the outer width of an element, optionally including its margins. * * There are a few different methods for getting the width of an element, none of * which work perfectly for all Shuffle's use cases. * * 1. getBoundingClientRect() `left` and `right` properties. * - Accounts for transform scaled elements, making it useless for Shuffle * elements which have shrunk. * 2. The `offsetWidth` property (or jQuery's CSS). * - This value stays the same regardless of the elements transform property, * however, it does not return subpixel values. * 3. getComputedStyle() * - This works great Chrome, Firefox, Safari, but IE<=11 does not include * padding and border when box-sizing: border-box is set, requiring a feature * test and extra work to add the padding back for IE and other browsers which * follow the W3C spec here. * * @param {Element} element The element. * @param {boolean} [includeMargins] Whether to include margins. Default is false. * @return {number} The width. */ Shuffle._getOuterWidth = function( element, includeMargins ) { // Store the styles so that they can be used by others without asking for it again. var styles = getStyles( element, null ); var width = Shuffle._getNumberStyle( element, 'width', styles ); // Use jQuery here because it uses getComputedStyle internally and is // cross-browser. Using the style property of the element will only work // if there are inline styles. if ( includeMargins ) { var marginLeft = Shuffle._getNumberStyle( element, 'marginLeft', styles ); var marginRight = Shuffle._getNumberStyle( element, 'marginRight', styles ); width += marginLeft + marginRight; } return width; }; /** * Returns the outer height of an element, optionally including its margins. * @param {Element} element The element. * @param {boolean} [includeMargins] Whether to include margins. Default is false. * @return {number} The height. */ Shuffle._getOuterHeight = function( element, includeMargins ) { var styles = getStyles( element, null ); var height = Shuffle._getNumberStyle( element, 'height', styles ); if ( includeMargins ) { var marginTop = Shuffle._getNumberStyle( element, 'marginTop', styles ); var marginBottom = Shuffle._getNumberStyle( element, 'marginBottom', styles ); height += marginTop + marginBottom; } return height; }; /** * Change a property or execute a function which will not have a transition * @param {Element} element DOM element that won't be transitioned * @param {Function} callback A function which will be called while transition * is set to 0ms. * @param {Object} [context] Optional context for the callback function. * @private */ Shuffle._skipTransition = function( element, callback, context ) { var duration = element.style[ TRANSITION_DURATION ]; // Set the duration to zero so it happens immediately element.style[ TRANSITION_DURATION ] = '0ms'; // ms needed for firefox! callback.call( context ); // Force reflow var reflow = element.offsetWidth; // Avoid jshint warnings: unused variables and expressions. reflow = null; // Put the duration back element.style[ TRANSITION_DURATION ] = duration; }; /** * Instance methods. */ Shuffle.prototype._init = function() { this.$items = this._getItems(); this.sizer = this._getElementOption( this.sizer ); if ( this.sizer ) { this.useSizer = true; } // Add class and invalidate styles this.$el.addClass( Shuffle.ClassName.BASE ); // Set initial css for each item this._initItems(); // Bind resize events // http://stackoverflow.com/questions/1852751/window-resize-event-firing-in-internet-explorer $window.on('resize.' + SHUFFLE + '.' + this.unique, this._getResizeFunction()); // Get container css all in one request. Causes reflow var containerCSS = this.$el.css(['position', 'overflow']); var containerWidth = Shuffle._getOuterWidth( this.element ); // Add styles to the container if it doesn't have them. this._validateStyles( containerCSS ); // We already got the container's width above, no need to cause another reflow getting it again... // Calculate the number of columns there will be this._setColumns( containerWidth ); // Kick off! this.shuffle( this.group, this.initialSort ); // The shuffle items haven't had transitions set on them yet // so the user doesn't see the first layout. Set them now that the first layout is done. if ( this.supported ) { defer(function() { this._setTransitions(); this.element.style[ TRANSITION ] = 'height ' + this.speed + 'ms ' + this.easing; }, this); } }; /** * Returns a throttled and proxied function for the resize handler. * @return {Function} * @private */ Shuffle.prototype._getResizeFunction = function() { var resizeFunction = $.proxy( this._onResize, this ); return this.throttle ? this.throttle( resizeFunction, this.throttleTime ) : resizeFunction; }; /** * Retrieve an element from an option. * @param {string|jQuery|Element} option The option to check. * @return {?Element} The plain element or null. * @private */ Shuffle.prototype._getElementOption = function( option ) { // If column width is a string, treat is as a selector and search for the // sizer element within the outermost container if ( typeof option === 'string' ) { return this.$el.find( option )[0] || null; // Check for an element } else if ( option && option.nodeType && option.nodeType === 1 ) { return option; // Check for jQuery object } else if ( option && option.jquery ) { return option[0]; } return null; }; /** * Ensures the shuffle container has the css styles it needs applied to it. * @param {Object} styles Key value pairs for position and overflow. * @private */ Shuffle.prototype._validateStyles = function(styles) { // Position cannot be static. if ( styles.position === 'static' ) { this.element.style.position = 'relative'; } // Overflow has to be hidden if ( styles.overflow !== 'hidden' ) { this.element.style.overflow = 'hidden'; } }; /** * Filter the elements by a category. * @param {string} [category] Category to filter by. If it's given, the last * category will be used to filter the items. * @param {ArrayLike} [$collection] Optionally filter a collection. Defaults to * all the items. * @return {jQuery} Filtered items. * @private */ Shuffle.prototype._filter = function( category, $collection ) { category = category || this.lastFilter; $collection = $collection || this.$items; var set = this._getFilteredSets( category, $collection ); // Individually add/remove concealed/filtered classes this._toggleFilterClasses( set.filtered, set.concealed ); // Save the last filter in case elements are appended. this.lastFilter = category; // This is saved mainly because providing a filter function (like searching) // will overwrite the `lastFilter` property every time its called. if ( typeof category === 'string' ) { this.group = category; } return set.filtered; }; /** * Returns an object containing the filtered and concealed elements. * @param {string|Function} category Category or function to filter by. * @param {ArrayLike.} $items A collection of items to filter. * @return {!{filtered: jQuery, concealed: jQuery}} * @private */ Shuffle.prototype._getFilteredSets = function( category, $items ) { var $filtered = $(); var $concealed = $(); // category === 'all', add filtered class to everything if ( category === ALL_ITEMS ) { $filtered = $items; // Loop through each item and use provided function to determine // whether to hide it or not. } else { each($items, function( el ) { var $item = $(el); if ( this._doesPassFilter( category, $item ) ) { $filtered = $filtered.add( $item ); } else { $concealed = $concealed.add( $item ); } }, this); } return { filtered: $filtered, concealed: $concealed }; }; /** * Test an item to see if it passes a category. * @param {string|Function} category Category or function to filter by. * @param {jQuery} $item A single item, wrapped with jQuery. * @return {boolean} Whether it passes the category/filter. * @private */ Shuffle.prototype._doesPassFilter = function( category, $item ) { if ( $.isFunction( category ) ) { return category.call( $item[0], $item, this ); // Check each element's data-groups attribute against the given category. } else { var groups = $item.data( FILTER_ATTRIBUTE_KEY ); var keys = this.delimeter && !$.isArray( groups ) ? groups.split( this.delimeter ) : groups; return $.inArray(category, keys) > -1; } }; /** * Toggles the filtered and concealed class names. * @param {jQuery} $filtered Filtered set. * @param {jQuery} $concealed Concealed set. * @private */ Shuffle.prototype._toggleFilterClasses = function( $filtered, $concealed ) { $filtered .removeClass( Shuffle.ClassName.CONCEALED ) .addClass( Shuffle.ClassName.FILTERED ); $concealed .removeClass( Shuffle.ClassName.FILTERED ) .addClass( Shuffle.ClassName.CONCEALED ); }; /** * Set the initial css for each item * @param {jQuery} [$items] Optionally specifiy at set to initialize */ Shuffle.prototype._initItems = function( $items ) { $items = $items || this.$items; $items.addClass([ Shuffle.ClassName.SHUFFLE_ITEM, Shuffle.ClassName.FILTERED ].join(' ')); $items.css( this.itemCss ).data('point', new Point()).data('scale', DEFAULT_SCALE); }; /** * Updates the filtered item count. * @private */ Shuffle.prototype._updateItemCount = function() { this.visibleItems = this._getFilteredItems().length; }; /** * Sets css transform transition on a an element. * @param {Element} element Element to set transition on. * @private */ Shuffle.prototype._setTransition = function( element ) { element.style[ TRANSITION ] = CSS_TRANSFORM + ' ' + this.speed + 'ms ' + this.easing + ', opacity ' + this.speed + 'ms ' + this.easing; }; /** * Sets css transform transition on a group of elements. * @param {ArrayLike.} $items Elements to set transitions on. * @private */ Shuffle.prototype._setTransitions = function( $items ) { $items = $items || this.$items; each($items, function( el ) { this._setTransition( el ); }, this); }; /** * Sets a transition delay on a collection of elements, making each delay * greater than the last. * @param {ArrayLike.} $collection Array to iterate over. */ Shuffle.prototype._setSequentialDelay = function( $collection ) { if ( !this.supported ) { return; } // $collection can be an array of dom elements or jquery object each($collection, function( el, i ) { // This works because the transition-property: transform, opacity; el.style[ TRANSITION_DELAY ] = '0ms,' + ((i + 1) * this.sequentialFadeDelay) + 'ms'; }, this); }; Shuffle.prototype._getItems = function() { return this.$el.children( this.itemSelector ); }; Shuffle.prototype._getFilteredItems = function() { return this.$items.filter('.' + Shuffle.ClassName.FILTERED); }; Shuffle.prototype._getConcealedItems = function() { return this.$items.filter('.' + Shuffle.ClassName.CONCEALED); }; /** * Returns the column size, based on column width and sizer options. * @param {number} containerWidth Size of the parent container. * @param {number} gutterSize Size of the gutters. * @return {number} * @private */ Shuffle.prototype._getColumnSize = function( containerWidth, gutterSize ) { var size; // If the columnWidth property is a function, then the grid is fluid if ( $.isFunction( this.columnWidth ) ) { size = this.columnWidth(containerWidth); // columnWidth option isn't a function, are they using a sizing element? } else if ( this.useSizer ) { size = Shuffle._getOuterWidth(this.sizer); // if not, how about the explicitly set option? } else if ( this.columnWidth ) { size = this.columnWidth; // or use the size of the first item } else if ( this.$items.length > 0 ) { size = Shuffle._getOuterWidth(this.$items[0], true); // if there's no items, use size of container } else { size = containerWidth; } // Don't let them set a column width of zero. if ( size === 0 ) { size = containerWidth; } return size + gutterSize; }; /** * Returns the gutter size, based on gutter width and sizer options. * @param {number} containerWidth Size of the parent container. * @return {number} * @private */ Shuffle.prototype._getGutterSize = function( containerWidth ) { var size; if ( $.isFunction( this.gutterWidth ) ) { size = this.gutterWidth(containerWidth); } else if ( this.useSizer ) { size = Shuffle._getNumberStyle(this.sizer, 'marginLeft'); } else { size = this.gutterWidth; } return size; }; /** * Calculate the number of columns to be used. Gets css if using sizer element. * @param {number} [theContainerWidth] Optionally specify a container width if it's already available. */ Shuffle.prototype._setColumns = function( theContainerWidth ) { var containerWidth = theContainerWidth || Shuffle._getOuterWidth( this.element ); var gutter = this._getGutterSize( containerWidth ); var columnWidth = this._getColumnSize( containerWidth, gutter ); var calculatedColumns = (containerWidth + gutter) / columnWidth; // Widths given from getStyles are not precise enough... if ( Math.abs(Math.round(calculatedColumns) - calculatedColumns) < this.columnThreshold ) { // e.g. calculatedColumns = 11.998876 calculatedColumns = Math.round( calculatedColumns ); } this.cols = Math.max( Math.floor(calculatedColumns), 1 ); this.containerWidth = containerWidth; this.colWidth = columnWidth; }; /** * Adjust the height of the grid */ Shuffle.prototype._setContainerSize = function() { this.$el.css( 'height', this._getContainerSize() ); }; /** * Based on the column heights, it returns the biggest one. * @return {number} * @private */ Shuffle.prototype._getContainerSize = function() { return arrayMax( this.positions ); }; /** * Fire events with .shuffle namespace */ Shuffle.prototype._fire = function( name, args ) { this.$el.trigger( name + '.' + SHUFFLE, args && args.length ? args : [ this ] ); }; /** * Zeros out the y columns array, which is used to determine item placement. * @private */ Shuffle.prototype._resetCols = function() { var i = this.cols; this.positions = []; while (i--) { this.positions.push( 0 ); } }; /** * Loops through each item that should be shown and calculates the x, y position. * @param {Array.} items Array of items that will be shown/layed out in order in their array. * Because jQuery collection are always ordered in DOM order, we can't pass a jq collection. * @param {boolean} [isOnlyPosition=false] If true this will position the items with zero opacity. */ Shuffle.prototype._layout = function( items, isOnlyPosition ) { each(items, function( item ) { this._layoutItem( item, !!isOnlyPosition ); }, this); // `_layout` always happens after `_shrink`, so it's safe to process the style // queue here with styles from the shrink method. this._processStyleQueue(); // Adjust the height of the container. this._setContainerSize(); }; /** * Calculates the position of the item and pushes it onto the style queue. * @param {Element} item Element which is being positioned. * @param {boolean} isOnlyPosition Whether to position the item, but with zero * opacity so that it can fade in later. * @private */ Shuffle.prototype._layoutItem = function( item, isOnlyPosition ) { var $item = $(item); var itemData = $item.data(); var currPos = itemData.point; var currScale = itemData.scale; var itemSize = { width: Shuffle._getOuterWidth( item, true ), height: Shuffle._getOuterHeight( item, true ) }; var pos = this._getItemPosition( itemSize ); // If the item will not change its position, do not add it to the render // queue. Transitions don't fire when setting a property to the same value. if ( Point.equals(currPos, pos) && currScale === DEFAULT_SCALE ) { return; } // Save data for shrink itemData.point = pos; itemData.scale = DEFAULT_SCALE; this.styleQueue.push({ $item: $item, point: pos, scale: DEFAULT_SCALE, opacity: isOnlyPosition ? 0 : 1, // Set styles immediately if there is no transition speed. skipTransition: isOnlyPosition || this.speed === 0, callfront: function() { if ( !isOnlyPosition ) { $item.css( 'visibility', 'visible' ); } }, callback: function() { if ( isOnlyPosition ) { $item.css( 'visibility', 'hidden' ); } } }); }; /** * Determine the location of the next item, based on its size. * @param {{width: number, height: number}} itemSize Object with width and height. * @return {Point} * @private */ Shuffle.prototype._getItemPosition = function( itemSize ) { var columnSpan = this._getColumnSpan( itemSize.width, this.colWidth, this.cols ); var setY = this._getColumnSet( columnSpan, this.cols ); // Finds the index of the smallest number in the set. var shortColumnIndex = this._getShortColumn( setY, this.buffer ); // Position the item var point = new Point( Math.round( this.colWidth * shortColumnIndex ), Math.round( setY[shortColumnIndex] )); // Update the columns array with the new values for each column. // e.g. before the update the columns could be [250, 0, 0, 0] for an item // which spans 2 columns. After it would be [250, itemHeight, itemHeight, 0]. var setHeight = setY[shortColumnIndex] + itemSize.height; var setSpan = this.cols + 1 - setY.length; for ( var i = 0; i < setSpan; i++ ) { this.positions[ shortColumnIndex + i ] = setHeight; } return point; }; /** * Determine the number of columns an items spans. * @param {number} itemWidth Width of the item. * @param {number} columnWidth Width of the column (includes gutter). * @param {number} columns Total number of columns * @return {number} * @private */ Shuffle.prototype._getColumnSpan = function( itemWidth, columnWidth, columns ) { var columnSpan = itemWidth / columnWidth; // If the difference between the rounded column span number and the // calculated column span number is really small, round the number to // make it fit. if ( Math.abs(Math.round( columnSpan ) - columnSpan ) < this.columnThreshold ) { // e.g. columnSpan = 4.0089945390298745 columnSpan = Math.round( columnSpan ); } // Ensure the column span is not more than the amount of columns in the whole layout. return Math.min( Math.ceil( columnSpan ), columns ); }; /** * Retrieves the column set to use for placement. * @param {number} columnSpan The number of columns this current item spans. * @param {number} columns The total columns in the grid. * @return {Array.} An array of numbers represeting the column set. * @private */ Shuffle.prototype._getColumnSet = function( columnSpan, columns ) { // The item spans only one column. if ( columnSpan === 1 ) { return this.positions; // The item spans more than one column, figure out how many different // places it could fit horizontally. // The group count is the number of places within the positions this block // could fit, ignoring the current positions of items. // Imagine a 2 column brick as the second item in a 4 column grid with // 10px height each. Find the places it would fit: // [10, 0, 0, 0] // | | | // * * * // // Then take the places which fit and get the bigger of the two: // max([10, 0]), max([0, 0]), max([0, 0]) = [10, 0, 0] // // Next, find the first smallest number (the short column). // [10, 0, 0] // | // * // // And that's where it should be placed! } else { var groupCount = columns + 1 - columnSpan; var groupY = []; // For how many possible positions for this item there are. for ( var i = 0; i < groupCount; i++ ) { // Find the bigger value for each place it could fit. groupY[i] = arrayMax( this.positions.slice( i, i + columnSpan ) ); } return groupY; } }; /** * Find index of short column, the first from the left where this item will go. * * @param {Array.} positions The array to search for the smallest number. * @param {number} buffer Optional buffer which is very useful when the height * is a percentage of the width. * @return {number} Index of the short column. * @private */ Shuffle.prototype._getShortColumn = function( positions, buffer ) { var minPosition = arrayMin( positions ); for (var i = 0, len = positions.length; i < len; i++) { if ( positions[i] >= minPosition - buffer && positions[i] <= minPosition + buffer ) { return i; } } return 0; }; /** * Hides the elements that don't match our filter. * @param {jQuery} $collection jQuery collection to shrink. * @private */ Shuffle.prototype._shrink = function( $collection ) { var $concealed = $collection || this._getConcealedItems(); each($concealed, function( item ) { var $item = $(item); var itemData = $item.data(); // Continuing would add a transitionend event listener to the element, but // that listener would not execute because the transform and opacity would // stay the same. if ( itemData.scale === CONCEALED_SCALE ) { return; } itemData.scale = CONCEALED_SCALE; this.styleQueue.push({ $item: $item, point: itemData.point, scale : CONCEALED_SCALE, opacity: 0, callback: function() { $item.css( 'visibility', 'hidden' ); } }); }, this); }; /** * Resize handler. * @private */ Shuffle.prototype._onResize = function() { // If shuffle is disabled, destroyed, don't do anything if ( !this.enabled || this.destroyed ) { return; } // Will need to check height in the future if it's layed out horizontaly var containerWidth = Shuffle._getOuterWidth( this.element ); // containerWidth hasn't changed, don't do anything if ( containerWidth === this.containerWidth ) { return; } this.update(); }; /** * Returns styles for either jQuery animate or transition. * @param {Object} opts Transition options. * @return {!Object} Transforms for transitions, left/top for animate. * @private */ Shuffle.prototype._getStylesForTransition = function( opts ) { var styles = { opacity: opts.opacity }; if ( this.supported ) { styles[ TRANSFORM ] = Shuffle._getItemTransformString( opts.point, opts.scale ); } else { styles.left = opts.point.x; styles.top = opts.point.y; } return styles; }; /** * Transitions an item in the grid * * @param {Object} opts options. * @param {jQuery} opts.$item jQuery object representing the current item. * @param {Point} opts.point A point object with the x and y coordinates. * @param {number} opts.scale Amount to scale the item. * @param {number} opts.opacity Opacity of the item. * @param {Function} opts.callback Complete function for the animation. * @param {Function} opts.callfront Function to call before transitioning. * @private */ Shuffle.prototype._transition = function( opts ) { var styles = this._getStylesForTransition( opts ); this._startItemAnimation( opts.$item, styles, opts.callfront || $.noop, opts.callback || $.noop ); }; Shuffle.prototype._startItemAnimation = function( $item, styles, callfront, callback ) { var _this = this; // Transition end handler removes its listener. function handleTransitionEnd( evt ) { // Make sure this event handler has not bubbled up from a child. if ( evt.target === evt.currentTarget ) { $( evt.target ).off( TRANSITIONEND, handleTransitionEnd ); _this._removeTransitionReference(reference); callback(); } } var reference = { $element: $item, handler: handleTransitionEnd }; callfront(); // Transitions are not set until shuffle has loaded to avoid the initial transition. if ( !this.initialized ) { $item.css( styles ); callback(); return; } // Use CSS Transforms if we have them if ( this.supported ) { $item.css( styles ); $item.on( TRANSITIONEND, handleTransitionEnd ); this._transitions.push(reference); // Use jQuery to animate left/top } else { // Save the deferred object which jQuery returns. var anim = $item.stop( true ).animate( styles, this.speed, 'swing', callback ); // Push the animation to the list of pending animations. this._animations.push( anim.promise() ); } }; /** * Execute the styles gathered in the style queue. This applies styles to elements, * triggering transitions. * @param {boolean} noLayout Whether to trigger a layout event. * @private */ Shuffle.prototype._processStyleQueue = function( noLayout ) { if ( this.isTransitioning ) { this._cancelMovement(); } var $transitions = $(); // Iterate over the queue and keep track of ones that use transitions. each(this.styleQueue, function( transitionObj ) { if ( transitionObj.skipTransition ) { this._styleImmediately( transitionObj ); } else { $transitions = $transitions.add( transitionObj.$item ); this._transition( transitionObj ); } }, this); if ( $transitions.length > 0 && this.initialized && this.speed > 0 ) { // Set flag that shuffle is currently in motion. this.isTransitioning = true; if ( this.supported ) { this._whenCollectionDone( $transitions, TRANSITIONEND, this._movementFinished ); // The _transition function appends a promise to the animations array. // When they're all complete, do things. } else { this._whenAnimationsDone( this._movementFinished ); } // A call to layout happened, but none of the newly filtered items will // change position. Asynchronously fire the callback here. } else if ( !noLayout ) { defer( this._layoutEnd, this ); } // Remove everything in the style queue this.styleQueue.length = 0; }; Shuffle.prototype._cancelMovement = function() { if (this.supported) { // Remove the transition end event for each listener. each(this._transitions, function( transition ) { transition.$element.off( TRANSITIONEND, transition.handler ); }); } else { // Even when `stop` is called on the jQuery animation, its promise will // still be resolved. Since it cannot be determine from within that callback // whether the animation was stopped or not, a flag is set here to distinguish // between the two states. this._isMovementCanceled = true; this.$items.stop(true); this._isMovementCanceled = false; } // Reset the array. this._transitions.length = 0; // Show it's no longer active. this.isTransitioning = false; }; Shuffle.prototype._removeTransitionReference = function(ref) { var indexInArray = $.inArray(ref, this._transitions); if (indexInArray > -1) { this._transitions.splice(indexInArray, 1); } }; /** * Apply styles without a transition. * @param {Object} opts Transitions options object. * @private */ Shuffle.prototype._styleImmediately = function( opts ) { Shuffle._skipTransition(opts.$item[0], function() { opts.$item.css( this._getStylesForTransition( opts ) ); }, this); }; Shuffle.prototype._movementFinished = function() { this.isTransitioning = false; this._layoutEnd(); }; Shuffle.prototype._layoutEnd = function() { this._fire( Shuffle.EventType.LAYOUT ); }; Shuffle.prototype._addItems = function( $newItems, addToEnd, isSequential ) { // Add classes and set initial positions. this._initItems( $newItems ); // Add transition to each item. this._setTransitions( $newItems ); // Update the list of this.$items = this._getItems(); // Shrink all items (without transitions). this._shrink( $newItems ); each(this.styleQueue, function( transitionObj ) { transitionObj.skipTransition = true; }); // Apply shrink positions, but do not cause a layout event. this._processStyleQueue( true ); if ( addToEnd ) { this._addItemsToEnd( $newItems, isSequential ); } else { this.shuffle( this.lastFilter ); } }; Shuffle.prototype._addItemsToEnd = function( $newItems, isSequential ) { // Get ones that passed the current filter var $passed = this._filter( null, $newItems ); var passed = $passed.get(); // How many filtered elements? this._updateItemCount(); this._layout( passed, true ); if ( isSequential && this.supported ) { this._setSequentialDelay( passed ); } this._revealAppended( passed ); }; /** * Triggers appended elements to fade in. * @param {ArrayLike.} $newFilteredItems Collection of elements. * @private */ Shuffle.prototype._revealAppended = function( newFilteredItems ) { defer(function() { each(newFilteredItems, function( el ) { var $item = $( el ); this._transition({ $item: $item, opacity: 1, point: $item.data('point'), scale: DEFAULT_SCALE }); }, this); this._whenCollectionDone($(newFilteredItems), TRANSITIONEND, function() { $(newFilteredItems).css( TRANSITION_DELAY, '0ms' ); this._movementFinished(); }); }, this, this.revealAppendedDelay); }; /** * Execute a function when an event has been triggered for every item in a collection. * @param {jQuery} $collection Collection of elements. * @param {string} eventName Event to listen for. * @param {Function} callback Callback to execute when they're done. * @private */ Shuffle.prototype._whenCollectionDone = function( $collection, eventName, callback ) { var done = 0; var items = $collection.length; var self = this; function handleEventName( evt ) { if ( evt.target === evt.currentTarget ) { $( evt.target ).off( eventName, handleEventName ); done++; // Execute callback if all items have emitted the correct event. if ( done === items ) { self._removeTransitionReference(reference); callback.call( self ); } } } var reference = { $element: $collection, handler: handleEventName }; // Bind the event to all items. $collection.on( eventName, handleEventName ); // Keep track of transitionend events so they can be removed. this._transitions.push(reference); }; /** * Execute a callback after jQuery `animate` for a collection has finished. * @param {Function} callback Callback to execute when they're done. * @private */ Shuffle.prototype._whenAnimationsDone = function( callback ) { $.when.apply( null, this._animations ).always( $.proxy( function() { this._animations.length = 0; if (!this._isMovementCanceled) { callback.call( this ); } }, this )); }; /** * Public Methods */ /** * The magic. This is what makes the plugin 'shuffle' * @param {string|Function} [category] Category to filter by. Can be a function * @param {Object} [sortObj] A sort object which can sort the filtered set */ Shuffle.prototype.shuffle = function( category, sortObj ) { if ( !this.enabled ) { return; } if ( !category ) { category = ALL_ITEMS; } this._filter( category ); // How many filtered elements? this._updateItemCount(); // Shrink each concealed item this._shrink(); // Update transforms on .filtered elements so they will animate to their new positions this.sort( sortObj ); }; /** * Gets the .filtered elements, sorts them, and passes them to layout. * @param {Object} opts the options object for the sorted plugin */ Shuffle.prototype.sort = function( opts ) { if ( this.enabled ) { this._resetCols(); var sortOptions = opts || this.lastSort; var items = this._getFilteredItems().sorted( sortOptions ); this._layout( items ); this.lastSort = sortOptions; } }; /** * Reposition everything. * @param {boolean} isOnlyLayout If true, column and gutter widths won't be * recalculated. */ Shuffle.prototype.update = function( isOnlyLayout ) { if ( this.enabled ) { if ( !isOnlyLayout ) { // Get updated colCount this._setColumns(); } // Layout items this.sort(); } }; /** * Use this instead of `update()` if you don't need the columns and gutters updated * Maybe an image inside `shuffle` loaded (and now has a height), which means calculations * could be off. */ Shuffle.prototype.layout = function() { this.update( true ); }; /** * New items have been appended to shuffle. Fade them in sequentially * @param {jQuery} $newItems jQuery collection of new items * @param {boolean} [addToEnd=false] If true, new items will be added to the end / bottom * of the items. If not true, items will be mixed in with the current sort order. * @param {boolean} [isSequential=true] If false, new items won't sequentially fade in */ Shuffle.prototype.appended = function( $newItems, addToEnd, isSequential ) { this._addItems( $newItems, addToEnd === true, isSequential !== false ); }; /** * Disables shuffle from updating dimensions and layout on resize */ Shuffle.prototype.disable = function() { this.enabled = false; }; /** * Enables shuffle again * @param {boolean} [isUpdateLayout=true] if undefined, shuffle will update columns and gutters */ Shuffle.prototype.enable = function( isUpdateLayout ) { this.enabled = true; if ( isUpdateLayout !== false ) { this.update(); } }; /** * Remove 1 or more shuffle items * @param {jQuery} $collection A jQuery object containing one or more element in shuffle * @return {Shuffle} The shuffle object */ Shuffle.prototype.remove = function( $collection ) { // If this isn't a jquery object, exit if ( !$collection.length || !$collection.jquery ) { return; } function handleRemoved() { // Remove the collection in the callback $collection.remove(); // Update things now that elements have been removed. this.$items = this._getItems(); this._updateItemCount(); this._fire( Shuffle.EventType.REMOVED, [ $collection, this ] ); // Let it get garbage collected $collection = null; } // Hide collection first. this._toggleFilterClasses( $(), $collection ); this._shrink( $collection ); this.sort(); this.$el.one( Shuffle.EventType.LAYOUT + '.' + SHUFFLE, $.proxy( handleRemoved, this ) ); }; /** * Destroys shuffle, removes events, styles, and classes */ Shuffle.prototype.destroy = function() { // If there is more than one shuffle instance on the page, // removing the resize handler from the window would remove them // all. This is why a unique value is needed. $window.off('.' + this.unique); // Reset container styles this.$el .removeClass( SHUFFLE ) .removeAttr('style') .removeData( SHUFFLE ); // Reset individual item styles this.$items .removeAttr('style') .removeData('point') .removeData('scale') .removeClass([ Shuffle.ClassName.CONCEALED, Shuffle.ClassName.FILTERED, Shuffle.ClassName.SHUFFLE_ITEM ].join(' ')); // Null DOM references this.$items = null; this.$el = null; this.sizer = null; this.element = null; this._transitions = null; // Set a flag so if a debounced resize has been triggered, // it can first check if it is actually destroyed and not doing anything this.destroyed = true; }; // Plugin definition $.fn.shuffle = function( opts ) { var args = Array.prototype.slice.call( arguments, 1 ); return this.each(function() { var $this = $( this ); var shuffle = $this.data( SHUFFLE ); // If we don't have a stored shuffle, make a new one and save it if ( !shuffle ) { shuffle = new Shuffle( this, opts ); $this.data( SHUFFLE, shuffle ); } else if ( typeof opts === 'string' && shuffle[ opts ] ) { shuffle[ opts ].apply( shuffle, args ); } }); }; // http://stackoverflow.com/a/962890/373422 function randomize( array ) { var tmp, current; var top = array.length; if ( !top ) { return array; } while ( --top ) { current = Math.floor( Math.random() * (top + 1) ); tmp = array[ current ]; array[ current ] = array[ top ]; array[ top ] = tmp; } return array; } // You can return `undefined` from the `by` function to revert to DOM order // This plugin does NOT return a jQuery object. It returns a plain array because // jQuery sorts everything in DOM order. $.fn.sorted = function(options) { var opts = $.extend({}, $.fn.sorted.defaults, options); var arr = this.get(); var revert = false; if ( !arr.length ) { return []; } if ( opts.randomize ) { return randomize( arr ); } // Sort the elements by the opts.by function. // If we don't have opts.by, default to DOM order if ( $.isFunction( opts.by ) ) { arr.sort(function(a, b) { // Exit early if we already know we want to revert if ( revert ) { return 0; } var valA = opts.by($(a)); var valB = opts.by($(b)); // If both values are undefined, use the DOM order if ( valA === undefined && valB === undefined ) { revert = true; return 0; } if ( valA < valB || valA === 'sortFirst' || valB === 'sortLast' ) { return -1; } if ( valA > valB || valA === 'sortLast' || valB === 'sortFirst' ) { return 1; } return 0; }); } // Revert to the original array if necessary if ( revert ) { return this.get(); } if ( opts.reverse ) { arr.reverse(); } return arr; }; $.fn.sorted.defaults = { reverse: false, // Use array.reverse() to reverse the results by: null, // Sorting function randomize: false // If true, this will skip the sorting and return a randomized order in the array }; return Shuffle; }); /*! Waypoints - 3.0.0 Copyright © 2011-2014 Caleb Troughton Licensed under the MIT license. https://github.com/imakewebthings/waypoints/blog/master/licenses.txt */ !function(){"use strict";function t(o){if(!o)throw new Error("No options passed to Waypoint constructor");if(!o.element)throw new Error("No element option passed to Waypoint constructor");if(!o.handler)throw new Error("No handler option passed to Waypoint constructor");this.key="waypoint-"+e,this.options=t.Adapter.extend({},t.defaults,o),this.element=this.options.element,this.adapter=new t.Adapter(this.element),this.callback=o.handler,this.axis=this.options.horizontal?"horizontal":"vertical",this.enabled=this.options.enabled,this.triggerPoint=null,this.group=t.Group.findOrCreate({name:this.options.group,axis:this.axis}),this.context=t.Context.findOrCreateByElement(this.options.context),t.offsetAliases[this.options.offset]&&(this.options.offset=t.offsetAliases[this.options.offset]),this.group.add(this),this.context.add(this),i[this.key]=this,e+=1}var e=0,i={};t.prototype.queueTrigger=function(t){this.group.queueTrigger(this,t)},t.prototype.trigger=function(t){this.enabled&&this.callback&&this.callback.apply(this,t)},t.prototype.destroy=function(){this.context.remove(this),this.group.remove(this),delete i[this.key]},t.prototype.disable=function(){return this.enabled=!1,this},t.prototype.enable=function(){return this.context.refresh(),this.enabled=!0,this},t.prototype.next=function(){return this.group.next(this)},t.prototype.previous=function(){return this.group.previous(this)},t.destroyAll=function(){var t=[];for(var e in i)t.push(i[e]);for(var o=0,r=t.length;r>o;o++)t[o].destroy()},t.refreshAll=function(){t.Context.refreshAll()},t.viewportHeight=function(){return window.innerHeight||document.documentElement.clientHeight},t.viewportWidth=function(){return document.documentElement.clientWidth},t.adapters=[],t.defaults={context:window,continuous:!0,enabled:!0,group:"default",horizontal:!1,offset:0},t.offsetAliases={"bottom-in-view":function(){return this.context.innerHeight()-this.adapter.outerHeight()},"right-in-view":function(){return this.context.innerWidth()-this.adapter.outerWidth()}},window.Waypoint=t}(),function(){"use strict";function t(t){window.setTimeout(t,1e3/60)}function e(t){this.element=t,this.Adapter=r.Adapter,this.adapter=new this.Adapter(t),this.key="waypoint-context-"+i,this.didScroll=!1,this.didResize=!1,this.oldScroll={x:this.adapter.scrollLeft(),y:this.adapter.scrollTop()},this.waypoints={vertical:{},horizontal:{}},t.waypointContextKey=this.key,o[t.waypointContextKey]=this,i+=1,this.createThrottledScrollHandler(),this.createThrottledResizeHandler()}var i=0,o={},r=window.Waypoint,n=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||t,s=window.onload;e.prototype.add=function(t){var e=t.options.horizontal?"horizontal":"vertical";this.waypoints[e][t.key]=t,this.refresh()},e.prototype.checkEmpty=function(){var t=this.Adapter.isEmptyObject(this.waypoints.horizontal),e=this.Adapter.isEmptyObject(this.waypoints.vertical);t&&e&&(this.adapter.off(".waypoints"),delete o[this.key])},e.prototype.createThrottledResizeHandler=function(){function t(){e.handleResize(),e.didResize=!1}var e=this;this.adapter.on("resize.waypoints",function(){e.didResize||(e.didResize=!0,n(t))})},e.prototype.createThrottledScrollHandler=function(){function t(){e.handleScroll(),e.didScroll=!1}var e=this;this.adapter.on("scroll.waypoints",function(){(!e.didScroll||r.isTouch)&&(e.didScroll=!0,n(t))})},e.prototype.handleResize=function(){r.Context.refreshAll()},e.prototype.handleScroll=function(){var t={},e={horizontal:{newScroll:this.adapter.scrollLeft(),oldScroll:this.oldScroll.x,forward:"right",backward:"left"},vertical:{newScroll:this.adapter.scrollTop(),oldScroll:this.oldScroll.y,forward:"down",backward:"up"}};for(var i in e){var o=e[i],r=o.newScroll>o.oldScroll,n=r?o.forward:o.backward;for(var s in this.waypoints[i]){var a=this.waypoints[i][s],l=o.oldScroll=a.triggerPoint,p=l&&h,u=!l&&!h;(p||u)&&(a.queueTrigger(n),t[a.group.id]=a.group)}}for(var c in t)t[c].flushTriggers();this.oldScroll={x:e.horizontal.newScroll,y:e.vertical.newScroll}},e.prototype.innerHeight=function(){return this.element===this.element.window?r.viewportHeight():this.adapter.innerHeight()},e.prototype.remove=function(t){delete this.waypoints[t.axis][t.key],this.checkEmpty()},e.prototype.innerWidth=function(){return this.element===this.element.window?r.viewportWidth():this.adapter.innerWidth()},e.prototype.destroy=function(){var t=[];for(var e in this.waypoints)for(var i in this.waypoints[e])t.push(this.waypoints[e][i]);for(var o=0,r=t.length;r>o;o++)t[o].destroy()},e.prototype.refresh=function(){var t,e=this.element===this.element.window,i=this.adapter.offset(),o={};this.handleScroll(),t={horizontal:{contextOffset:e?0:i.left,contextScroll:e?0:this.oldScroll.x,contextDimension:this.innerWidth(),oldScroll:this.oldScroll.x,forward:"right",backward:"left",offsetProp:"left"},vertical:{contextOffset:e?0:i.top,contextScroll:e?0:this.oldScroll.y,contextDimension:this.innerHeight(),oldScroll:this.oldScroll.y,forward:"down",backward:"up",offsetProp:"top"}};for(var r in t){var n=t[r];for(var s in this.waypoints[r]){var a,l,h,p,u,c=this.waypoints[r][s],d=c.options.offset,f=c.triggerPoint,w=0,y=null==f;c.element!==c.element.window&&(w=c.adapter.offset()[n.offsetProp]),"function"==typeof d?d=d.apply(c):"string"==typeof d&&(d=parseFloat(d),c.options.offset.indexOf("%")>-1&&(d=Math.ceil(n.contextDimension*d/100))),a=n.contextScroll-n.contextOffset,c.triggerPoint=w+a-d,l=f=n.oldScroll,p=l&&h,u=!l&&!h,!y&&p?(c.queueTrigger(n.backward),o[c.group.id]=c.group):!y&&u?(c.queueTrigger(n.forward),o[c.group.id]=c.group):y&&n.oldScroll>=c.triggerPoint&&(c.queueTrigger(n.forward),o[c.group.id]=c.group)}}for(var g in o)o[g].flushTriggers();return this},e.findOrCreateByElement=function(t){return e.findByElement(t)||new e(t)},e.refreshAll=function(){for(var t in o)o[t].refresh()},e.findByElement=function(t){return o[t.waypointContextKey]},window.onload=function(){s&&s(),e.refreshAll()},r.Context=e}(),function(){"use strict";function t(t,e){return t.triggerPoint-e.triggerPoint}function e(t,e){return e.triggerPoint-t.triggerPoint}function i(t){this.name=t.name,this.axis=t.axis,this.id=this.name+"-"+this.axis,this.waypoints=[],this.clearTriggerQueues(),o[this.axis][this.name]=this}var o={vertical:{},horizontal:{}},r=window.Waypoint;i.prototype.add=function(t){this.waypoints.push(t)},i.prototype.clearTriggerQueues=function(){this.triggerQueues={up:[],down:[],left:[],right:[]}},i.prototype.flushTriggers=function(){for(var i in this.triggerQueues){var o=this.triggerQueues[i],r="up"===i||"left"===i;o.sort(r?e:t);for(var n=0,s=o.length;s>n;n+=1){var a=o[n];(a.options.continuous||n===o.length-1)&&a.trigger([i])}}this.clearTriggerQueues()},i.prototype.next=function(e){this.waypoints.sort(t);var i=r.Adapter.inArray(e,this.waypoints),o=i===this.waypoints.length-1;return o?null:this.waypoints[i+1]},i.prototype.previous=function(e){this.waypoints.sort(t);var i=r.Adapter.inArray(e,this.waypoints);return i?this.waypoints[i-1]:null},i.prototype.queueTrigger=function(t,e){this.triggerQueues[e].push(t)},i.prototype.remove=function(t){var e=r.Adapter.inArray(t,this.waypoints);e>-1&&this.waypoints.splice(e,1)},i.prototype.first=function(){return this.waypoints[0]},i.prototype.last=function(){return this.waypoints[this.waypoints.length-1]},i.findOrCreate=function(t){return o[t.axis][t.name]||new i(t)},r.Group=i}(),function(){"use strict";function t(t){this.$element=e(t)}var e=window.jQuery,i=window.Waypoint;e.each(["innerHeight","innerWidth","off","offset","on","outerHeight","outerWidth","scrollLeft","scrollTop"],function(e,i){t.prototype[i]=function(){var t=Array.prototype.slice.call(arguments);return this.$element[i].apply(this.$element,t)}}),e.each(["extend","inArray","isEmptyObject"],function(i,o){t[o]=e[o]}),i.adapters.push({name:"jquery",Adapter:t}),i.Adapter=t}(),function(){"use strict";function t(t){return function(){var i=[],o=arguments[0];return t.isFunction(arguments[0])&&(o=t.extend({},arguments[1]),o.handler=arguments[0]),this.each(function(){var r=t.extend({},o,{element:this});"string"==typeof r.context&&(r.context=t(this).closest(r.context)[0]),i.push(new e(r))}),i}}var e=window.Waypoint;window.jQuery&&(window.jQuery.fn.waypoint=t(window.jQuery)),window.Zepto&&(window.Zepto.fn.waypoint=t(window.Zepto))}(); /* Modernizr 2.8.3 (Custom Build) | MIT & BSD * Build: http://modernizr.com/download/#-fontface-opacity-cssanimations-shiv-mq-cssclasses-teststyles-testprop-testallprops-prefixes-domprefixes-load */ ;window.Modernizr=function(a,b,c){function A(a){j.cssText=a}function B(a,b){return A(m.join(a+";")+(b||""))}function C(a,b){return typeof a===b}function D(a,b){return!!~(""+a).indexOf(b)}function E(a,b){for(var d in a){var e=a[d];if(!D(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function F(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:C(f,"function")?f.bind(d||b):f}return!1}function G(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+o.join(d+" ")+d).split(" ");return C(b,"string")||C(b,"undefined")?E(e,b):(e=(a+" "+p.join(d+" ")+d).split(" "),F(e,b,c))}var d="2.8.3",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n="Webkit Moz O ms",o=n.split(" "),p=n.toLowerCase().split(" "),q={},r={},s={},t=[],u=t.slice,v,w=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["­",'"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},x=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b)&&c(b).matches||!1;var d;return w("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},y={}.hasOwnProperty,z;!C(y,"undefined")&&!C(y.call,"undefined")?z=function(a,b){return y.call(a,b)}:z=function(a,b){return b in a&&C(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=u.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(u.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(u.call(arguments)))};return e}),q.opacity=function(){return B("opacity:.55"),/^0.55$/.test(j.opacity)},q.cssanimations=function(){return G("animationName")},q.fontface=function(){var a;return w('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a};for(var H in q)z(q,H)&&(v=H.toLowerCase(),e[v]=q[H](),t.push((e[v]?"":"no-")+v));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)z(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},A(""),i=k=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,e._prefixes=m,e._domPrefixes=p,e._cssomPrefixes=o,e.mq=x,e.testProp=function(a){return E([a])},e.testAllProps=G,e.testStyles=w,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+t.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f 2)) { throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3') } }(jQuery); /* ======================================================================== * Bootstrap: transition.js v3.3.6 * http://getbootstrap.com/javascript/#transitions * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) // ============================================================ function transitionEnd() { var el = document.createElement('bootstrap') var transEndEventNames = { WebkitTransition : 'webkitTransitionEnd', MozTransition : 'transitionend', OTransition : 'oTransitionEnd otransitionend', transition : 'transitionend' } for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return { end: transEndEventNames[name] } } } return false // explicit for ie8 ( ._.) } // http://blog.alexmaccaw.com/css-transitions $.fn.emulateTransitionEnd = function (duration) { var called = false var $el = this $(this).one('bsTransitionEnd', function () { called = true }) var callback = function () { if (!called) $($el).trigger($.support.transition.end) } setTimeout(callback, duration) return this } $(function () { $.support.transition = transitionEnd() if (!$.support.transition) return $.event.special.bsTransitionEnd = { bindType: $.support.transition.end, delegateType: $.support.transition.end, handle: function (e) { if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) } } }) }(jQuery); /* ======================================================================== * Bootstrap: alert.js v3.3.6 * http://getbootstrap.com/javascript/#alerts * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // ALERT CLASS DEFINITION // ====================== var dismiss = '[data-dismiss="alert"]' var Alert = function (el) { $(el).on('click', dismiss, this.close) } Alert.VERSION = '3.3.6' Alert.TRANSITION_DURATION = 150 Alert.prototype.close = function (e) { var $this = $(this) var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = $(selector) if (e) e.preventDefault() if (!$parent.length) { $parent = $this.closest('.alert') } $parent.trigger(e = $.Event('close.bs.alert')) if (e.isDefaultPrevented()) return $parent.removeClass('in') function removeElement() { // detach from parent, fire event then clean up data $parent.detach().trigger('closed.bs.alert').remove() } $.support.transition && $parent.hasClass('fade') ? $parent .one('bsTransitionEnd', removeElement) .emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement() } // ALERT PLUGIN DEFINITION // ======================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.alert') if (!data) $this.data('bs.alert', (data = new Alert(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.alert $.fn.alert = Plugin $.fn.alert.Constructor = Alert // ALERT NO CONFLICT // ================= $.fn.alert.noConflict = function () { $.fn.alert = old return this } // ALERT DATA-API // ============== $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) }(jQuery); /* ======================================================================== * Bootstrap: button.js v3.3.6 * http://getbootstrap.com/javascript/#buttons * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // BUTTON PUBLIC CLASS DEFINITION // ============================== var Button = function (element, options) { this.$element = $(element) this.options = $.extend({}, Button.DEFAULTS, options) this.isLoading = false } Button.VERSION = '3.3.6' Button.DEFAULTS = { loadingText: 'loading...' } Button.prototype.setState = function (state) { var d = 'disabled' var $el = this.$element var val = $el.is('input') ? 'val' : 'html' var data = $el.data() state += 'Text' if (data.resetText == null) $el.data('resetText', $el[val]()) // push to event loop to allow forms to submit setTimeout($.proxy(function () { $el[val](data[state] == null ? this.options[state] : data[state]) if (state == 'loadingText') { this.isLoading = true $el.addClass(d).attr(d, d) } else if (this.isLoading) { this.isLoading = false $el.removeClass(d).removeAttr(d) } }, this), 0) } Button.prototype.toggle = function () { var changed = true var $parent = this.$element.closest('[data-toggle="buttons"]') if ($parent.length) { var $input = this.$element.find('input') if ($input.prop('type') == 'radio') { if ($input.prop('checked')) changed = false $parent.find('.active').removeClass('active') this.$element.addClass('active') } else if ($input.prop('type') == 'checkbox') { if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false this.$element.toggleClass('active') } $input.prop('checked', this.$element.hasClass('active')) if (changed) $input.trigger('change') } else { this.$element.attr('aria-pressed', !this.$element.hasClass('active')) this.$element.toggleClass('active') } } // BUTTON PLUGIN DEFINITION // ======================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.button') var options = typeof option == 'object' && option if (!data) $this.data('bs.button', (data = new Button(this, options))) if (option == 'toggle') data.toggle() else if (option) data.setState(option) }) } var old = $.fn.button $.fn.button = Plugin $.fn.button.Constructor = Button // BUTTON NO CONFLICT // ================== $.fn.button.noConflict = function () { $.fn.button = old return this } // BUTTON DATA-API // =============== $(document) .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { var $btn = $(e.target) if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') Plugin.call($btn, 'toggle') if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault() }) .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) }) }(jQuery); /* ======================================================================== * Bootstrap: carousel.js v3.3.6 * http://getbootstrap.com/javascript/#carousel * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CAROUSEL CLASS DEFINITION // ========================= var Carousel = function (element, options) { this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.paused = null this.sliding = null this.interval = null this.$active = null this.$items = null this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) } Carousel.VERSION = '3.3.6' Carousel.TRANSITION_DURATION = 600 Carousel.DEFAULTS = { interval: 5000, pause: 'hover', wrap: true, keyboard: true } Carousel.prototype.keydown = function (e) { if (/input|textarea/i.test(e.target.tagName)) return switch (e.which) { case 37: this.prev(); break case 39: this.next(); break default: return } e.preventDefault() } Carousel.prototype.cycle = function (e) { e || (this.paused = false) this.interval && clearInterval(this.interval) this.options.interval && !this.paused && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) return this } Carousel.prototype.getItemIndex = function (item) { this.$items = item.parent().children('.item') return this.$items.index(item || this.$active) } Carousel.prototype.getItemForDirection = function (direction, active) { var activeIndex = this.getItemIndex(active) var willWrap = (direction == 'prev' && activeIndex === 0) || (direction == 'next' && activeIndex == (this.$items.length - 1)) if (willWrap && !this.options.wrap) return active var delta = direction == 'prev' ? -1 : 1 var itemIndex = (activeIndex + delta) % this.$items.length return this.$items.eq(itemIndex) } Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) if (pos > (this.$items.length - 1) || pos < 0) return if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" if (activeIndex == pos) return this.pause().cycle() return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) } Carousel.prototype.pause = function (e) { e || (this.paused = true) if (this.$element.find('.next, .prev').length && $.support.transition) { this.$element.trigger($.support.transition.end) this.cycle(true) } this.interval = clearInterval(this.interval) return this } Carousel.prototype.next = function () { if (this.sliding) return return this.slide('next') } Carousel.prototype.prev = function () { if (this.sliding) return return this.slide('prev') } Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') var $next = next || this.getItemForDirection(type, $active) var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var that = this if ($next.hasClass('active')) return (this.sliding = false) var relatedTarget = $next[0] var slideEvent = $.Event('slide.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) this.$element.trigger(slideEvent) if (slideEvent.isDefaultPrevented()) return this.sliding = true isCycling && this.pause() if (this.$indicators.length) { this.$indicators.find('.active').removeClass('active') var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) $nextIndicator && $nextIndicator.addClass('active') } var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" if ($.support.transition && this.$element.hasClass('slide')) { $next.addClass(type) $next[0].offsetWidth // force reflow $active.addClass(direction) $next.addClass(direction) $active .one('bsTransitionEnd', function () { $next.removeClass([type, direction].join(' ')).addClass('active') $active.removeClass(['active', direction].join(' ')) that.sliding = false setTimeout(function () { that.$element.trigger(slidEvent) }, 0) }) .emulateTransitionEnd(Carousel.TRANSITION_DURATION) } else { $active.removeClass('active') $next.addClass('active') this.sliding = false this.$element.trigger(slidEvent) } isCycling && this.cycle() return this } // CAROUSEL PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.carousel') var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) var action = typeof option == 'string' ? option : options.slide if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) if (typeof option == 'number') data.to(option) else if (action) data[action]() else if (options.interval) data.pause().cycle() }) } var old = $.fn.carousel $.fn.carousel = Plugin $.fn.carousel.Constructor = Carousel // CAROUSEL NO CONFLICT // ==================== $.fn.carousel.noConflict = function () { $.fn.carousel = old return this } // CAROUSEL DATA-API // ================= var clickHandler = function (e) { var href var $this = $(this) var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 if (!$target.hasClass('carousel')) return var options = $.extend({}, $target.data(), $this.data()) var slideIndex = $this.attr('data-slide-to') if (slideIndex) options.interval = false Plugin.call($target, options) if (slideIndex) { $target.data('bs.carousel').to(slideIndex) } e.preventDefault() } $(document) .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) $(window).on('load', function () { $('[data-ride="carousel"]').each(function () { var $carousel = $(this) Plugin.call($carousel, $carousel.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: collapse.js v3.3.6 * http://getbootstrap.com/javascript/#collapse * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // COLLAPSE PUBLIC CLASS DEFINITION // ================================ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + '[data-toggle="collapse"][data-target="#' + element.id + '"]') this.transitioning = null if (this.options.parent) { this.$parent = this.getParent() } else { this.addAriaAndCollapsedClass(this.$element, this.$trigger) } if (this.options.toggle) this.toggle() } Collapse.VERSION = '3.3.6' Collapse.TRANSITION_DURATION = 350 Collapse.DEFAULTS = { toggle: true } Collapse.prototype.dimension = function () { var hasWidth = this.$element.hasClass('width') return hasWidth ? 'width' : 'height' } Collapse.prototype.show = function () { if (this.transitioning || this.$element.hasClass('in')) return var activesData var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') if (actives && actives.length) { activesData = actives.data('bs.collapse') if (activesData && activesData.transitioning) return } var startEvent = $.Event('show.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return if (actives && actives.length) { Plugin.call(actives, 'hide') activesData || actives.data('bs.collapse', null) } var dimension = this.dimension() this.$element .removeClass('collapse') .addClass('collapsing')[dimension](0) .attr('aria-expanded', true) this.$trigger .removeClass('collapsed') .attr('aria-expanded', true) this.transitioning = 1 var complete = function () { this.$element .removeClass('collapsing') .addClass('collapse in')[dimension]('') this.transitioning = 0 this.$element .trigger('shown.bs.collapse') } if (!$.support.transition) return complete.call(this) var scrollSize = $.camelCase(['scroll', dimension].join('-')) this.$element .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { if (this.transitioning || !this.$element.hasClass('in')) return var startEvent = $.Event('hide.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return var dimension = this.dimension() this.$element[dimension](this.$element[dimension]())[0].offsetHeight this.$element .addClass('collapsing') .removeClass('collapse in') .attr('aria-expanded', false) this.$trigger .addClass('collapsed') .attr('aria-expanded', false) this.transitioning = 1 var complete = function () { this.transitioning = 0 this.$element .removeClass('collapsing') .addClass('collapse') .trigger('hidden.bs.collapse') } if (!$.support.transition) return complete.call(this) this.$element [dimension](0) .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION) } Collapse.prototype.toggle = function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() } Collapse.prototype.getParent = function () { return $(this.options.parent) .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') .each($.proxy(function (i, element) { var $element = $(element) this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) }, this)) .end() } Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { var isOpen = $element.hasClass('in') $element.attr('aria-expanded', isOpen) $trigger .toggleClass('collapsed', !isOpen) .attr('aria-expanded', isOpen) } function getTargetFromTrigger($trigger) { var href var target = $trigger.attr('data-target') || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 return $(target) } // COLLAPSE PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.collapse') var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.collapse $.fn.collapse = Plugin $.fn.collapse.Constructor = Collapse // COLLAPSE NO CONFLICT // ==================== $.fn.collapse.noConflict = function () { $.fn.collapse = old return this } // COLLAPSE DATA-API // ================= $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { var $this = $(this) if (!$this.attr('data-target')) e.preventDefault() var $target = getTargetFromTrigger($this) var data = $target.data('bs.collapse') var option = data ? 'toggle' : $this.data() Plugin.call($target, option) }) }(jQuery); /* ======================================================================== * Bootstrap: dropdown.js v3.3.6 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // DROPDOWN CLASS DEFINITION // ========================= var backdrop = '.dropdown-backdrop' var toggle = '[data-toggle="dropdown"]' var Dropdown = function (element) { $(element).on('click.bs.dropdown', this.toggle) } Dropdown.VERSION = '3.3.6' function getParent($this) { var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = selector && $(selector) return $parent && $parent.length ? $parent : $this.parent() } function clearMenus(e) { if (e && e.which === 3) return $(backdrop).remove() $(toggle).each(function () { var $this = $(this) var $parent = getParent($this) var relatedTarget = { relatedTarget: this } if (!$parent.hasClass('open')) return if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this.attr('aria-expanded', 'false') $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) }) } Dropdown.prototype.toggle = function (e) { var $this = $(this) if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') clearMenus() if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we use a backdrop because click events don't delegate $(document.createElement('div')) .addClass('dropdown-backdrop') .insertAfter($(this)) .on('click', clearMenus) } var relatedTarget = { relatedTarget: this } $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this .trigger('focus') .attr('aria-expanded', 'true') $parent .toggleClass('open') .trigger($.Event('shown.bs.dropdown', relatedTarget)) } return false } Dropdown.prototype.keydown = function (e) { if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return var $this = $(this) e.preventDefault() e.stopPropagation() if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') if (!isActive && e.which != 27 || isActive && e.which == 27) { if (e.which == 27) $parent.find(toggle).trigger('focus') return $this.trigger('click') } var desc = ' li:not(.disabled):visible a' var $items = $parent.find('.dropdown-menu' + desc) if (!$items.length) return var index = $items.index(e.target) if (e.which == 38 && index > 0) index-- // up if (e.which == 40 && index < $items.length - 1) index++ // down if (!~index) index = 0 $items.eq(index).trigger('focus') } // DROPDOWN PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.dropdown') if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.dropdown $.fn.dropdown = Plugin $.fn.dropdown.Constructor = Dropdown // DROPDOWN NO CONFLICT // ==================== $.fn.dropdown.noConflict = function () { $.fn.dropdown = old return this } // APPLY TO STANDARD DROPDOWN ELEMENTS // =================================== $(document) .on('click.bs.dropdown.data-api', clearMenus) .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) }(jQuery); /* ======================================================================== * Bootstrap: modal.js v3.3.6 * http://getbootstrap.com/javascript/#modals * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // MODAL CLASS DEFINITION // ====================== var Modal = function (element, options) { this.options = options this.$body = $(document.body) this.$element = $(element) this.$dialog = this.$element.find('.modal-dialog') this.$backdrop = null this.isShown = null this.originalBodyPad = null this.scrollbarWidth = 0 this.ignoreBackdropClick = false if (this.options.remote) { this.$element .find('.modal-content') .load(this.options.remote, $.proxy(function () { this.$element.trigger('loaded.bs.modal') }, this)) } } Modal.VERSION = '3.3.6' Modal.TRANSITION_DURATION = 300 Modal.BACKDROP_TRANSITION_DURATION = 150 Modal.DEFAULTS = { backdrop: true, keyboard: true, show: true } Modal.prototype.toggle = function (_relatedTarget) { return this.isShown ? this.hide() : this.show(_relatedTarget) } Modal.prototype.show = function (_relatedTarget) { var that = this var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) this.$element.trigger(e) if (this.isShown || e.isDefaultPrevented()) return this.isShown = true this.checkScrollbar() this.setScrollbar() this.$body.addClass('modal-open') this.escape() this.resize() this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) this.$dialog.on('mousedown.dismiss.bs.modal', function () { that.$element.one('mouseup.dismiss.bs.modal', function (e) { if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true }) }) this.backdrop(function () { var transition = $.support.transition && that.$element.hasClass('fade') if (!that.$element.parent().length) { that.$element.appendTo(that.$body) // don't move modals dom position } that.$element .show() .scrollTop(0) that.adjustDialog() if (transition) { that.$element[0].offsetWidth // force reflow } that.$element.addClass('in') that.enforceFocus() var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) transition ? that.$dialog // wait for modal to slide in .one('bsTransitionEnd', function () { that.$element.trigger('focus').trigger(e) }) .emulateTransitionEnd(Modal.TRANSITION_DURATION) : that.$element.trigger('focus').trigger(e) }) } Modal.prototype.hide = function (e) { if (e) e.preventDefault() e = $.Event('hide.bs.modal') this.$element.trigger(e) if (!this.isShown || e.isDefaultPrevented()) return this.isShown = false this.escape() this.resize() $(document).off('focusin.bs.modal') this.$element .removeClass('in') .off('click.dismiss.bs.modal') .off('mouseup.dismiss.bs.modal') this.$dialog.off('mousedown.dismiss.bs.modal') $.support.transition && this.$element.hasClass('fade') ? this.$element .one('bsTransitionEnd', $.proxy(this.hideModal, this)) .emulateTransitionEnd(Modal.TRANSITION_DURATION) : this.hideModal() } Modal.prototype.enforceFocus = function () { $(document) .off('focusin.bs.modal') // guard against infinite focus loop .on('focusin.bs.modal', $.proxy(function (e) { if (this.$element[0] !== e.target && !this.$element.has(e.target).length) { this.$element.trigger('focus') } }, this)) } Modal.prototype.escape = function () { if (this.isShown && this.options.keyboard) { this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { e.which == 27 && this.hide() }, this)) } else if (!this.isShown) { this.$element.off('keydown.dismiss.bs.modal') } } Modal.prototype.resize = function () { if (this.isShown) { $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) } else { $(window).off('resize.bs.modal') } } Modal.prototype.hideModal = function () { var that = this this.$element.hide() this.backdrop(function () { that.$body.removeClass('modal-open') that.resetAdjustments() that.resetScrollbar() that.$element.trigger('hidden.bs.modal') }) } Modal.prototype.removeBackdrop = function () { this.$backdrop && this.$backdrop.remove() this.$backdrop = null } Modal.prototype.backdrop = function (callback) { var that = this var animate = this.$element.hasClass('fade') ? 'fade' : '' if (this.isShown && this.options.backdrop) { var doAnimate = $.support.transition && animate this.$backdrop = $(document.createElement('div')) .addClass('modal-backdrop ' + animate) .appendTo(this.$body) this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { if (this.ignoreBackdropClick) { this.ignoreBackdropClick = false return } if (e.target !== e.currentTarget) return this.options.backdrop == 'static' ? this.$element[0].focus() : this.hide() }, this)) if (doAnimate) this.$backdrop[0].offsetWidth // force reflow this.$backdrop.addClass('in') if (!callback) return doAnimate ? this.$backdrop .one('bsTransitionEnd', callback) .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callback() } else if (!this.isShown && this.$backdrop) { this.$backdrop.removeClass('in') var callbackRemove = function () { that.removeBackdrop() callback && callback() } $.support.transition && this.$element.hasClass('fade') ? this.$backdrop .one('bsTransitionEnd', callbackRemove) .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callbackRemove() } else if (callback) { callback() } } // these following methods are used to handle overflowing modals Modal.prototype.handleUpdate = function () { this.adjustDialog() } Modal.prototype.adjustDialog = function () { var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight this.$element.css({ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' }) } Modal.prototype.resetAdjustments = function () { this.$element.css({ paddingLeft: '', paddingRight: '' }) } Modal.prototype.checkScrollbar = function () { var fullWindowWidth = window.innerWidth if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 var documentElementRect = document.documentElement.getBoundingClientRect() fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) } this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth this.scrollbarWidth = this.measureScrollbar() } Modal.prototype.setScrollbar = function () { var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) this.originalBodyPad = document.body.style.paddingRight || '' if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) } Modal.prototype.resetScrollbar = function () { this.$body.css('padding-right', this.originalBodyPad) } Modal.prototype.measureScrollbar = function () { // thx walsh var scrollDiv = document.createElement('div') scrollDiv.className = 'modal-scrollbar-measure' this.$body.append(scrollDiv) var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth this.$body[0].removeChild(scrollDiv) return scrollbarWidth } // MODAL PLUGIN DEFINITION // ======================= function Plugin(option, _relatedTarget) { return this.each(function () { var $this = $(this) var data = $this.data('bs.modal') var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data) $this.data('bs.modal', (data = new Modal(this, options))) if (typeof option == 'string') data[option](_relatedTarget) else if (options.show) data.show(_relatedTarget) }) } var old = $.fn.modal $.fn.modal = Plugin $.fn.modal.Constructor = Modal // MODAL NO CONFLICT // ================= $.fn.modal.noConflict = function () { $.fn.modal = old return this } // MODAL DATA-API // ============== $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { var $this = $(this) var href = $this.attr('href') var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) if ($this.is('a')) e.preventDefault() $target.one('show.bs.modal', function (showEvent) { if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown $target.one('hidden.bs.modal', function () { $this.is(':visible') && $this.trigger('focus') }) }) Plugin.call($target, option, this) }) }(jQuery); /* ======================================================================== * Bootstrap: tooltip.js v3.3.6 * http://getbootstrap.com/javascript/#tooltip * Inspired by the original jQuery.tipsy by Jason Frame * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // TOOLTIP PUBLIC CLASS DEFINITION // =============================== var Tooltip = function (element, options) { this.type = null this.options = null this.enabled = null this.timeout = null this.hoverState = null this.$element = null this.inState = null this.init('tooltip', element, options) } Tooltip.VERSION = '3.3.6' Tooltip.TRANSITION_DURATION = 150 Tooltip.DEFAULTS = { animation: true, placement: 'top', selector: false, template: '', trigger: 'hover focus', title: '', delay: 0, html: false, container: false, viewport: { selector: 'body', padding: 0 } } Tooltip.prototype.init = function (type, element, options) { this.enabled = true this.type = type this.$element = $(element) this.options = this.getOptions(options) this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) this.inState = { click: false, hover: false, focus: false } if (this.$element[0] instanceof document.constructor && !this.options.selector) { throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') } var triggers = this.options.trigger.split(' ') for (var i = triggers.length; i--;) { var trigger = triggers[i] if (trigger == 'click') { this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) } else if (trigger != 'manual') { var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) } } this.options.selector ? (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : this.fixTitle() } Tooltip.prototype.getDefaults = function () { return Tooltip.DEFAULTS } Tooltip.prototype.getOptions = function (options) { options = $.extend({}, this.getDefaults(), this.$element.data(), options) if (options.delay && typeof options.delay == 'number') { options.delay = { show: options.delay, hide: options.delay } } return options } Tooltip.prototype.getDelegateOptions = function () { var options = {} var defaults = this.getDefaults() this._options && $.each(this._options, function (key, value) { if (defaults[key] != value) options[key] = value }) return options } Tooltip.prototype.enter = function (obj) { var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) $(obj.currentTarget).data('bs.' + this.type, self) } if (obj instanceof $.Event) { self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true } if (self.tip().hasClass('in') || self.hoverState == 'in') { self.hoverState = 'in' return } clearTimeout(self.timeout) self.hoverState = 'in' if (!self.options.delay || !self.options.delay.show) return self.show() self.timeout = setTimeout(function () { if (self.hoverState == 'in') self.show() }, self.options.delay.show) } Tooltip.prototype.isInStateTrue = function () { for (var key in this.inState) { if (this.inState[key]) return true } return false } Tooltip.prototype.leave = function (obj) { var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) $(obj.currentTarget).data('bs.' + this.type, self) } if (obj instanceof $.Event) { self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false } if (self.isInStateTrue()) return clearTimeout(self.timeout) self.hoverState = 'out' if (!self.options.delay || !self.options.delay.hide) return self.hide() self.timeout = setTimeout(function () { if (self.hoverState == 'out') self.hide() }, self.options.delay.hide) } Tooltip.prototype.show = function () { var e = $.Event('show.bs.' + this.type) if (this.hasContent() && this.enabled) { this.$element.trigger(e) var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) if (e.isDefaultPrevented() || !inDom) return var that = this var $tip = this.tip() var tipId = this.getUID(this.type) this.setContent() $tip.attr('id', tipId) this.$element.attr('aria-describedby', tipId) if (this.options.animation) $tip.addClass('fade') var placement = typeof this.options.placement == 'function' ? this.options.placement.call(this, $tip[0], this.$element[0]) : this.options.placement var autoToken = /\s?auto?\s?/i var autoPlace = autoToken.test(placement) if (autoPlace) placement = placement.replace(autoToken, '') || 'top' $tip .detach() .css({ top: 0, left: 0, display: 'block' }) .addClass(placement) .data('bs.' + this.type, this) this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) this.$element.trigger('inserted.bs.' + this.type) var pos = this.getPosition() var actualWidth = $tip[0].offsetWidth var actualHeight = $tip[0].offsetHeight if (autoPlace) { var orgPlacement = placement var viewportDim = this.getPosition(this.$viewport) placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : placement $tip .removeClass(orgPlacement) .addClass(placement) } var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) this.applyPlacement(calculatedOffset, placement) var complete = function () { var prevHoverState = that.hoverState that.$element.trigger('shown.bs.' + that.type) that.hoverState = null if (prevHoverState == 'out') that.leave(that) } $.support.transition && this.$tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() } } Tooltip.prototype.applyPlacement = function (offset, placement) { var $tip = this.tip() var width = $tip[0].offsetWidth var height = $tip[0].offsetHeight // manually read margins because getBoundingClientRect includes difference var marginTop = parseInt($tip.css('margin-top'), 10) var marginLeft = parseInt($tip.css('margin-left'), 10) // we must check for NaN for ie 8/9 if (isNaN(marginTop)) marginTop = 0 if (isNaN(marginLeft)) marginLeft = 0 offset.top += marginTop offset.left += marginLeft // $.fn.offset doesn't round pixel values // so we use setOffset directly with our own function B-0 $.offset.setOffset($tip[0], $.extend({ using: function (props) { $tip.css({ top: Math.round(props.top), left: Math.round(props.left) }) } }, offset), 0) $tip.addClass('in') // check to see if placing tip in new offset caused the tip to resize itself var actualWidth = $tip[0].offsetWidth var actualHeight = $tip[0].offsetHeight if (placement == 'top' && actualHeight != height) { offset.top = offset.top + height - actualHeight } var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) if (delta.left) offset.left += delta.left else offset.top += delta.top var isVertical = /top|bottom/.test(placement) var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' $tip.offset(offset) this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) } Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { this.arrow() .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') .css(isVertical ? 'top' : 'left', '') } Tooltip.prototype.setContent = function () { var $tip = this.tip() var title = this.getTitle() $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) $tip.removeClass('fade in top bottom left right') } Tooltip.prototype.hide = function (callback) { var that = this var $tip = $(this.$tip) var e = $.Event('hide.bs.' + this.type) function complete() { if (that.hoverState != 'in') $tip.detach() that.$element .removeAttr('aria-describedby') .trigger('hidden.bs.' + that.type) callback && callback() } this.$element.trigger(e) if (e.isDefaultPrevented()) return $tip.removeClass('in') $.support.transition && $tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() this.hoverState = null return this } Tooltip.prototype.fixTitle = function () { var $e = this.$element if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') } } Tooltip.prototype.hasContent = function () { return this.getTitle() } Tooltip.prototype.getPosition = function ($element) { $element = $element || this.$element var el = $element[0] var isBody = el.tagName == 'BODY' var elRect = el.getBoundingClientRect() if (elRect.width == null) { // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) } var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null return $.extend({}, elRect, scroll, outerDims, elOffset) } Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } } Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { var delta = { top: 0, left: 0 } if (!this.$viewport) return delta var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 var viewportDimensions = this.getPosition(this.$viewport) if (/right|left/.test(placement)) { var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight if (topEdgeOffset < viewportDimensions.top) { // top overflow delta.top = viewportDimensions.top - topEdgeOffset } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset } } else { var leftEdgeOffset = pos.left - viewportPadding var rightEdgeOffset = pos.left + viewportPadding + actualWidth if (leftEdgeOffset < viewportDimensions.left) { // left overflow delta.left = viewportDimensions.left - leftEdgeOffset } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset } } return delta } Tooltip.prototype.getTitle = function () { var title var $e = this.$element var o = this.options title = $e.attr('data-original-title') || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) return title } Tooltip.prototype.getUID = function (prefix) { do prefix += ~~(Math.random() * 1000000) while (document.getElementById(prefix)) return prefix } Tooltip.prototype.tip = function () { if (!this.$tip) { this.$tip = $(this.options.template) if (this.$tip.length != 1) { throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') } } return this.$tip } Tooltip.prototype.arrow = function () { return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) } Tooltip.prototype.enable = function () { this.enabled = true } Tooltip.prototype.disable = function () { this.enabled = false } Tooltip.prototype.toggleEnabled = function () { this.enabled = !this.enabled } Tooltip.prototype.toggle = function (e) { var self = this if (e) { self = $(e.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(e.currentTarget, this.getDelegateOptions()) $(e.currentTarget).data('bs.' + this.type, self) } } if (e) { self.inState.click = !self.inState.click if (self.isInStateTrue()) self.enter(self) else self.leave(self) } else { self.tip().hasClass('in') ? self.leave(self) : self.enter(self) } } Tooltip.prototype.destroy = function () { var that = this clearTimeout(this.timeout) this.hide(function () { that.$element.off('.' + that.type).removeData('bs.' + that.type) if (that.$tip) { that.$tip.detach() } that.$tip = null that.$arrow = null that.$viewport = null }) } // TOOLTIP PLUGIN DEFINITION // ========================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.tooltip') var options = typeof option == 'object' && option if (!data && /destroy|hide/.test(option)) return if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.tooltip $.fn.tooltip = Plugin $.fn.tooltip.Constructor = Tooltip // TOOLTIP NO CONFLICT // =================== $.fn.tooltip.noConflict = function () { $.fn.tooltip = old return this } }(jQuery); /* ======================================================================== * Bootstrap: popover.js v3.3.6 * http://getbootstrap.com/javascript/#popovers * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // POPOVER PUBLIC CLASS DEFINITION // =============================== var Popover = function (element, options) { this.init('popover', element, options) } if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') Popover.VERSION = '3.3.6' Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { placement: 'right', trigger: 'click', content: '', template: '' }) // NOTE: POPOVER EXTENDS tooltip.js // ================================ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) Popover.prototype.constructor = Popover Popover.prototype.getDefaults = function () { return Popover.DEFAULTS } Popover.prototype.setContent = function () { var $tip = this.tip() var title = this.getTitle() var content = this.getContent() $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' ](content) $tip.removeClass('fade top bottom left right in') // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do // this manually by checking the contents. if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() } Popover.prototype.hasContent = function () { return this.getTitle() || this.getContent() } Popover.prototype.getContent = function () { var $e = this.$element var o = this.options return $e.attr('data-content') || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) } Popover.prototype.arrow = function () { return (this.$arrow = this.$arrow || this.tip().find('.arrow')) } // POPOVER PLUGIN DEFINITION // ========================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.popover') var options = typeof option == 'object' && option if (!data && /destroy|hide/.test(option)) return if (!data) $this.data('bs.popover', (data = new Popover(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.popover $.fn.popover = Plugin $.fn.popover.Constructor = Popover // POPOVER NO CONFLICT // =================== $.fn.popover.noConflict = function () { $.fn.popover = old return this } }(jQuery); /* ======================================================================== * Bootstrap: scrollspy.js v3.3.6 * http://getbootstrap.com/javascript/#scrollspy * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // SCROLLSPY CLASS DEFINITION // ========================== function ScrollSpy(element, options) { this.$body = $(document.body) this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) this.options = $.extend({}, ScrollSpy.DEFAULTS, options) this.selector = (this.options.target || '') + ' .nav li > a' this.offsets = [] this.targets = [] this.activeTarget = null this.scrollHeight = 0 this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) this.refresh() this.process() } ScrollSpy.VERSION = '3.3.6' ScrollSpy.DEFAULTS = { offset: 10 } ScrollSpy.prototype.getScrollHeight = function () { return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) } ScrollSpy.prototype.refresh = function () { var that = this var offsetMethod = 'offset' var offsetBase = 0 this.offsets = [] this.targets = [] this.scrollHeight = this.getScrollHeight() if (!$.isWindow(this.$scrollElement[0])) { offsetMethod = 'position' offsetBase = this.$scrollElement.scrollTop() } this.$body .find(this.selector) .map(function () { var $el = $(this) var href = $el.data('target') || $el.attr('href') var $href = /^#./.test(href) && $(href) return ($href && $href.length && $href.is(':visible') && [[$href[offsetMethod]().top + offsetBase, href]]) || null }) .sort(function (a, b) { return a[0] - b[0] }) .each(function () { that.offsets.push(this[0]) that.targets.push(this[1]) }) } ScrollSpy.prototype.process = function () { var scrollTop = this.$scrollElement.scrollTop() + this.options.offset var scrollHeight = this.getScrollHeight() var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() var offsets = this.offsets var targets = this.targets var activeTarget = this.activeTarget var i if (this.scrollHeight != scrollHeight) { this.refresh() } if (scrollTop >= maxScroll) { return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) } if (activeTarget && scrollTop < offsets[0]) { this.activeTarget = null return this.clear() } for (i = offsets.length; i--;) { activeTarget != targets[i] && scrollTop >= offsets[i] && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) && this.activate(targets[i]) } } ScrollSpy.prototype.activate = function (target) { this.activeTarget = target this.clear() var selector = this.selector + '[data-target="' + target + '"],' + this.selector + '[href="' + target + '"]' var active = $(selector) .parents('li') .addClass('active') if (active.parent('.dropdown-menu').length) { active = active .closest('li.dropdown') .addClass('active') } active.trigger('activate.bs.scrollspy') } ScrollSpy.prototype.clear = function () { $(this.selector) .parentsUntil(this.options.target, '.active') .removeClass('active') } // SCROLLSPY PLUGIN DEFINITION // =========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.scrollspy') var options = typeof option == 'object' && option if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.scrollspy $.fn.scrollspy = Plugin $.fn.scrollspy.Constructor = ScrollSpy // SCROLLSPY NO CONFLICT // ===================== $.fn.scrollspy.noConflict = function () { $.fn.scrollspy = old return this } // SCROLLSPY DATA-API // ================== $(window).on('load.bs.scrollspy.data-api', function () { $('[data-spy="scroll"]').each(function () { var $spy = $(this) Plugin.call($spy, $spy.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: tab.js v3.3.6 * http://getbootstrap.com/javascript/#tabs * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // TAB CLASS DEFINITION // ==================== var Tab = function (element) { // jscs:disable requireDollarBeforejQueryAssignment this.element = $(element) // jscs:enable requireDollarBeforejQueryAssignment } Tab.VERSION = '3.3.6' Tab.TRANSITION_DURATION = 150 Tab.prototype.show = function () { var $this = this.element var $ul = $this.closest('ul:not(.dropdown-menu)') var selector = $this.data('target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } if ($this.parent('li').hasClass('active')) return var $previous = $ul.find('.active:last a') var hideEvent = $.Event('hide.bs.tab', { relatedTarget: $this[0] }) var showEvent = $.Event('show.bs.tab', { relatedTarget: $previous[0] }) $previous.trigger(hideEvent) $this.trigger(showEvent) if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return var $target = $(selector) this.activate($this.closest('li'), $ul) this.activate($target, $target.parent(), function () { $previous.trigger({ type: 'hidden.bs.tab', relatedTarget: $this[0] }) $this.trigger({ type: 'shown.bs.tab', relatedTarget: $previous[0] }) }) } Tab.prototype.activate = function (element, container, callback) { var $active = container.find('> .active') var transition = callback && $.support.transition && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) function next() { $active .removeClass('active') .find('> .dropdown-menu > .active') .removeClass('active') .end() .find('[data-toggle="tab"]') .attr('aria-expanded', false) element .addClass('active') .find('[data-toggle="tab"]') .attr('aria-expanded', true) if (transition) { element[0].offsetWidth // reflow for transition element.addClass('in') } else { element.removeClass('fade') } if (element.parent('.dropdown-menu').length) { element .closest('li.dropdown') .addClass('active') .end() .find('[data-toggle="tab"]') .attr('aria-expanded', true) } callback && callback() } $active.length && transition ? $active .one('bsTransitionEnd', next) .emulateTransitionEnd(Tab.TRANSITION_DURATION) : next() $active.removeClass('in') } // TAB PLUGIN DEFINITION // ===================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.tab') if (!data) $this.data('bs.tab', (data = new Tab(this))) if (typeof option == 'string') data[option]() }) } var old = $.fn.tab $.fn.tab = Plugin $.fn.tab.Constructor = Tab // TAB NO CONFLICT // =============== $.fn.tab.noConflict = function () { $.fn.tab = old return this } // TAB DATA-API // ============ var clickHandler = function (e) { e.preventDefault() Plugin.call($(this), 'show') } $(document) .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) }(jQuery); /* ======================================================================== * Bootstrap: affix.js v3.3.6 * http://getbootstrap.com/javascript/#affix * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // AFFIX CLASS DEFINITION // ====================== var Affix = function (element, options) { this.options = $.extend({}, Affix.DEFAULTS, options) this.$target = $(this.options.target) .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) this.$element = $(element) this.affixed = null this.unpin = null this.pinnedOffset = null this.checkPosition() } Affix.VERSION = '3.3.6' Affix.RESET = 'affix affix-top affix-bottom' Affix.DEFAULTS = { offset: 0, target: window } Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { var scrollTop = this.$target.scrollTop() var position = this.$element.offset() var targetHeight = this.$target.height() if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false if (this.affixed == 'bottom') { if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' } var initializing = this.affixed == null var colliderTop = initializing ? scrollTop : position.top var colliderHeight = initializing ? targetHeight : height if (offsetTop != null && scrollTop <= offsetTop) return 'top' if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' return false } Affix.prototype.getPinnedOffset = function () { if (this.pinnedOffset) return this.pinnedOffset this.$element.removeClass(Affix.RESET).addClass('affix') var scrollTop = this.$target.scrollTop() var position = this.$element.offset() return (this.pinnedOffset = position.top - scrollTop) } Affix.prototype.checkPositionWithEventLoop = function () { setTimeout($.proxy(this.checkPosition, this), 1) } Affix.prototype.checkPosition = function () { if (!this.$element.is(':visible')) return var height = this.$element.height() var offset = this.options.offset var offsetTop = offset.top var offsetBottom = offset.bottom var scrollHeight = Math.max($(document).height(), $(document.body).height()) if (typeof offset != 'object') offsetBottom = offsetTop = offset if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) if (this.affixed != affix) { if (this.unpin != null) this.$element.css('top', '') var affixType = 'affix' + (affix ? '-' + affix : '') var e = $.Event(affixType + '.bs.affix') this.$element.trigger(e) if (e.isDefaultPrevented()) return this.affixed = affix this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null this.$element .removeClass(Affix.RESET) .addClass(affixType) .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') } if (affix == 'bottom') { this.$element.offset({ top: scrollHeight - height - offsetBottom }) } } // AFFIX PLUGIN DEFINITION // ======================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.affix') var options = typeof option == 'object' && option if (!data) $this.data('bs.affix', (data = new Affix(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.affix $.fn.affix = Plugin $.fn.affix.Constructor = Affix // AFFIX NO CONFLICT // ================= $.fn.affix.noConflict = function () { $.fn.affix = old return this } // AFFIX DATA-API // ============== $(window).on('load', function () { $('[data-spy="affix"]').each(function () { var $spy = $(this) var data = $spy.data() data.offset = data.offset || {} if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom if (data.offsetTop != null) data.offset.top = data.offsetTop Plugin.call($spy, data) }) }) }(jQuery);