You are here

adaptive_image.module in Adaptive Image 7

Adaptive Image - Adaptive images for Drupal

@author Stefan Auditor <stefan.auditor@erdfisch.de>

File

adaptive_image.module
View source
<?php

/**
 * @file
 * Adaptive Image - Adaptive images for Drupal
 * @see http://adaptive-images.com/
 *
 * @author
 * Stefan Auditor <stefan.auditor@erdfisch.de>
 */

/**
 * Implements hook_init().
 */
function adaptive_image_init() {

  // According to the documentation of hook_init() it should not be used to
  // load JS or CSS. The CSS case has been moved to the info file. But the JS is
  // here by intention, as we want it inline to prevent wait time while loading
  // the script
  // No need for drupal behaviours, jquery compatibility wrapper nor ready event
  $js = "document.cookie = 'adaptive_image=' + Math.max(screen.width, screen.height) + '; path=/';";
  drupal_add_js($js, array(
    'type' => 'inline',
    'scope' => 'header',
    'group' => JS_LIBRARY,
    'every_page' => TRUE,
    'weight' => -500,
  ));
}

/**
 * Implements hook_menu().
 */
function adaptive_image_menu() {
  $items = array();

  // Add image style generation paths adaptive URLs.
  if (module_exists('image')) {

    // Generate and deliver image derivatives of public files.
    $directory_path = file_stream_wrapper_get_instance_by_scheme('public')
      ->getDirectoryPath();
    $items[$directory_path . '/styles/%image_style/adaptive-image'] = array(
      'title' => 'Generate image style',
      'page callback' => 'adaptive_image_style_deliver',
      'page arguments' => array(
        count(explode('/', $directory_path)) + 1,
      ),
      'access callback' => TRUE,
      'type' => MENU_CALLBACK,
      'file' => 'adaptive_image.image.inc',
    );

    // Generate and deliver image derivatives of private files.
    $items['system/files/styles/%image_style/adaptive-image'] = array(
      'title' => 'Generate adaptive image style',
      'page callback' => 'adaptive_image_style_deliver',
      'page arguments' => array(
        3,
      ),
      'access callback' => TRUE,
      'type' => MENU_CALLBACK,
      'file' => 'adaptive_image.image.inc',
    );
  }
  return $items;
}

/**
 * Implements hook_image_effect_info().
 */
function adaptive_image_image_effect_info() {
  $effects = array();
  $effects['adaptive_image'] = array(
    'label' => t('Adaptive'),
    'help' => t('Adaptive image scale according to client resolution.'),
    'effect callback' => 'image_scale_effect',
    'dimensions callback' => 'image_scale_dimensions',
    'form callback' => 'adaptive_image_scale_form',
    'summary theme' => 'adaptive_image_scale_summary',
  );
  return $effects;
}

/**
 * Form structure for the image scale form.
 *
 * Note that this is not a complete form, it only contains the portion of the
 * form for configuring the scale options. Therefore it does not not need to
 * include metadata about the effect, nor a submit button.
 *
 * @param $data
 *   The current configuration for this scale effect.
 */
function adaptive_image_scale_form($data) {
  $form['resolutions'] = array(
    '#type' => 'textfield',
    '#title' => t('Resolutions'),
    '#default_value' => isset($data['resolutions']) ? $data['resolutions'] : '1382, 992, 768, 480',
    '#required' => TRUE,
    '#description' => t('The resolution break-points to use (screen widths, in pixels).'),
  );
  $form['mobile_first'] = array(
    '#type' => 'checkbox',
    '#title' => t('Mobile first'),
    '#default_value' => isset($data['mobile_first']) ? $data['mobile_first'] : TRUE,
    '#description' => t("Check this to send the smallest version when the resolution can not be determined."),
  );
  $resolutions = explode(',', str_replace(' ', '', $form['resolutions']['#default_value']));
  $resolution = adaptive_image_resolution($resolutions);

  // Provide needed defaults
  $form['height'] = array(
    '#type' => 'hidden',
    '#default_value' => NULL,
  );
  $form['width'] = array(
    '#type' => 'hidden',
    '#default_value' => $resolution,
  );
  $form['upscale'] = array(
    '#type' => 'hidden',
    '#default_value' => NULL,
  );
  return $form;
}

/**
 * Implements hook_theme().
 */
function adaptive_image_theme() {
  return array(
    'adaptive_image_scale_summary' => array(
      'variables' => array(
        'data' => NULL,
      ),
    ),
  );
}

/**
 * Returns HTML for a summary of an image scale effect.
 *
 * @param $variables
 *   An associative array containing:
 *   - data: The current configuration for this scale effect.
 *
 * @ingroup themeable
 */
