diff --git a/ajax/libs/fluidbox/1.4.1/css/fluidbox.css b/ajax/libs/fluidbox/1.4.1/css/fluidbox.css
new file mode 100644
index 000000000..093f69e66
--- /dev/null
+++ b/ajax/libs/fluidbox/1.4.1/css/fluidbox.css
@@ -0,0 +1,44 @@
+.fluidbox {
+ outline: none;
+}
+.hidden {
+ display: none;
+}
+.fluidbox-overlay {
+ background-color: rgba(255,255,255,.85);
+ cursor: pointer;
+ cursor: -webkit-zoom-out;
+ cursor: -moz-zoom-out;
+ opacity: 0;
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ transition: all .25s ease-in-out;
+}
+.fluidbox-wrap {
+ background-position: center center;
+ background-size: cover;
+ margin: 0 auto;
+ position: relative;
+ transition: all .25s ease-in-out;
+}
+.fluidbox-ghost {
+ background-size: 100% 100%;
+ background-position: center center;
+ position: absolute;
+ transition: all .25s ease-in-out;
+}
+ .fluidbox-closed .fluidbox-ghost {
+ -webkit-transition-property: top, left, opacity, -webkit-transform;
+ -moz-transition-property: top, left, opacity, -moz-transform;
+ -o-transition-property: top, left, opacity, -o-transform;
+ transition-property: top, left, opacity, transform;
+ transition-delay: 0, 0, .25s, 0;
+ }
+ .fluidbox-closed .fluidbox-wrap img {
+ transition-property: opacity;
+ transition-delay: .25s;
+ transition-duration: 0s;
+ }
\ No newline at end of file
diff --git a/ajax/libs/fluidbox/1.4.1/jquery.fluidbox.js b/ajax/libs/fluidbox/1.4.1/jquery.fluidbox.js
new file mode 100644
index 000000000..5fd1f8f65
--- /dev/null
+++ b/ajax/libs/fluidbox/1.4.1/jquery.fluidbox.js
@@ -0,0 +1,424 @@
+// Fluidbox
+// Description: Replicating the seamless lightbox transition effect seen on Medium.com, with some improvements
+// Version: 1.4.1
+// Author: Terry Mun
+// Author URI: http://terrymun.com
+
+// -------------------------------------------------------- //
+// Dependency: Paul Irish's jQuery debounced resize event //
+// -------------------------------------------------------- //
+(function($,sr){
+
+ // debouncing function from John Hann
+ // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
+ var debounce = function (func, threshold, execAsap) {
+ var timeout;
+
+ return function debounced () {
+ var obj = this, args = arguments;
+ function delayed () {
+ if (!execAsap)
+ func.apply(obj, args);
+ timeout = null;
+ };
+
+ if (timeout)
+ clearTimeout(timeout);
+ else if (execAsap)
+ func.apply(obj, args);
+
+ timeout = setTimeout(delayed, threshold || 100);
+ };
+ }
+ // smartresize
+ jQuery.fn[sr] = function(fn){ return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
+
+})(jQuery,'smartresize');
+
+
+// ---------------------------------------------------------------------------------------------------------------------- //
+// Dependency: David Walsh (http://davidwalsh.name/css-animation-callback) //
+// and //
+// Jonathan Suh (https://jonsuh.com/blog/detect-the-end-of-css-animations-and-transitions-with-javascript/) //
+// ---------------------------------------------------------------------------------------------------------------------- //
+function whichTransitionEvent() {
+ var t,
+ el = document.createElement("fakeelement");
+
+ var transitions = {
+ "transition" : "transitionend",
+ "OTransition" : "oTransitionEnd",
+ "MozTransition" : "transitionend",
+ "WebkitTransition": "webkitTransitionEnd"
+ }
+
+ for (t in transitions){
+ if (el.style[t] !== undefined){
+ return transitions[t];
+ }
+ }
+}
+var customTransitionEnd = whichTransitionEvent();
+
+// -----------------------------
+// Fluidbox plugin starts here
+// -----------------------------
+(function ($) {
+
+ $.fn.fluidbox = function (opts) {
+
+ // Default settings
+ var settings = $.extend(true, {
+ viewportFill: 0.95,
+ debounceResize: true,
+ stackIndex: 1000,
+ stackIndexDelta: 10,
+ closeTrigger: [
+ {
+ selector: '.fluidbox-overlay',
+ event: 'click'
+ },
+ {
+ selector: 'document',
+ event: 'keyup',
+ keyCode: 27
+ }
+ ]
+ }, opts);
+
+ // Ensure that the stackIndex does not become negative
+ if(settings.stackIndex < settings.stackIndexDelta) settings.stackIndexDelta = settings.stackIndex;
+
+ // Dynamically create overlay
+ $fbOverlay = $('
', {
+ 'class': 'fluidbox-overlay',
+ css: {
+ 'z-index': settings.stackIndex
+ }
+ });
+
+ // Declare variables
+ var $fb = this,
+ $w = $(window), // Shorthand for $(window)
+ vpRatio,
+
+ // Function:
+ // 1. funcCloseFb() - used to close any instance of opened Fluidbox
+ // 2. funcPositionFb() - used for dynamic positioning of any instance of opened Fluidbox
+ // 3. funcCalcAll() - used to run funcCalc() for every instance of targered Fluidbox thumbnail
+ // 4. funcCalc() - used to store dimensions of image, ghost element and wrapper element upon initialization or resize
+ // 5. fbClickhandler() - universal click handler for all Fluidbox items
+ funcCloseFb = function () {
+ $('.fluidbox-opened').trigger('click');
+ },
+ funcPositionFb = function ($activeFb, customEvent) {
+ // Get shorthand for more objects
+ var $img = $activeFb.find('img'),
+ $ghost = $activeFb.find('.fluidbox-ghost'),
+ $wrap = $activeFb.find('.fluidbox-wrap'),
+ $data = $activeFb.data(),
+ fHeight = 0,
+ fWidth = 0;
+ $img.data().imgRatio = $data.natWidth/ $data.natHeight;
+
+ var newHeight, missingRatioNormal, missingRatio;
+
+ // Check natural dimensions
+ if(vpRatio > $img.data().imgRatio) {
+ if($data.natHeight < $w.height()*settings.viewportFill) {
+ fHeight = $data.natHeight;
+ } else {
+ fHeight = $w.height()*settings.viewportFill;
+ }
+ $data.imgScale = fHeight/$img.height();
+ $data.imgScaleY = $data.imgScale;
+
+ newHeight = $img.height() * $data.imgScaleY;
+ missingRatioNormal = newHeight / $data.natHeight;
+ missingRatio = $data.natWidth * missingRatioNormal / $img.width();
+
+ $data.imgScaleX = missingRatio;
+ } else {
+ if($data.natWidth < $w.width()*settings.viewportFill) {
+ fWidth = $data.natWidth;
+ } else {
+ fWidth = $w.width()*settings.viewportFill;
+ }
+ $data.imgScale = fWidth/$img.width();
+ $data.imgScaleX = $data.imgScale;
+
+ var newWidth = $img.width() * $data.imgScaleX;
+ var missingRatioNormal = newWidth / $data.natWidth;
+ var missingRatio = $data.natHeight * missingRatioNormal / $img.height();
+
+ $data.imgScaleY = missingRatio;
+ }
+
+ // Calculation goes here
+ var offsetY = $w.scrollTop()-$img.offset().top+0.5*($img.data('imgHeight')*($img.data('imgScale')-1))+0.5*($w.height()-$img.data('imgHeight')*$img.data('imgScale')),
+ offsetX = 0.5*($img.data('imgWidth')*($img.data('imgScale')-1))+0.5*($w.width()-$img.data('imgWidth')*$img.data('imgScale'))-$img.offset().left,
+ scale = parseInt($data.imgScaleX * 1000) / 1000 + ',' + parseInt($data.imgScaleY * 1000) / 1000;
+
+ // Apply CSS transforms to ghost element
+ // For offsetX and Y, we round to one decimal place
+ // For scale, we round to three decimal places
+ $ghost.css({
+ 'transform': 'translate('+parseInt(offsetX*10)/10+'px,'+parseInt(offsetY*10)/10+'px) scale('+ scale +')',
+ top: $img.offset().top - $wrap.offset().top,
+ left: $img.offset().left - $wrap.offset().left
+ }).one(customTransitionEnd, function() {
+ $activeFb.trigger(customEvent);
+ });
+ },
+ funcCalcAll = function() {
+ $fb.each(function () {
+ funcCalc($(this));
+ });
+ },
+ funcCalc = function ($fbItem) {
+ // Get viewport ratio
+ vpRatio = $w.width() / $w.height();
+
+ // Get image dimensions and aspect ratio
+ if($fbItem.hasClass('fluidbox')) {
+ var $img = $fbItem.find('img'),
+ $ghost = $fbItem.find('.fluidbox-ghost'),
+ $wrap = $fbItem.find('.fluidbox-wrap'),
+ data = $img.data();
+
+ function imageProp() {
+ // Store image dimensions in jQuery object
+ data.imgWidth = $img.width();
+ data.imgHeight = $img.height();
+ data.imgRatio = $img.width()/$img.height();
+
+ // Resize and position ghost element
+ $ghost.css({
+ width: $img.width(),
+ height: $img.height(),
+ top: $img.offset().top - $wrap.offset().top + parseInt($img.css('borderTopWidth')) + parseInt($img.css('paddingTop')),
+ left: $img.offset().left - $wrap.offset().left + parseInt($img.css('borderLeftWidth')) + parseInt($img.css('paddingLeft'))
+ });
+
+ // Calculate scale based on orientation
+ if(vpRatio > data.imgRatio) {
+ data.imgScale = $w.height()*settings.viewportFill/$img.height();
+ } else {
+ data.imgScale = $w.width()*settings.viewportFill/$img.width();
+ }
+ }
+
+ imageProp();
+
+ // Rerun everything on imageload, to overcome issue in Firefox
+ $img.load(imageProp);
+ }
+ },
+ fbClickHandler = function(e) {
+
+ // Check if the fluidbox element does have .fluidbox assigned to it
+ if($(this).hasClass('fluidbox')) {
+
+ // Variables
+ var $activeFb = $(this),
+ $img = $(this).find('img'),
+ $ghost = $(this).find('.fluidbox-ghost'),
+ $wrap = $(this).find('.fluidbox-wrap'),
+ timer = {};
+
+ if($(this).data('fluidbox-state') === 0 || !$(this).data('fluidbox-state')) {
+ // State: Closed
+ // Action: Open fluidbox
+
+ // Fire custom event: openstart
+ $(this).trigger('openstart');
+
+ // Wait for ghost image to be loaded successfully first, then do the rest
+ $('
', {
+ src: $img.attr('src')
+ }).load(function () {
+ // Preload ghost image
+ $('
', {
+ src: $activeFb.attr('href')
+ }).load(function() {
+ // Store natural width and heights
+ $activeFb
+ .data('natWidth', $(this)[0].naturalWidth)
+ .data('natHeight', $(this)[0].naturalHeight);
+
+ // What are we doing here:
+ // 1. Append overlay in fluidbox
+ // 2. Toggle fluidbox state with data attribute
+ // 3. Store original z-index with data attribute (so users can change z-index when they see fit in CSS file)
+ // 4. Class toggle
+ $activeFb
+ .append($fbOverlay)
+ .data('fluidbox-state', 1)
+ .removeClass('fluidbox-closed')
+ .addClass('fluidbox-opened');
+
+ // Force timer to completion
+ if(timer['close']) window.clearTimeout(timer['close']);
+
+ // Set timer for opening
+ timer['open'] = window.setTimeout(function() {
+ // Show overlay
+ $('.fluidbox-overlay').css({ opacity: 1 });
+ }, 10);
+
+ // Change wrapper z-index, so it is above everything else
+ // Decrease all siblings z-index by 1 just in case
+ $('.fluidbox-wrap').css({ zIndex: settings.stackIndex - settings.stackIndexDelta - 1 });
+ $wrap.css({ 'z-index': settings.stackIndex + settings.stackIndexDelta });
+
+ // Set thumbnail image source as background image first, preload later
+ $ghost.css({
+ 'background-image': 'url('+$img.attr('src')+')',
+ opacity: 1
+ });
+
+ // Hide original image
+ $img.css({ opacity: 0 });
+
+ $ghost.css({ 'background-image': 'url('+$activeFb.attr('href')+')' });
+
+ // Position Fluidbox
+ funcPositionFb($activeFb, 'openend');
+ });
+ });
+
+ } else {
+ // State: Open
+ // Action: Close fluidbox
+
+ // Fire custom event: closestart
+ $activeFb.trigger('closestart');
+
+ // Switch state
+ $activeFb
+ .data('fluidbox-state', 0)
+ .removeClass('fluidbox-opened')
+ .addClass('fluidbox-closed');
+
+ // Set timer for closing
+ if(timer['open']) window.clearTimeout(timer['open']);
+ timer['close'] = window.setTimeout(function() {
+ $('.fluidbox-overlay').remove();
+ $wrap.css({ 'z-index': settings.stackIndex - settings.stackIndexDelta });
+ }, 10);
+
+ // Hide overlay
+ $('.fluidbox-overlay').css({ opacity: 0 });
+
+ // Reverse animation on wrapped elements, and restore stacking order
+ // You might want to change this value if your transition timing is longer
+ $ghost.css({
+ 'transform': 'translate(0,0) scale(1)',
+ opacity: 0,
+ top: $img.offset().top - $wrap.offset().top + parseInt($img.css('borderTopWidth')) + parseInt($img.css('paddingTop')),
+ left: $img.offset().left - $wrap.offset().left + parseInt($img.css('borderLeftWidth')) + parseInt($img.css('paddingLeft'))
+ }).one(customTransitionEnd, function() {
+ $activeFb.trigger('closeend');
+ });
+ $img.css({ opacity: 1 });
+ }
+
+ e.preventDefault();
+ }
+ };
+
+ // When should we close Fluidbox?
+ if(settings.closeTrigger) {
+ // Go through array
+ $.each(settings.closeTrigger, function (i) {
+ var trigger = settings.closeTrigger[i];
+
+ // Attach events
+ if(trigger.selector != 'window') {
+ // If it is not 'window', we append click handler to $(document) object, allow it to bubble up
+ // However, if thes selector is 'document', we use a different .on() syntax
+ if(trigger.selector == 'document') {
+ if(trigger.keyCode) {
+ $(document).on(trigger.event, function (e) {
+ if(e.keyCode == trigger.keyCode) funcCloseFb();
+ });
+ } else {
+ $(document).on(trigger.event, funcCloseFb);
+ }
+ } else {
+ $(document).on(trigger.event, settings.closeTrigger[i].selector, funcCloseFb);
+ }
+ } else {
+ // If it is 'window', append click handler to $(window) object
+ $w.on(trigger.event, funcCloseFb);
+ }
+ });
+ }
+
+ // Go through each individual object
+ $fb.each(function (i) {
+
+ // Check if Fluidbox:
+ // 1. Is an anchor element ,
+ // 2. Contains one and ONLY one child
+ // 3. The only child is an image element,
+ // 4. If the element is hidden
+ if($(this).is('a') && $(this).children().length === 1 && $(this).children().is('img') && $(this).css('display') !== 'none' && $(this).parents().css('display') !=='none') {
+
+ // Define wrap
+ var $fbInnerWrap = $('', {
+ 'class': 'fluidbox-wrap',
+ css: {
+ 'z-index': settings.stackIndex - settings.stackIndexDelta
+ }
+ });
+
+ // Add class
+ var $fbItem = $(this);
+ $fbItem
+ .addClass('fluidbox')
+ .wrapInner($fbInnerWrap)
+ .find('img')
+ .css({ opacity: 1 })
+ .after('')
+ .each(function(){
+ var $img = $(this);
+
+ if ($img.width() > 0 && $img.height() > 0) {
+ // if image is already loaded (from cache)
+ funcCalc($fbItem);
+ $fbItem.click(fbClickHandler);
+ } else {
+ // wait for image to load
+ $img.load(function(){
+ funcCalc($fbItem);
+ $fbItem.click(fbClickHandler);
+ });
+ }
+ });
+
+ }
+ });
+
+ // Listen to window resize event
+ // Check if user wants to debounce the resize event (it is debounced by default)
+ var funcResize = function () {
+ // Recalculate dimensions
+ funcCalcAll();
+
+ // Reposition Fluidbox, but only if one is found to be open
+ var $activeFb = $('a.fluidbox.fluidbox-opened');
+ if($activeFb.length > 0) funcPositionFb($activeFb, 'resizeend');
+ }
+
+ if(settings.debounceResize) {
+ $(window).smartresize(funcResize);
+ } else {
+ $(window).resize(funcResize);
+ }
+
+ // Return to allow chaining
+ return $fb;
+ };
+
+})(jQuery);
\ No newline at end of file
diff --git a/ajax/libs/fluidbox/1.4.1/jquery.fluidbox.min.js b/ajax/libs/fluidbox/1.4.1/jquery.fluidbox.min.js
new file mode 100644
index 000000000..69a955d86
--- /dev/null
+++ b/ajax/libs/fluidbox/1.4.1/jquery.fluidbox.min.js
@@ -0,0 +1 @@
+function whichTransitionEvent(){var a,b=document.createElement("fakeelement"),c={transition:"transitionend",OTransition:"oTransitionEnd",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(a in c)if(void 0!==b.style[a])return c[a]}!function(a,b){var c=function(a,b,c){var d;return function(){function g(){c||a.apply(e,f),d=null}var e=this,f=arguments;d?clearTimeout(d):c&&a.apply(e,f),d=setTimeout(g,b||100)}};jQuery.fn[b]=function(a){return a?this.bind("resize",c(a)):this.trigger(b)}}(jQuery,"smartresize");var customTransitionEnd=whichTransitionEvent();!function(a){a.fn.fluidbox=function(b){var c=a.extend(!0,{viewportFill:.95,debounceResize:!0,stackIndex:1e3,stackIndexDelta:10,closeTrigger:[{selector:".fluidbox-overlay",event:"click"},{selector:"document",event:"keyup",keyCode:27}]},b);c.stackIndex",{"class":"fluidbox-overlay",css:{"z-index":c.stackIndex}});var f,d=this,e=a(window),g=function(){a(".fluidbox-opened").trigger("click")},h=function(a,b){var d=a.find("img"),g=a.find(".fluidbox-ghost"),h=a.find(".fluidbox-wrap"),i=a.data(),j=0,k=0;d.data().imgRatio=i.natWidth/i.natHeight;var l,m,n;if(f>d.data().imgRatio)j=i.natHeighth.imgRatio?e.height()*c.viewportFill/b.height():e.width()*c.viewportFill/b.width()}if(f=e.width()/e.height(),a.hasClass("fluidbox")){var b=a.find("img"),d=a.find(".fluidbox-ghost"),g=a.find(".fluidbox-wrap"),h=b.data();i(),b.load(i)}},k=function(b){if(a(this).hasClass("fluidbox")){var d=a(this),e=a(this).find("img"),f=a(this).find(".fluidbox-ghost"),g=a(this).find(".fluidbox-wrap"),i={};0!==a(this).data("fluidbox-state")&&a(this).data("fluidbox-state")?(d.trigger("closestart"),d.data("fluidbox-state",0).removeClass("fluidbox-opened").addClass("fluidbox-closed"),i.open&&window.clearTimeout(i.open),i.close=window.setTimeout(function(){a(".fluidbox-overlay").remove(),g.css({"z-index":c.stackIndex-c.stackIndexDelta})},10),a(".fluidbox-overlay").css({opacity:0}),f.css({transform:"translate(0,0) scale(1)",opacity:0,top:e.offset().top-g.offset().top+parseInt(e.css("borderTopWidth"))+parseInt(e.css("paddingTop")),left:e.offset().left-g.offset().left+parseInt(e.css("borderLeftWidth"))+parseInt(e.css("paddingLeft"))}).one(customTransitionEnd,function(){d.trigger("closeend")}),e.css({opacity:1})):(a(this).trigger("openstart"),a("
",{src:e.attr("src")}).load(function(){a("
",{src:d.attr("href")}).load(function(){d.data("natWidth",a(this)[0].naturalWidth).data("natHeight",a(this)[0].naturalHeight),d.append($fbOverlay).data("fluidbox-state",1).removeClass("fluidbox-closed").addClass("fluidbox-opened"),i.close&&window.clearTimeout(i.close),i.open=window.setTimeout(function(){a(".fluidbox-overlay").css({opacity:1})},10),a(".fluidbox-wrap").css({zIndex:c.stackIndex-c.stackIndexDelta-1}),g.css({"z-index":c.stackIndex+c.stackIndexDelta}),f.css({"background-image":"url("+e.attr("src")+")",opacity:1}),e.css({opacity:0}),f.css({"background-image":"url("+d.attr("href")+")"}),h(d,"openend")})})),b.preventDefault()}};c.closeTrigger&&a.each(c.closeTrigger,function(b){var d=c.closeTrigger[b];"window"!=d.selector?"document"==d.selector?d.keyCode?a(document).on(d.event,function(a){a.keyCode==d.keyCode&&g()}):a(document).on(d.event,g):a(document).on(d.event,c.closeTrigger[b].selector,g):e.on(d.event,g)}),d.each(function(){if(a(this).is("a")&&1===a(this).children().length&&a(this).children().is("img")&&"none"!==a(this).css("display")&&"none"!==a(this).parents().css("display")){var d=a("",{"class":"fluidbox-wrap",css:{"z-index":c.stackIndex-c.stackIndexDelta}}),e=a(this);e.addClass("fluidbox").wrapInner(d).find("img").css({opacity:1}).after('').each(function(){var b=a(this);b.width()>0&&b.height()>0?(j(e),e.click(k)):b.load(function(){j(e),e.click(k)})})}});var l=function(){i();var b=a("a.fluidbox.fluidbox-opened");b.length>0&&h(b,"resizeend")};return c.debounceResize?a(window).smartresize(l):a(window).resize(l),d}}(jQuery);
\ No newline at end of file
diff --git a/ajax/libs/fluidbox/package.json b/ajax/libs/fluidbox/package.json
index e08df06ad..082647fb1 100644
--- a/ajax/libs/fluidbox/package.json
+++ b/ajax/libs/fluidbox/package.json
@@ -1,7 +1,7 @@
{
"name": "fluidbox",
"filename": "jquery.fluidbox.min.js",
- "version": "1.4.0",
+ "version": "1.4.1",
"description": "Replicating Medium's beautiful, minimalist lightbox module",
"homepage": "https://github.com/terrymun/Fluidbox",
"authors": [