flickr-justified-gallery
Version:
Flickr's justified images gallery
1 lines • 72.7 kB
Source Map (JSON)
{"version":3,"file":"fjGallery.cjs","sources":["../node_modules/throttle-debounce/esm/index.js","../node_modules/raf-schd/dist/raf-schd.esm.js","../node_modules/better-justified-layout/dist/better-justified-layout.js","../src/utils/ready.js","../src/utils/global.js","../src/utils/get-img-dimensions.js","../src/fjGallery.js"],"sourcesContent":["/* eslint-disable no-undefined,no-param-reassign,no-shadow */\n\n/**\n * Throttle execution of a function. Especially useful for rate limiting\n * execution of handlers on events like resize and scroll.\n *\n * @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher)\n * are most useful.\n * @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through,\n * as-is, to `callback` when the throttled-function is executed.\n * @param {object} [options] - An object to configure options.\n * @param {boolean} [options.noTrailing] - Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds\n * while the throttled-function is being called. If noTrailing is false or unspecified, callback will be executed\n * one final time after the last throttled-function call. (After the throttled-function has not been called for\n * `delay` milliseconds, the internal counter is reset).\n * @param {boolean} [options.noLeading] - Optional, defaults to false. If noLeading is false, the first throttled-function call will execute callback\n * immediately. If noLeading is true, the first the callback execution will be skipped. It should be noted that\n * callback will never executed if both noLeading = true and noTrailing = true.\n * @param {boolean} [options.debounceMode] - If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is\n * false (at end), schedule `callback` to execute after `delay` ms.\n *\n * @returns {Function} A new, throttled, function.\n */\nfunction throttle (delay, callback, options) {\n var _ref = options || {},\n _ref$noTrailing = _ref.noTrailing,\n noTrailing = _ref$noTrailing === void 0 ? false : _ref$noTrailing,\n _ref$noLeading = _ref.noLeading,\n noLeading = _ref$noLeading === void 0 ? false : _ref$noLeading,\n _ref$debounceMode = _ref.debounceMode,\n debounceMode = _ref$debounceMode === void 0 ? undefined : _ref$debounceMode;\n /*\n * After wrapper has stopped being called, this timeout ensures that\n * `callback` is executed at the proper times in `throttle` and `end`\n * debounce modes.\n */\n\n\n var timeoutID;\n var cancelled = false; // Keep track of the last time `callback` was executed.\n\n var lastExec = 0; // Function to clear existing timeout\n\n function clearExistingTimeout() {\n if (timeoutID) {\n clearTimeout(timeoutID);\n }\n } // Function to cancel next exec\n\n\n function cancel(options) {\n var _ref2 = options || {},\n _ref2$upcomingOnly = _ref2.upcomingOnly,\n upcomingOnly = _ref2$upcomingOnly === void 0 ? false : _ref2$upcomingOnly;\n\n clearExistingTimeout();\n cancelled = !upcomingOnly;\n }\n /*\n * The `wrapper` function encapsulates all of the throttling / debouncing\n * functionality and when executed will limit the rate at which `callback`\n * is executed.\n */\n\n\n function wrapper() {\n for (var _len = arguments.length, arguments_ = new Array(_len), _key = 0; _key < _len; _key++) {\n arguments_[_key] = arguments[_key];\n }\n\n var self = this;\n var elapsed = Date.now() - lastExec;\n\n if (cancelled) {\n return;\n } // Execute `callback` and update the `lastExec` timestamp.\n\n\n function exec() {\n lastExec = Date.now();\n callback.apply(self, arguments_);\n }\n /*\n * If `debounceMode` is true (at begin) this is used to clear the flag\n * to allow future `callback` executions.\n */\n\n\n function clear() {\n timeoutID = undefined;\n }\n\n if (!noLeading && debounceMode && !timeoutID) {\n /*\n * Since `wrapper` is being called for the first time and\n * `debounceMode` is true (at begin), execute `callback`\n * and noLeading != true.\n */\n exec();\n }\n\n clearExistingTimeout();\n\n if (debounceMode === undefined && elapsed > delay) {\n if (noLeading) {\n /*\n * In throttle mode with noLeading, if `delay` time has\n * been exceeded, update `lastExec` and schedule `callback`\n * to execute after `delay` ms.\n */\n lastExec = Date.now();\n\n if (!noTrailing) {\n timeoutID = setTimeout(debounceMode ? clear : exec, delay);\n }\n } else {\n /*\n * In throttle mode without noLeading, if `delay` time has been exceeded, execute\n * `callback`.\n */\n exec();\n }\n } else if (noTrailing !== true) {\n /*\n * In trailing throttle mode, since `delay` time has not been\n * exceeded, schedule `callback` to execute `delay` ms after most\n * recent execution.\n *\n * If `debounceMode` is true (at begin), schedule `clear` to execute\n * after `delay` ms.\n *\n * If `debounceMode` is false (at end), schedule `callback` to\n * execute after `delay` ms.\n */\n timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);\n }\n }\n\n wrapper.cancel = cancel; // Return the wrapper function.\n\n return wrapper;\n}\n\n/* eslint-disable no-undefined */\n/**\n * Debounce execution of a function. Debouncing, unlike throttling,\n * guarantees that a function is only executed a single time, either at the\n * very beginning of a series of calls, or at the very end.\n *\n * @param {number} delay - A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.\n * @param {Function} callback - A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,\n * to `callback` when the debounced-function is executed.\n * @param {object} [options] - An object to configure options.\n * @param {boolean} [options.atBegin] - Optional, defaults to false. If atBegin is false or unspecified, callback will only be executed `delay` milliseconds\n * after the last debounced-function call. If atBegin is true, callback will be executed only at the first debounced-function call.\n * (After the throttled-function has not been called for `delay` milliseconds, the internal counter is reset).\n *\n * @returns {Function} A new, debounced function.\n */\n\nfunction debounce (delay, callback, options) {\n var _ref = options || {},\n _ref$atBegin = _ref.atBegin,\n atBegin = _ref$atBegin === void 0 ? false : _ref$atBegin;\n\n return throttle(delay, callback, {\n debounceMode: atBegin !== false\n });\n}\n\nexport { debounce, throttle };\n//# sourceMappingURL=index.js.map\n","var rafSchd = function rafSchd(fn) {\n var lastArgs = [];\n var frameId = null;\n\n var wrapperFn = function wrapperFn() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n lastArgs = args;\n\n if (frameId) {\n return;\n }\n\n frameId = requestAnimationFrame(function () {\n frameId = null;\n fn.apply(void 0, lastArgs);\n });\n };\n\n wrapperFn.cancel = function () {\n if (!frameId) {\n return;\n }\n\n cancelAnimationFrame(frameId);\n frameId = null;\n };\n\n return wrapperFn;\n};\n\nexport default rafSchd;\n","'use strict';\n\n/**\n * Row\n * Wrapper for each row in a justified layout.\n * Stores relevant values and provides methods for calculating layout of individual rows.\n *\n * @param {Object} layoutConfig - The same as that passed\n * @param {Object} Initialization parameters. The following are all required:\n * @param params.index {Number} The index of this row\n * @param params.top {Number} Top of row, relative to container\n * @param params.left {Number} Left side of row relative to container (equal to container left padding)\n * @param params.width {Number} Width of row, not including container padding\n * @param params.spacing {Number} Horizontal spacing between items\n * @param params.targetRowHeight {Number} Layout algorithm will aim for this row height\n * @param params.targetRowHeightTolerance {Number} Row heights may vary +/- (`targetRowHeight` x `targetRowHeightTolerance`)\n * @param params.edgeCaseMinRowHeight {Number} Absolute minimum row height for edge cases that cannot be resolved within tolerance.\n * @param params.edgeCaseMaxRowHeight {Number} Absolute maximum row height for edge cases that cannot be resolved within tolerance.\n * @param params.isBreakoutRow {Boolean} Is this row in particular one of those breakout rows? Always false if it's not that kind of photo list\n * @param params.widowLayoutStyle {String} If widows are visible, how should they be laid out?\n * @constructor\n */\nconst Row = function (params) {\n // The index of this row\n this.index = params.index;\n\n // Top of row, relative to container\n this.top = params.top;\n\n // Left side of row relative to container (equal to container left padding)\n this.left = params.left;\n\n // Width of row, not including container padding\n this.width = params.width;\n\n // Horizontal spacing between items\n this.spacing = params.spacing;\n\n // Row height calculation values\n this.targetRowHeight = params.targetRowHeight;\n this.targetRowHeightTolerance = params.targetRowHeightTolerance;\n this.minAspectRatio = this.width / params.targetRowHeight * (1 - params.targetRowHeightTolerance);\n this.maxAspectRatio = this.width / params.targetRowHeight * (1 + params.targetRowHeightTolerance);\n\n // Edge case row height minimum/maximum\n this.edgeCaseMinRowHeight = params.edgeCaseMinRowHeight;\n this.edgeCaseMaxRowHeight = params.edgeCaseMaxRowHeight;\n\n // Widow layout direction\n this.widowLayoutStyle = params.widowLayoutStyle;\n\n // Full width breakout rows\n this.isBreakoutRow = params.isBreakoutRow;\n\n // Store layout data for each item in row\n this.items = [];\n\n // Height remains at 0 until it's been calculated\n this.height = 0;\n};\nRow.prototype = {\n /**\n * Attempt to add a single item to the row.\n * This is the heart of the justified algorithm.\n * This method is direction-agnostic; it deals only with sizes, not positions.\n *\n * If the item fits in the row, without pushing row height beyond min/max tolerance,\n * the item is added and the method returns true.\n *\n * If the item leaves row height too high, there may be room to scale it down and add another item.\n * In this case, the item is added and the method returns true, but the row is incomplete.\n *\n * If the item leaves row height too short, there are too many items to fit within tolerance.\n * The method will either accept or reject the new item, favoring the resulting row height closest to within tolerance.\n * If the item is rejected, left/right padding will be required to fit the row height within tolerance;\n * if the item is accepted, top/bottom cropping will be required to fit the row height within tolerance.\n *\n * @method addItem\n * @param itemData {Object} Item layout data, containing item aspect ratio.\n * @return {Boolean} True if successfully added; false if rejected.\n */\n addItem: function (itemData) {\n const newItems = this.items.concat(itemData);\n // Calculate aspect ratios for items only; exclude spacing\n const rowWidthWithoutSpacing = this.width - (newItems.length - 1) * this.spacing;\n const newAspectRatio = newItems.reduce(function (sum, item) {\n return sum + item.aspectRatio;\n }, 0);\n const targetAspectRatio = rowWidthWithoutSpacing / this.targetRowHeight;\n let previousRowWidthWithoutSpacing;\n let previousAspectRatio;\n let previousTargetAspectRatio;\n\n // Handle big full-width breakout photos if we're doing them\n if (this.isBreakoutRow) {\n // Only do it if there's no other items in this row\n if (this.items.length === 0) {\n // Only go full width if this photo is a square or landscape\n if (itemData.aspectRatio >= 1) {\n // Close out the row with a full width photo\n this.items.push(itemData);\n this.completeLayout(rowWidthWithoutSpacing / itemData.aspectRatio, \"justify\");\n return true;\n }\n }\n }\n if (newAspectRatio < this.minAspectRatio) {\n // New aspect ratio is too narrow / scaled row height is too tall.\n // Accept this item and leave row open for more items.\n\n this.items.push(Object.assign({}, itemData));\n return true;\n } else if (newAspectRatio > this.maxAspectRatio) {\n // New aspect ratio is too wide / scaled row height will be too short.\n // Accept item if the resulting aspect ratio is closer to target than it would be without the item.\n // NOTE: Any row that falls into this block will require cropping/padding on individual items.\n\n if (this.items.length === 0) {\n // When there are no existing items, force acceptance of the new item and complete the layout.\n // This is the pano special case.\n this.items.push(Object.assign({}, itemData));\n this.completeLayout(rowWidthWithoutSpacing / newAspectRatio, \"justify\");\n return true;\n }\n\n // Calculate width/aspect ratio for row before adding new item\n previousRowWidthWithoutSpacing = this.width - (this.items.length - 1) * this.spacing;\n previousAspectRatio = this.items.reduce(function (sum, item) {\n return sum + item.aspectRatio;\n }, 0);\n previousTargetAspectRatio = previousRowWidthWithoutSpacing / this.targetRowHeight;\n if (Math.abs(newAspectRatio - targetAspectRatio) > Math.abs(previousAspectRatio - previousTargetAspectRatio)) {\n // Row with new item is us farther away from target than row without; complete layout and reject item.\n this.completeLayout(previousRowWidthWithoutSpacing / previousAspectRatio, \"justify\");\n return false;\n } else {\n // Row with new item is us closer to target than row without;\n // accept the new item and complete the row layout.\n this.items.push(Object.assign({}, itemData));\n this.completeLayout(rowWidthWithoutSpacing / newAspectRatio, \"justify\");\n return true;\n }\n } else {\n // New aspect ratio / scaled row height is within tolerance;\n // accept the new item and complete the row layout.\n this.items.push(Object.assign({}, itemData));\n this.completeLayout(rowWidthWithoutSpacing / newAspectRatio, \"justify\");\n return true;\n }\n },\n /**\n * Check if a row has completed its layout.\n *\n * @method isLayoutComplete\n * @return {Boolean} True if complete; false if not.\n */\n isLayoutComplete: function () {\n return this.height > 0;\n },\n /**\n * Set row height and compute item geometry from that height.\n * Will justify items within the row unless instructed not to.\n *\n * @method completeLayout\n * @param newHeight {Number} Set row height to this value.\n * @param widowLayoutStyle {String} How should widows display? Supported: left | justify | center\n */\n completeLayout: function (newHeight, widowLayoutStyle) {\n let itemWidthSum = this.left;\n const rowWidthWithoutSpacing = this.width - (this.items.length - 1) * this.spacing;\n let clampedToNativeRatio;\n let clampedHeight;\n let errorWidthPerItem;\n let roundedCumulativeErrors;\n let singleItemGeometry;\n let centerOffset;\n\n // Justify unless explicitly specified otherwise.\n if (typeof widowLayoutStyle === \"undefined\" || [\"justify\", \"center\", \"left\"].indexOf(widowLayoutStyle) < 0) {\n widowLayoutStyle = \"left\";\n }\n\n // Clamp row height to edge case minimum/maximum.\n clampedHeight = Math.max(this.edgeCaseMinRowHeight, Math.min(newHeight, this.edgeCaseMaxRowHeight));\n if (newHeight !== clampedHeight) {\n // If row height was clamped, the resulting row/item aspect ratio will be off,\n // so force it to fit the width (recalculate aspectRatio to match clamped height).\n // NOTE: this will result in cropping/padding commensurate to the amount of clamping.\n this.height = clampedHeight;\n clampedToNativeRatio = rowWidthWithoutSpacing / clampedHeight / (rowWidthWithoutSpacing / newHeight);\n } else {\n // If not clamped, leave ratio at 1.0.\n this.height = newHeight;\n clampedToNativeRatio = 1.0;\n }\n\n // Compute item geometry based on newHeight.\n this.items.forEach(function (item) {\n item.row = this.index;\n item.top = this.top;\n item.width = item.aspectRatio * this.height * clampedToNativeRatio;\n item.height = this.height;\n\n // Left-to-right.\n // TODO right to left\n // item.left = this.width - itemWidthSum - item.width;\n item.left = itemWidthSum;\n\n // Increment width.\n itemWidthSum += item.width + this.spacing;\n }, this);\n\n // If specified, ensure items fill row and distribute error\n // caused by rounding width and height across all items.\n if (widowLayoutStyle === \"justify\") {\n itemWidthSum -= this.spacing + this.left;\n errorWidthPerItem = (itemWidthSum - this.width) / this.items.length;\n roundedCumulativeErrors = this.items.map(function (item, i) {\n return Math.round((i + 1) * errorWidthPerItem);\n });\n if (this.items.length === 1) {\n // For rows with only one item, adjust item width to fill row.\n singleItemGeometry = this.items[0];\n singleItemGeometry.width -= Math.round(errorWidthPerItem);\n } else {\n // For rows with multiple items, adjust item width and shift items to fill the row,\n // while maintaining equal spacing between items in the row.\n this.items.forEach(function (item, i) {\n if (i > 0) {\n item.left -= roundedCumulativeErrors[i - 1];\n item.width -= roundedCumulativeErrors[i] - roundedCumulativeErrors[i - 1];\n } else {\n item.width -= roundedCumulativeErrors[i];\n }\n });\n }\n } else if (widowLayoutStyle === \"center\") {\n // Center widows\n centerOffset = (this.width - itemWidthSum) / 2;\n this.items.forEach(function (item) {\n item.left += centerOffset + this.spacing;\n }, this);\n }\n },\n /**\n * Force completion of row layout with current items.\n *\n * @method forceComplete\n * @param fitToWidth {Boolean} Stretch current items to fill the row width.\n * This will likely result in padding.\n * @param fitToWidth {Number}\n */\n forceComplete: function (fitToWidth, rowHeight) {\n // TODO Handle fitting to width\n // var rowWidthWithoutSpacing = this.width - (this.items.length - 1) * this.spacing,\n // \tcurrentAspectRatio = this.items.reduce(function (sum, item) {\n // \t\treturn sum + item.aspectRatio;\n // \t}, 0);\n\n if (typeof rowHeight === \"number\") {\n this.completeLayout(rowHeight, this.widowLayoutStyle);\n } else {\n // Complete using target row height.\n this.completeLayout(this.targetRowHeight, this.widowLayoutStyle);\n }\n },\n /**\n * Return layout data for items within row.\n * Note: returns actual list, not a copy.\n *\n * @method getItems\n * @return Layout data for items within row.\n */\n getItems: function () {\n return this.items;\n }\n};\n\n/*!\n * Copyright 2019 SmugMug, Inc. | Copyright 2024 nK\n * Licensed under the terms of the MIT license. Please see LICENSE file in the project root for terms.\n * @license\n */\n\n/**\n * Create a new, empty row.\n *\n * @method createNewRow\n * @param layoutConfig {Object} The layout configuration\n * @param layoutData {Object} The current state of the layout\n * @return A new, empty row of the type specified by this layout.\n */\nfunction createNewRow(layoutConfig, layoutData) {\n let isBreakoutRow;\n\n // Work out if this is a full width breakout row\n if (layoutConfig.fullWidthBreakoutRowCadence !== false) {\n if ((layoutData._rows.length + 1) % layoutConfig.fullWidthBreakoutRowCadence === 0) {\n isBreakoutRow = true;\n }\n }\n return new Row({\n index: layoutData._rows.length,\n top: layoutData._containerHeight,\n left: layoutConfig.containerPadding.left,\n width: layoutConfig.containerWidth - layoutConfig.containerPadding.left - layoutConfig.containerPadding.right,\n spacing: layoutConfig.boxSpacing.horizontal,\n targetRowHeight: layoutConfig.targetRowHeight,\n targetRowHeightTolerance: layoutConfig.targetRowHeightTolerance,\n edgeCaseMinRowHeight: layoutConfig.edgeCaseMinRowHeight * layoutConfig.targetRowHeight,\n edgeCaseMaxRowHeight: layoutConfig.edgeCaseMaxRowHeight * layoutConfig.targetRowHeight,\n rightToLeft: false,\n isBreakoutRow: isBreakoutRow,\n widowLayoutStyle: layoutConfig.widowLayoutStyle\n });\n}\n\n/**\n * Add a completed row to the layout.\n * Note: the row must have already been completed.\n *\n * @method addRow\n * @param layoutConfig {Object} The layout configuration\n * @param layoutData {Object} The current state of the layout\n * @param row {Row} The row to add.\n * @return {Array} Each item added to the row.\n */\nfunction addRow(layoutConfig, layoutData, row) {\n layoutData._rows.push(row);\n layoutData._layoutItems = layoutData._layoutItems.concat(row.getItems());\n\n // Increment the container height\n layoutData._containerHeight += row.height + layoutConfig.boxSpacing.vertical;\n return row.items;\n}\n\n/**\n * Calculate the current layout for all items in the list that require layout.\n * \"Layout\" means geometry: position within container and size\n *\n * @method computeLayout\n * @param layoutConfig {Object} The layout configuration\n * @param layoutData {Object} The current state of the layout\n * @param itemLayoutData {Array} Array of items to lay out, with data required to lay out each item\n * @return {Object} The newly-calculated layout, containing the new container height, and lists of layout items\n */\nfunction computeLayout(layoutConfig, layoutData, itemLayoutData) {\n let laidOutItems = [];\n let itemAdded;\n let currentRow;\n let nextToLastRowHeight;\n\n // Apply forced aspect ratio if specified, and set a flag.\n if (layoutConfig.forceAspectRatio) {\n itemLayoutData.forEach(function (itemData) {\n itemData.forcedAspectRatio = true;\n itemData.aspectRatio = layoutConfig.forceAspectRatio;\n });\n }\n\n // Loop through the items\n itemLayoutData.some(function (itemData, i) {\n if (isNaN(itemData.aspectRatio)) {\n throw new Error(\"Item \" + i + \" has an invalid aspect ratio\");\n }\n\n // If not currently building up a row, make a new one.\n if (!currentRow) {\n currentRow = createNewRow(layoutConfig, layoutData);\n }\n\n // Attempt to add item to the current row.\n itemAdded = currentRow.addItem(itemData);\n if (currentRow.isLayoutComplete()) {\n // Row is filled; add it and start a new one\n laidOutItems = laidOutItems.concat(addRow(layoutConfig, layoutData, currentRow));\n if (layoutData._rows.length >= layoutConfig.maxNumRows) {\n currentRow = null;\n return true;\n }\n currentRow = createNewRow(layoutConfig, layoutData);\n\n // Item was rejected; add it to its own row\n if (!itemAdded) {\n itemAdded = currentRow.addItem(itemData);\n if (currentRow.isLayoutComplete()) {\n // If the rejected item fills a row on its own, add the row and start another new one\n laidOutItems = laidOutItems.concat(addRow(layoutConfig, layoutData, currentRow));\n if (layoutData._rows.length >= layoutConfig.maxNumRows) {\n currentRow = null;\n return true;\n }\n currentRow = createNewRow(layoutConfig, layoutData);\n }\n }\n }\n });\n\n // Handle any leftover content (orphans) depending on where they lie\n // in this layout update, and in the total content set.\n if (currentRow && currentRow.getItems().length && layoutConfig.showWidows) {\n // Last page of all content or orphan suppression is suppressed; lay out orphans.\n if (layoutData._rows.length) {\n // Only Match previous row's height if it exists and it isn't a breakout row\n if (layoutData._rows[layoutData._rows.length - 1].isBreakoutRow) {\n nextToLastRowHeight = layoutData._rows[layoutData._rows.length - 1].targetRowHeight;\n } else {\n nextToLastRowHeight = layoutData._rows[layoutData._rows.length - 1].height;\n }\n currentRow.forceComplete(false, nextToLastRowHeight);\n } else {\n // ...else use target height if there is no other row height to reference.\n currentRow.forceComplete(false);\n }\n laidOutItems = laidOutItems.concat(addRow(layoutConfig, layoutData, currentRow));\n layoutConfig._widowCount = currentRow.getItems().length;\n }\n\n // We need to clean up the bottom container padding\n // First remove the height added for box spacing\n layoutData._containerHeight = layoutData._containerHeight - layoutConfig.boxSpacing.vertical;\n // Then add our bottom container padding\n layoutData._containerHeight = layoutData._containerHeight + layoutConfig.containerPadding.bottom;\n return {\n containerHeight: layoutData._containerHeight,\n widowCount: layoutConfig._widowCount,\n boxes: layoutData._layoutItems\n };\n}\n\n/**\n * Takes in a bunch of box data and config. Returns\n * geometry to lay them out in a justified view.\n *\n * @method covertSizesToAspectRatios\n * @param sizes {Array} Array of objects with widths and heights\n * @return {Array} A list of aspect ratios\n */\nfunction index (input, config) {\n let layoutConfig = {};\n const layoutData = {};\n\n // Defaults\n const defaults = {\n containerWidth: 1060,\n containerPadding: 10,\n boxSpacing: 10,\n targetRowHeight: 320,\n targetRowHeightTolerance: 0.25,\n edgeCaseMinRowHeight: 0.5,\n edgeCaseMaxRowHeight: 2.5,\n maxNumRows: Number.POSITIVE_INFINITY,\n forceAspectRatio: false,\n showWidows: true,\n fullWidthBreakoutRowCadence: false,\n widowLayoutStyle: \"left\"\n };\n const containerPadding = {};\n const boxSpacing = {};\n config = config || {};\n\n // Merge defaults and config passed in\n layoutConfig = Object.assign(defaults, config);\n\n // Sort out padding and spacing values\n containerPadding.top = !isNaN(parseFloat(layoutConfig.containerPadding.top)) ? layoutConfig.containerPadding.top : layoutConfig.containerPadding;\n containerPadding.right = !isNaN(parseFloat(layoutConfig.containerPadding.right)) ? layoutConfig.containerPadding.right : layoutConfig.containerPadding;\n containerPadding.bottom = !isNaN(parseFloat(layoutConfig.containerPadding.bottom)) ? layoutConfig.containerPadding.bottom : layoutConfig.containerPadding;\n containerPadding.left = !isNaN(parseFloat(layoutConfig.containerPadding.left)) ? layoutConfig.containerPadding.left : layoutConfig.containerPadding;\n boxSpacing.horizontal = !isNaN(parseFloat(layoutConfig.boxSpacing.horizontal)) ? layoutConfig.boxSpacing.horizontal : layoutConfig.boxSpacing;\n boxSpacing.vertical = !isNaN(parseFloat(layoutConfig.boxSpacing.vertical)) ? layoutConfig.boxSpacing.vertical : layoutConfig.boxSpacing;\n layoutConfig.containerPadding = containerPadding;\n layoutConfig.boxSpacing = boxSpacing;\n\n // Local\n layoutData._layoutItems = [];\n layoutData._awakeItems = [];\n layoutData._inViewportItems = [];\n layoutData._leadingOrphans = [];\n layoutData._trailingOrphans = [];\n layoutData._containerHeight = layoutConfig.containerPadding.top;\n layoutData._rows = [];\n layoutData._orphans = [];\n layoutConfig._widowCount = 0;\n\n // Convert widths and heights to aspect ratios if we need to\n return computeLayout(layoutConfig, layoutData, input.map(function (item) {\n if (item.width && item.height) {\n return {\n aspectRatio: item.width / item.height\n };\n } else {\n return {\n aspectRatio: item\n };\n }\n }));\n}\n\nmodule.exports = index;\n//# sourceMappingURL=better-justified-layout.js.map\n","function ready(callback) {\n if (document.readyState === 'complete' || document.readyState === 'interactive') {\n // Already ready or interactive, execute callback\n callback();\n } else {\n document.addEventListener('DOMContentLoaded', callback, {\n capture: true,\n once: true,\n passive: true,\n });\n }\n}\n\nexport default ready;\n","/* eslint-disable import/no-mutable-exports */\n/* eslint-disable no-restricted-globals */\nlet win;\n\nif (typeof window !== 'undefined') {\n win = window;\n} else if (typeof global !== 'undefined') {\n win = global;\n} else if (typeof self !== 'undefined') {\n win = self;\n} else {\n win = {};\n}\n\nexport default win;\n","// get image dimensions\n// thanks https://gist.github.com/dimsemenov/5382856\nfunction getImgDimensions(img, cb) {\n let interval;\n let hasSize = false;\n let addedListeners = false;\n\n const onHasSize = () => {\n if (hasSize) {\n cb(hasSize);\n return;\n }\n\n // check for non-zero, non-undefined naturalWidth\n if (!img.naturalWidth) {\n return;\n }\n\n hasSize = {\n width: img.naturalWidth,\n height: img.naturalHeight,\n };\n cb(hasSize);\n\n clearInterval(interval);\n if (addedListeners) {\n // eslint-disable-next-line no-use-before-define\n removeListeners();\n }\n };\n const onLoaded = () => {\n onHasSize();\n };\n const onError = () => {\n onHasSize();\n };\n const checkSize = () => {\n if (img.naturalWidth > 0) {\n onHasSize();\n }\n };\n const addListeners = () => {\n addedListeners = true;\n img.addEventListener('load', onLoaded);\n img.addEventListener('error', onError);\n };\n const removeListeners = () => {\n addedListeners = false;\n img.removeEventListener('load', onLoaded);\n img.removeEventListener('error', onError);\n };\n\n checkSize();\n\n if (!hasSize) {\n addListeners();\n interval = setInterval(checkSize, 100);\n }\n}\n\nexport default getImgDimensions;\n","import { debounce } from 'throttle-debounce';\nimport rafSchd from 'raf-schd';\nimport justifiedLayout from 'better-justified-layout';\n\nimport domReady from './utils/ready';\nimport global from './utils/global';\nimport getImgDimensions from './utils/get-img-dimensions';\n\n// list with all fjGallery instances\n// need to render all in one scroll/resize event\nconst fjGalleryList = [];\n\nconst updateFjGallery = rafSchd(() => {\n fjGalleryList.forEach((item) => {\n item.resize();\n });\n});\n\nglobal.addEventListener('resize', updateFjGallery);\nglobal.addEventListener('orientationchange', updateFjGallery);\nglobal.addEventListener('load', updateFjGallery);\ndomReady(() => {\n updateFjGallery();\n});\n\nlet instanceID = 0;\n\n// fjGallery class\nclass FJGallery {\n constructor(container, userOptions) {\n const self = this;\n\n self.instanceID = instanceID;\n instanceID += 1;\n\n self.$container = container;\n\n self.images = [];\n\n self.defaults = {\n itemSelector: '.fj-gallery-item',\n imageSelector: 'img',\n gutter: 10, // supports object like `{ horizontal: 10, vertical: 10 }`.\n rowHeight: 320,\n rowHeightTolerance: 0.25, // [0, 1]\n maxRowsCount: Number.POSITIVE_INFINITY,\n edgeCaseMinRowHeight: 0.5,\n edgeCaseMaxRowHeight: 2.5,\n lastRow: 'left', // left, center, right, hide\n transitionDuration: '0.3s',\n calculateItemsHeight: false,\n resizeDebounce: 100,\n isRtl: self.css(self.$container, 'direction') === 'rtl',\n\n // events\n onInit: null, // function() {}\n onDestroy: null, // function() {}\n onAppendImages: null, // function() {}\n onBeforeJustify: null, // function() {}\n onJustify: null, // function() {}\n };\n\n // prepare data-options\n const dataOptions = self.$container.dataset || {};\n const pureDataOptions = {};\n Object.keys(dataOptions).forEach((key) => {\n const loweCaseOption = key.substr(0, 1).toLowerCase() + key.substr(1);\n if (loweCaseOption && typeof self.defaults[loweCaseOption] !== 'undefined') {\n pureDataOptions[loweCaseOption] = dataOptions[key];\n }\n });\n\n self.options = {\n ...self.defaults,\n ...pureDataOptions,\n ...userOptions,\n };\n\n self.pureOptions = {\n ...self.options,\n };\n\n // debounce for resize\n self.resize = debounce(self.options.resizeDebounce, self.resize);\n self.justify = rafSchd(self.justify.bind(self));\n\n self.init();\n }\n\n // add styles to element\n // eslint-disable-next-line class-methods-use-this\n css(el, styles) {\n if (typeof styles === 'string') {\n return global.getComputedStyle(el).getPropertyValue(styles);\n }\n\n Object.keys(styles).forEach((key) => {\n el.style[key] = styles[key];\n });\n return el;\n }\n\n // set temporary transition with event listener\n applyTransition($item, properties) {\n const self = this;\n\n // Remove previous event listener\n self.onTransitionEnd($item)();\n\n // Add transitions\n self.css($item, {\n 'transition-property': properties.join(', '),\n 'transition-duration': self.options.transitionDuration,\n });\n\n // Add event listener\n $item.addEventListener('transitionend', self.onTransitionEnd($item, properties), false);\n }\n\n onTransitionEnd($item) {\n const self = this;\n\n return () => {\n self.css($item, {\n 'transition-property': '',\n 'transition-duration': '',\n });\n\n $item.removeEventListener('transitionend', self.onTransitionEnd($item));\n };\n }\n\n // add to fjGallery instances list\n addToFjGalleryList() {\n fjGalleryList.push(this);\n updateFjGallery();\n }\n\n // remove from fjGallery instances list\n removeFromFjGalleryList() {\n const self = this;\n\n fjGalleryList.forEach((item, key) => {\n if (item.instanceID === self.instanceID) {\n fjGalleryList.splice(key, 1);\n }\n });\n }\n\n init() {\n const self = this;\n\n self.appendImages(self.$container.querySelectorAll(self.options.itemSelector));\n\n self.addToFjGalleryList();\n\n // call onInit event\n if (self.options.onInit) {\n self.options.onInit.call(self);\n }\n }\n\n // append images\n appendImages($images) {\n const self = this;\n\n // check if jQuery\n if (global.jQuery && $images instanceof global.jQuery) {\n $images = $images.get();\n }\n\n if (!$images || !$images.length) {\n return;\n }\n\n $images.forEach(($item) => {\n // if $images is jQuery, for some reason in this array there is undefined item, that not a DOM,\n // so we need to check for $item.querySelector.\n if ($item && !$item.fjGalleryImage && $item.querySelector) {\n const $image = $item.querySelector(self.options.imageSelector);\n\n if ($image) {\n $item.fjGalleryImage = self;\n const data = {\n $item,\n $image,\n width: parseFloat($image.getAttribute('width')) || false,\n height: parseFloat($image.getAttribute('height')) || false,\n loadSizes() {\n const itemData = this;\n getImgDimensions($image, (dimensions) => {\n if (itemData.width !== dimensions.width || itemData.height !== dimensions.height) {\n itemData.width = dimensions.width;\n itemData.height = dimensions.height;\n self.resize();\n }\n });\n },\n };\n data.loadSizes();\n\n self.images.push(data);\n }\n }\n });\n\n // call onAppendImages event\n if (self.options.onAppendImages) {\n self.options.onAppendImages.call(self, [$images]);\n }\n\n self.justify();\n }\n\n // justify images\n justify() {\n const self = this;\n const justifyArray = [];\n\n self.justifyCount = (self.justifyCount || 0) + 1;\n\n // call onBeforeJustify event\n if (self.options.onBeforeJustify) {\n self.options.onBeforeJustify.call(self);\n }\n\n self.images.forEach((data) => {\n if (data.width && data.height) {\n justifyArray.push(data.width / data.height);\n }\n });\n\n const justifiedOptions = {\n containerWidth: self.$container.getBoundingClientRect().width,\n containerPadding: {\n top: parseFloat(self.css(self.$container, 'padding-top')) || 0,\n right: parseFloat(self.css(self.$container, 'padding-right')) || 0,\n bottom: parseFloat(self.css(self.$container, 'padding-bottom')) || 0,\n left: parseFloat(self.css(self.$container, 'padding-left')) || 0,\n },\n boxSpacing: self.options.gutter,\n targetRowHeight: self.options.rowHeight,\n targetRowHeightTolerance: self.options.rowHeightTolerance,\n maxNumRows: self.options.maxRowsCount,\n edgeCaseMinRowHeight: self.options.edgeCaseMinRowHeight,\n edgeCaseMaxRowHeight: self.options.edgeCaseMaxRowHeight,\n showWidows: self.options.lastRow !== 'hide',\n };\n const justifiedData = justifiedLayout(justifyArray, justifiedOptions);\n\n // Align last row\n if (\n justifiedData.widowCount &&\n (self.options.lastRow === 'center' || self.options.lastRow === 'right')\n ) {\n const lastItemData = justifiedData.boxes[justifiedData.boxes.length - 1];\n let gapSize = justifiedOptions.containerWidth - lastItemData.width - lastItemData.left;\n\n if (self.options.lastRow === 'center') {\n gapSize /= 2;\n }\n if (self.options.lastRow === 'right') {\n gapSize -= justifiedOptions.containerPadding.right;\n }\n\n for (let i = 1; i <= justifiedData.widowCount; i += 1) {\n justifiedData.boxes[justifiedData.boxes.length - i].left =\n justifiedData.boxes[justifiedData.boxes.length - i].left + gapSize;\n }\n }\n\n // RTL compatibility\n if (self.options.isRtl) {\n justifiedData.boxes.forEach((boxData, i) => {\n justifiedData.boxes[i].left =\n justifiedOptions.containerWidth -\n justifiedData.boxes[i].left -\n justifiedData.boxes[i].width -\n justifiedOptions.containerPadding.right +\n justifiedOptions.containerPadding.left;\n });\n }\n\n let i = 0;\n let additionalTopOffset = 0;\n const rowsMaxHeight = {};\n\n // Set image sizes.\n self.images.forEach((data, imgI) => {\n if (justifiedData.boxes[i] && data.width && data.height) {\n // calculate additional offset based on actual items height.\n if (\n self.options.calculateItemsHeight &&\n typeof rowsMaxHeight[justifiedData.boxes[i].row] === 'undefined' &&\n Object.keys(rowsMaxHeight).length\n ) {\n additionalTopOffset +=\n rowsMaxHeight[Object.keys(rowsMaxHeight).pop()] - justifiedData.boxes[imgI - 1].height;\n }\n\n if (self.options.transitionDuration && self.justifyCount > 1) {\n self.applyTransition(data.$item, ['transform']);\n }\n\n self.css(data.$item, {\n display: '',\n position: 'absolute',\n transform: `translateX(${justifiedData.boxes[i].left}px) translateY(${\n justifiedData.boxes[i].top + additionalTopOffset\n }px) translateZ(0)`,\n width: `${justifiedData.boxes[i].width}px`,\n });\n\n // calculate actual items height.\n if (self.options.calculateItemsHeight) {\n const rect = data.$item.getBoundingClientRect();\n\n if (\n typeof rowsMaxHeight[justifiedData.boxes[i].row] === 'undefined' ||\n rowsMaxHeight[justifiedData.boxes[i].row] < rect.height\n ) {\n rowsMaxHeight[justifiedData.boxes[i].row] = rect.height;\n }\n }\n\n i += 1;\n } else {\n self.css(data.$item, {\n display: 'none',\n });\n }\n });\n\n // increase additional offset based on the latest row items height.\n if (self.options.calculateItemsHeight && Object.keys(rowsMaxHeight).length) {\n additionalTopOffset +=\n rowsMaxHeight[Object.keys(rowsMaxHeight).pop()] -\n justifiedData.boxes[justifiedData.boxes.length - 1].height;\n }\n\n if (self.options.transitionDuration) {\n self.applyTransition(self.$container, ['height']);\n }\n\n // Set container height.\n self.css(self.$container, {\n height: `${justifiedData.containerHeight + additionalTopOffset}px`,\n });\n\n // call onJustify event\n if (self.options.onJustify) {\n self.options.onJustify.call(self);\n }\n }\n\n // update options and resize gallery items\n updateOptions(options) {\n const self = this;\n self.options = {\n ...self.options,\n ...options,\n };\n self.justify();\n }\n\n destroy() {\n const self = this;\n\n self.removeFromFjGalleryList();\n\n self.justifyCount = 0;\n\n // call onDestroy event\n if (self.options.onDestroy) {\n self.options.onDestroy.call(self);\n }\n\n // remove styles.\n self.css(self.$container, {\n height: '',\n transition: '',\n });\n self.images.forEach((data) => {\n self.css(data.$item, {\n position: '',\n transform: '',\n transition: '',\n width: '',\n height: '',\n });\n });\n\n // delete fjGalleryImage instance from images\n self.images.forEach((val) => {\n delete val.$item.fjGalleryImage;\n });\n\n // delete fjGallery instance from container\n delete self.$container.fjGallery;\n }\n\n resize() {\n const self = this;\n\n self.justify();\n }\n}\n\n// global definition\nconst fjGallery = function (items, options, ...args) {\n // check for dom element\n // thanks: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object\n if (\n typeof HTMLElement === 'object'\n ? items instanceof HTMLElement\n : items &&\n typeof items === 'object' &&\n items !== null &&\n items.nodeType === 1 &&\n typeof items.nodeName === 'string'\n ) {\n items = [items];\n }\n\n const len = items.length;\n let k = 0;\n let ret;\n\n for (k; k < len; k += 1) {\n if (typeof options === 'object' || typeof options === 'undefined') {\n if (!items[k].fjGallery) {\n // eslint-disable-next-line new-cap\n items[k].fjGallery = new FJGallery(items[k], options);\n }\n } else if (items[k].fjGallery) {\n // eslint-disable-next-line prefer-spread\n ret = items[k].fjGallery[options].apply(items[k].fjGallery, args);\n }\n if (typeof ret !== 'undefined') {\n return ret;\n }\n }\n\n return items;\n};\nfjGallery.constructor = FJGallery;\n\nexport default fjGallery;\n"],"names":["throttle","delay","callback","options","_ref","_ref$noTrailing","noTrailing","_ref$noLeading","noLeading","_ref$debounceMode","debounceMode","undefined","timeoutID","cancelled","lastExec","clearExistingTimeout","clearTimeout","cancel","_ref2","_ref2$upcomingOnly","upcomingOnly","wrapper","_len","arguments","length","arguments_","Array","_key","self","elapsed","Date","now","exec","apply","clear","setTimeout","rafSchd","fn","lastArgs","frameId","wrapperFn","args","requestAnimationFrame","cancelAnimationFrame","Row","params","index","top","left","width","spacing","targetRowHeight","targetRowHeightTolerance","maxAspectRatio","edgeCaseMaxRowHeight","widowLayoutStyle","isBreakoutRow","items","height","prototype","addItem","itemData","newItems","concat","rowWidthWithoutSpacing","targetAspectRatio","previousRowWidthWithoutSpacing","previousTargetAspectRatio","aspectRatio","push","completeLayout","newAspectRatio","minAspectRatio","Object","assign","reduce","sum","item","Math","abs","previousAspectRatio","newHeight","itemWidthSum","clampedToNativeRatio","clampedHeight","errorWidthPerItem","roundedCumulativeErrors","centerOffset","indexOf","max","edgeCaseMinRowHeight","min","forEach","row","map","i","round","singleItemGeometry","forceComplete","fitToWidth","rowHeight","getItems","ready","document","readyState","addEventListener","capture","once","passive","win","window","global","getImgDimensions","img","cb","interval","hasSize","addedListeners","onHasSize","naturalWidth","naturalHeight","clearInterval","removeListeners","onLoaded","onError","checkSize","addListeners","removeEventListener","setInterval","fjGalleryList","updateFjGallery","resize","domReady","instanceID","FJGallery","constructor","container","userOptions","$container","images","defaults","itemSelector","imageSelector","gutter","rowHeightTolerance","maxRowsCount","Number","POSITIVE_INFINITY","lastRow","transitionDuration","calculateItemsHeight","resizeDebounce","isRtl","css","onInit","onDestroy","onAppendImages","onBeforeJustify","onJustify","dataOptions","dataset","pureDataOptions","keys","key","loweCaseOption","substr","toLowerCase","pureOptions","debounce","justify","bind","init","el","styles","getComputedStyle","getPropertyValue","style","applyTransition","$item","properties","onTransitionEnd","join","addToFjGalleryList","removeFromFjGalleryList","splice","appendImages","querySelectorAll","call","$images","jQuery","get","fjGalleryImage","querySelector","$image","data","parseFloat","getAttribute","loadSizes","dimensions","justifyArray","justifyCount","justifiedOptions","containerWidth","getBoundingClientRect","containerPadding","right","bottom","boxSpacing","maxNumRows","showWidows","justifiedData","justifiedLayout","widowCount","lastItemData","boxes","gapSize","boxData","additionalTopOffset","rowsMaxHeight","imgI","pop","display","position","transform","rect","containerHeight","updateOptions","destroy","transition","val","fjGallery","HTMLElement","nodeType","nodeName","len","k","ret"],"mappings":"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