function theme_adaptive_image_scale_summary($variables) {
  $data = $variables['data'];
  if ($data['resolutions']) {
    return check_plain($data['resolutions']);
  }
}

/**
 * Validate Input of Resolutions-Field
 */
function adaptive_image_form_image_effect_form_alter(&$form, &$form_state, $form_id) {
  if (strpos($form['#action'], 'adaptive_image')) {
    $form['#validate'] = array(
      '_adaptive_image_form_image_effect_form_validate',
    );
  }
}

/**
 * Form validation handler
 */
function _adaptive_image_form_image_effect_form_validate($form, &$form_state) {
  $resolutions = explode(',', $form['data']['resolutions']['#value']);
  foreach ($resolutions as $width) {
    $width = trim($width, ' ');
    if (!is_numeric($width)) {
      form_set_error('resolutions', t("Values have to be seperated by comma. For example: 1382, 992, 768, 480"));
    }
  }
}

/**
 * Implements template_preprocess_image().
 *
 * Adds a class to adaptive images for max-width.
 */
function adaptive_image_preprocess_image(&$variables) {
  global $base_url;
  if (isset($variables['style_name'])) {

    // Get image style settings
    $style = image_style_load($variables['style_name']);

    // Check if style contains the adaptive image effect
    if ($style && adaptive_image_contains_effect($style)) {
      $settings = adaptive_image_effect_settings($style);
      $resolutions = explode(',', $settings['resolutions']);
      $resolution = adaptive_image_resolution($resolutions);

      // Only construct direct path if not private
      if (!strpos($variables['path'], '/system/') && is_numeric($resolution)) {
        $path_parts = pathinfo($variables['path']);
        $derivative_uri = $path_parts['dirname'] . '/' . $resolution . '/' . $path_parts['basename'];
      }
      if (isset($derivative_uri) && file_exists(str_replace($base_url, '.', $derivative_uri))) {

        // Deliver existing path to bypass menu callback
        $variables['path'] = $derivative_uri;
      }
      else {

        // Reconstruct the image path to %/%style_name/adaptive-image/% to
        // trigger image generation or file access check
        $variables['path'] = str_replace('styles/' . $variables['style_name'], 'styles/' . $variables['style_name'] . '/adaptive-image', $variables['path']);
      }

      // Add class for styling
      $variables['attributes']['class'][] = 'adaptive-image';

      // Remove fixed image dimensions
      unset($variables['height']);
      unset($variables['width']);
    }
  }
}

/**
 * Check for adaptive image effect from style
 */
function adaptive_image_contains_effect($style) {
  foreach ($style['effects'] as $effect) {
    if ($effect['name'] == 'adaptive_image') {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Get adaptive image effect from style settings
 */
function adaptive_image_effect_settings($style) {
  $settings = array();
  foreach ($style['effects'] as $effect) {
    if ($effect['name'] == 'adaptive_image') {
      $settings = $effect['data'];
    }
  }
  return $settings;
}

/**
 * Determine current resolution
 */
function adaptive_image_resolution($resolutions) {
  $resolution = '';

  /* Check to see if a valid cookie exists */
  if (count($resolutions) && isset($_COOKIE['adaptive_image'])) {
    if (is_numeric($_COOKIE['adaptive_image'])) {
      $client_width = (int) $_COOKIE['adaptive_image'];

      // store the cookie value in a variable

      /* the client width in the cookie is valid, now fit that number into the correct resolution break point */
      rsort($resolutions);

      // make sure the supplied break-points are in reverse size order
      $resolution = $resolutions[0];

      // by default it's the largest supported break-point
      foreach ($resolutions as $break_point) {

        // filter down
        if ($client_width <= trim($break_point, ' ')) {
          $resolution = $break_point;
        }
      }
    }
    else {
      setcookie("adaptive_image", "", time() - 1);

      // delete the mangled cookie
    }
  }
  return $resolution;
}

Functions

Namesort descending Description
adaptive_image_contains_effect Check for adaptive image effect from style
adaptive_image_effect_settings Get adaptive image effect from style settings
adaptive_image_form_image_effect_form_alter Validate Input of Resolutions-Field
adaptive_image_image_effect_info Implements hook_image_effect_info().
adaptive_image_init Implements hook_init().
adaptive_image_menu Implements hook_menu().
adaptive_image_preprocess_image Implements template_preprocess_image().
adaptive_image_resolution Determine current resolution
adaptive_image_scale_form Form structure for the image scale form.
adaptive_image_theme Implements hook_theme().
theme_adaptive_image_scale_summary Returns HTML for a summary of an image scale effect.
_adaptive_image_form_image_effect_form_validate Form validation handler