{"version":3,"file":"retina.js","sources":["../src/retina.js"],"sourcesContent":["/* @flow */\n\n/**\n * --------------------------------------------------------------------------\n * Retina.js\n * Licensed under MIT (https://github.com/strues/retinajs/blob/master/LICENSE)\n *\n * Retina.js is an open source script that makes it easy to serve high-resolution\n * images to devices with retina displays.\n * --------------------------------------------------------------------------\n */\n\n/*\n * Determine whether or not `window` is available.\n */\nconst hasWindow = typeof window !== 'undefined';\n\n/*\n * Get the device pixel ratio per our environment.\n * Default to 1.\n */\nconst environment = Math.round(hasWindow ? window.devicePixelRatio || 1 : 1);\n\n/*\n * Define a pattern for capturing src url suffixes.\n */\nconst srcReplace = /(\\.[A-z]{3,4}\\/?(\\?.*)?)$/;\nconst inlineReplace = /url\\(('|\")?([^)'\"]+)('|\")?\\)/i;\n\n/*\n * Define our selectors for elements to target.\n */\nconst selector = '[data-rjs]';\n\n/*\n * Define the attribute we'll use to mark an image as having been processed.\n */\nconst processedAttr = 'data-rjs-processed';\n\n/**\n * Shortcut for turning some iterable object into an array.\n *\n * @param {Iterable} object Any iterable object.\n *\n * @return {Array}\n */\nfunction arrayify(object) {\n return Array.prototype.slice.call(object);\n}\n\n/**\n * Chooses the actual image size to fetch, (for example 2 or 3) that\n * will be used to create a suffix like \"@2x\" or \"@3x\".\n *\n * @param {String|Number} cap The number the user provided indicating that\n * they have prepared images up to this size.\n *\n * @return {Number} The number we'll be using to create a suffix.\n */\nfunction chooseCap(cap) {\n const numericCap = parseInt(cap, 10);\n\n /*\n * If the environment's device pixel ratio is less than what the user\n * provided, we'll only grab images at that size.\n */\n if (environment < numericCap) {\n return environment;\n\n /*\n * If the device pixel ratio is greater than or equal to what the\n * user provided, we'll use what the user provided.\n */\n } else {\n return numericCap;\n }\n}\n\n/**\n * Makes sure that, since we are going to swap out the source of an image,\n * the image does not change size on the page.\n *\n * @param {Element} image An image element in the DOM.\n *\n * @return {Element} The same element that was passed in.\n */\nfunction forceOriginalDimensions(image) {\n if (!image.hasAttribute('data-no-resize')) {\n if (image.offsetWidth === 0 && image.offsetHeight === 0) {\n image.setAttribute('width', image.naturalWidth);\n image.setAttribute('height', image.naturalHeight);\n } else {\n image.setAttribute('width', image.offsetWidth);\n image.setAttribute('height', image.offsetHeight);\n }\n }\n return image;\n}\n\n/**\n * Determines whether the retina image actually exists on the server.\n * If so, swaps out the retina image for the standard one. If not,\n * leaves the original image alone.\n *\n * @param {Element} image An image element in the DOM.\n * @param {String} newSrc The url to the retina image.\n *\n * @return {undefined}\n */\nfunction setSourceIfAvailable(image, retinaURL) {\n const imgType = image.nodeName.toLowerCase();\n\n /*\n * Create a new image element and give it a load listener. When the\n * load listener fires, it means the URL is correct and we will then\n * attach it to the user's image.\n */\n const testImage = document.createElement('img');\n testImage.addEventListener('load', () => {\n /*\n * If we're dealing with an image tag, force it's dimensions\n * and set the source attribute. If not, go after the background-image\n * inline style.\n */\n if (imgType === 'img') {\n forceOriginalDimensions(image).setAttribute('src', retinaURL);\n } else {\n image.style.backgroundImage = `url(${retinaURL})`;\n }\n });\n\n /*\n * Attach the retina URL to our proxy image to load in the new\n * image resource.\n */\n testImage.setAttribute('src', retinaURL);\n\n /*\n * Mark our image as processed so that it won't be processed again.\n */\n image.setAttribute(processedAttr, true);\n}\n\n/**\n * Attempts to do an image url swap on a given image.\n *\n * @param {Element} image An image in the DOM.\n * @param {String} src The original image source attribute.\n * @param {String|Number} rjs The pixel density cap for images provided.\n *\n * @return {undefined}\n */\nfunction dynamicSwapImage(image, src, rjs = 1) {\n const cap = chooseCap(rjs);\n\n /*\n * Don't do anything if the cap is less than 2 or there is no src.\n */\n if (src && cap > 1) {\n const newSrc = src.replace(srcReplace, `@${cap}x$1`);\n setSourceIfAvailable(image, newSrc);\n }\n}\n\n/**\n * Performs an image url swap on a given image with a provided url.\n *\n * @param {Element} image An image in the DOM.\n * @param {String} src The original image source attribute.\n * @param {String} hdsrc The path for a 2x image.\n *\n * @return {undefined}\n */\nfunction manualSwapImage(image, src, hdsrc) {\n if (environment > 1) {\n setSourceIfAvailable(image, hdsrc);\n }\n}\n\n/**\n * Collects all images matching our selector, and converts our\n * NodeList into an Array so that Array methods will be available to it.\n *\n * @param {Iterable} images Optional. An Array, jQuery selection, or NodeList\n * of elements to affect with retina.js.\n *\n * @return {Iterable} Contains all elements matching our selector.\n */\nfunction getImages(images) {\n if (!images) {\n return typeof document !== 'undefined' ? arrayify(document.querySelectorAll(selector)) : [];\n } else {\n return typeof images.forEach === 'function' ? images : arrayify(images);\n }\n}\n\n/**\n * Converts a string like \"url(hello.png)\" into \"hello.png\".\n *\n * @param {Element} img An HTML element with a background image.\n *\n * @return {String}\n */\nfunction cleanBgImg(img) {\n return img.style.backgroundImage.replace(inlineReplace, '$2');\n}\n\n/**\n * Gets all participating images and dynamically swaps out each one for its\n * retina equivalent taking into account the environment capabilities and\n * the densities for which the user has provided images.\n *\n * @param {Iterable} images Optional. An Array, jQuery selection, or NodeList\n * of elements to affect with retina.js. If not\n * provided, retina.js will grab all images on the\n * page.\n *\n * @return {undefined}\n */\nfunction retina(images) {\n getImages(images).forEach(img => {\n if (!img.getAttribute(processedAttr)) {\n const isImg = img.nodeName.toLowerCase() === 'img';\n const src = isImg ? img.getAttribute('src') : cleanBgImg(img);\n const rjs = img.getAttribute('data-rjs');\n const rjsIsNumber = !isNaN(parseInt(rjs, 10));\n\n // do not try to load /null image!\n if (rjs === null) {\n return;\n }\n\n /*\n * If the user provided a number, dynamically swap out the image.\n * If the user provided a url, do it manually.\n */\n if (rjsIsNumber) {\n dynamicSwapImage(img, src, rjs);\n } else {\n manualSwapImage(img, src, rjs);\n }\n }\n });\n}\n\n/*\n * If this environment has `window`, activate the plugin.\n */\nif (hasWindow) {\n window.addEventListener('load', () => {\n retina();\n });\n window.retinajs = retina;\n}\n\nexport default retina;\n"],"names":["hasWindow","window","environment","Math","round","devicePixelRatio","srcReplace","inlineReplace","selector","processedAttr","arrayify","object","Array","prototype","slice","call","chooseCap","cap","numericCap","parseInt","forceOriginalDimensions","image","hasAttribute","offsetWidth","offsetHeight","setAttribute","naturalWidth","naturalHeight","setSourceIfAvailable","retinaURL","imgType","nodeName","toLowerCase","testImage","document","createElement","addEventListener","style","backgroundImage","dynamicSwapImage","src","rjs","newSrc","replace","manualSwapImage","hdsrc","getImages","images","querySelectorAll","forEach","cleanBgImg","img","retina","getAttribute","isImg","rjsIsNumber","isNaN","retinajs"],"mappings":"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"}