Skip to content

Instantly share code, notes, and snippets.

@robhawkes
Created July 3, 2020 16:38
Show Gist options
  • Select an option

  • Save robhawkes/80fd4c5f1b44985f8c209f5844ef1402 to your computer and use it in GitHub Desktop.

Select an option

Save robhawkes/80fd4c5f1b44985f8c209f5844ef1402 to your computer and use it in GitHub Desktop.

Revisions

  1. robhawkes created this gist Jul 3, 2020.
    152 changes: 152 additions & 0 deletions img_url.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,152 @@
    // Usage:
    // `{{img_url feature_image}}`
    // `{{img_url profile_image absolute="true"}}`
    // Note:
    // `{{img_url}}` - does not work, argument is required
    //
    // Returns the URL for the current object scope i.e. If inside a post scope will return image permalink
    // `absolute` flag outputs absolute URL, else URL is relative.

    const url = require('url');
    const _ = require('lodash');
    const {urlUtils, logging, i18n} = require('../services/proxy');

    const STATIC_IMAGE_URL_PREFIX = `${urlUtils.STATIC_IMAGE_URL_PREFIX}`;

    module.exports = function imgUrl(requestedImageUrl, options) {
    // CASE: if no url is passed, e.g. `{{img_url}}` we show a warning
    if (arguments.length < 2) {
    logging.warn(i18n.t('warnings.helpers.img_url.attrIsRequired'));
    return;
    }

    // CASE: if url is passed, but it is undefined, then the attribute was
    // an unknown value, e.g. {{img_url feature_img}} and we also show a warning
    if (requestedImageUrl === undefined) {
    logging.warn(i18n.t('warnings.helpers.img_url.attrIsRequired'));
    return;
    }

    // CASE: if you pass e.g. cover_image, but it is not set, then requestedImageUrl is null!
    // in this case we don't show a warning
    if (requestedImageUrl === null) {
    return;
    }

    // CASE: if you pass an external image, there is nothing we want to do to it!
    // ROBIN: Customuised this to modify Cloudinary images
    const isInternalImage = detectInternalImage(requestedImageUrl);
    if (!isInternalImage) {
    return getExternalImage(requestedImageUrl, options);
    }

    const {requestedSize, imageSizes} = getImageSizeOptions(options);
    const absoluteUrlRequested = getAbsoluteOption(options);

    function applyImageSizes(image) {
    return getImageWithSize(image, requestedSize, imageSizes);
    }

    function getImageUrl(image) {
    return urlUtils.urlFor('image', {image}, absoluteUrlRequested);
    }

    function ensureRelativePath(image) {
    return urlUtils.absoluteToRelative(image);
    }

    // CASE: only make paths relative if we didn't get a request for an absolute url
    const maybeEnsureRelativePath = !absoluteUrlRequested ? ensureRelativePath : _.identity;

    return maybeEnsureRelativePath(
    getImageUrl(
    applyImageSizes(requestedImageUrl)
    )
    );
    };

    function getExternalImage(requestedImageUrl, options) {
    if (detectCloudinaryImage) {
    return injectCloudinaryParameters(requestedImageUrl, options);
    }

    return requestedImageUrl;
    }

    function detectCloudinaryImage(requestedImageUrl) {
    return /cloudinary.com\/YOUR_CLOUDINARY_SITE/.test(requestedImageUrl);
    }

    function injectCloudinaryParameters(requestedImageUrl, options) {
    const parameters = options && options.hash && options.hash.cloudinary;

    if (!parameters) {
    return requestedImageUrl;
    }

    const defaultParam = 'YOUR_DEFAULT_CLOUDINARY_TRANSFORM';
    const newParams = [defaultParam, parameters].join('/');

    return requestedImageUrl.replace(defaultParam, newParams);
    }

    function getAbsoluteOption(options) {
    const absoluteOption = options && options.hash && options.hash.absolute;

    return absoluteOption ? !!absoluteOption && absoluteOption !== 'false' : false;
    }

    function getImageSizeOptions(options) {
    const requestedSize = options && options.hash && options.hash.size;
    const imageSizes = options && options.data && options.data.config && options.data.config.image_sizes;

    return {
    requestedSize,
    imageSizes
    };
    }

    function detectInternalImage(requestedImageUrl) {
    const siteUrl = urlUtils.getSiteUrl();
    const isAbsoluteImage = /https?:\/\//.test(requestedImageUrl);
    const isAbsoluteInternalImage = isAbsoluteImage && requestedImageUrl.startsWith(siteUrl);

    // CASE: imagePath is a "protocol relative" url e.g. "//www.gravatar.com/ava..."
    // by resolving the the imagePath relative to the blog url, we can then
    // detect if the imagePath is external, or internal.
    const isRelativeInternalImage = !isAbsoluteImage && url.resolve(siteUrl, requestedImageUrl).startsWith(siteUrl);

    return isAbsoluteInternalImage || isRelativeInternalImage;
    }

    function getImageWithSize(imagePath, requestedSize, imageSizes) {
    const hasLeadingSlash = imagePath[0] === '/';

    if (hasLeadingSlash) {
    return '/' + getImageWithSize(imagePath.slice(1), requestedSize, imageSizes);
    }

    if (!requestedSize) {
    return imagePath;
    }

    if (!imageSizes || !imageSizes[requestedSize]) {
    return imagePath;
    }

    const {width, height} = imageSizes[requestedSize];

    if (!width && !height) {
    return imagePath;
    }

    const [imgBlogUrl, imageName] = imagePath.split(STATIC_IMAGE_URL_PREFIX);

    const sizeDirectoryName = prefixIfPresent('w', width) + prefixIfPresent('h', height);

    return [imgBlogUrl, STATIC_IMAGE_URL_PREFIX, `/size/${sizeDirectoryName}`, imageName].join('');
    }

    function prefixIfPresent(prefix, string) {
    return string ? prefix + string : '';
    }