You are here

minify.module in Minify 7

File

minify.module
View source
<?php

/**
 * Implements hook_help().
 */
function minify_help($path, $arg) {
  switch ($path) {
    case 'admin/config/development/performance/minifyjs':
      $output = '<p>' . t('This is a simple module that provides the mechanism to minify JavaScript files, ');
      $output .= t('which ultimately reduces the file size and load time.') . '</p>';
      $output .= '<p>' . t('In <b>Operations</b> column,<br /><b>Minify</b> => Generate the minified version,<br />');
      $output .= t('<b>Refresh</b> => Refresh/regenerate the minified version,<br />');
      $output .= t('<b>Revert</b> => Delete the minified file and use original file.') . '</p>';
      $output .= '<p>' . t('In <b>Status</b> column,<br />');
      $output .= t('<span class="marker">Need to refresh</span> => Original file is updated, click Refresh to update minified file.') . '</p>';
      return $output;
  }
}

/**
 * Implements hook_menu()
 */
function minify_menu() {
  $items['admin/config/development/performance/default'] = array(
    'title' => 'Performance',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 1,
  );
  $items['admin/config/development/performance/minifyjs'] = array(
    'title' => 'Minify JavaScript files',
    'access arguments' => array(
      'administer minify',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'minify_js_callback',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 2,
    'file' => 'minify.admin.inc',
  );
  $items['admin/config/development/performance/minifyjs/revert'] = array(
    'title' => 'Revert Minify JS',
    'access arguments' => array(
      'administer minify',
    ),
    'page callback' => '_minify_js_revert_callback',
    'file' => 'minify.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['admin/config/development/performance/minifyjs/refresh'] = array(
    'title' => 'Minify JS',
    'access arguments' => array(
      'administer minify',
    ),
    'page callback' => '_minify_js_refresh_callback',
    'file' => 'minify.admin.inc',
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_permission()
 */
function minify_permission() {
  return array(
    'administer minify' => array(
      'title' => t('Administer minify'),
      'description' => t('Perform administration tasks for minify module.'),
    ),
  );
}

/**
 * Implements hook_FORM_ID_alter().
 *
 * @param type $form
 * @param type $form_state
 */
function minify_form_system_performance_settings_alter(&$form, &$form_state, $form_id) {
  if (user_access('administer minify')) {
    $m_cnt = 0;
    $cache = variable_get('minify_captured_js', array());
    foreach ($cache as $key => $value) {
      if ($cache[$key]['status']) {
        $m_cnt++;
      }
    }
    $description = t('Select <b>Minify JavaScript files</b> tab at top of the page or ') . l('Click here', 'admin/config/development/performance/minifyjs') . t(' to manage minified JavaScript files.') . t('<br />Out of <b>@cnt</b> only <b>@m_cnt</b> JavaScript files are minified.', array(
      '@cnt' => count($cache),
      '@m_cnt' => $m_cnt,
    ));
    $form['bandwidth_optimization']['minify_html'] = array(
      '#type' => 'checkbox',
      '#title' => t('Minify HTML.'),
      '#default_value' => intval(variable_get('minify_html', 0)),
    );
    $form['bandwidth_optimization']['minify_js'] = array(
      '#type' => 'checkbox',
      '#title' => t('Use Minified JavaScript files.'),
      '#description' => $description,
      '#default_value' => intval(variable_get('minify_js', 0)),
    );
  }
}

/**
 * Implements hook_process_html()
 */
function minify_process_html(&$variables) {
  global $theme;
  $theme_default = variable_get('theme_default', null);

  /* Check Monify HTML is selected and it is for front end theme */
  if (intval(variable_get('minify_html', 0)) && $theme == $theme_default) {

    /* Constant used as replacement tag key in global placeholders array */
    define('REPLACEMENT_HASH', 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']));

    /* Get active theme regions */
    $visible_theme_region = system_region_list($theme_default, REGIONS_ALL);

    /* Add page region to array */
    $visible_theme_region = array_merge(array(
      'page' => 'Page',
      'page_top' => 'Page top',
      'page_bottom' => 'Page bottom',
    ), $visible_theme_region);

    /* Prase and check if region is exist then minify HTML */
    foreach ($visible_theme_region as $key => $value) {
      if (isset($variables[$key])) {
        $variables[$key] = _minify_html($variables[$key]);
      }
    }
  }
}

/**
 * Helper function to minify HTML
 */
function _minify_html($buffer) {

  /* Replace <textarea> with placeholder */
  $buffer = preg_replace_callback('/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i', '_minify_html_callback', $buffer);

  /* Replace <pre> with placeholder */
  $buffer = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i', '_minify_html_callback', $buffer);

  /* Replace <iframe> with placeholder */
  $buffer = preg_replace_callback('/\\s*<iframe(\\b[^>]*?>[\\s\\S]*?<\\/iframe>)\\s*/i', '_minify_html_iframe_callback', $buffer);

  /* Replace <script> with placeholder */
  $buffer = preg_replace_callback('/\\s*<script(\\b[^>]*?>[\\s\\S]*?<\\/script>)\\s*/i', '_minify_html_script_callback', $buffer);

  /* Replace <style> with placeholder */
  $buffer = preg_replace_callback('/\\s*<style(\\b[^>]*?>[\\s\\S]*?<\\/style>)\\s*/i', '_minify_html_style_callback', $buffer);

  /* Remove HTML comment */
  $buffer = preg_replace_callback('/<!--([\\s\\S]*?)-->/', '_minify_html_html_comment', $buffer);
  $search = array(
    '/\\>[^\\S ]+/s',
    // remove whitespaces after tags, except space
    '/[^\\S ]+\\</s',
    // remove whitespaces before tags, except space
    '/(\\s)+/s',
    // shorten multiple whitespace sequences
    '/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' . '|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' . '|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' . '|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' . '|ul)\\b[^>]*>)/i',
    // remove whitespaces around block/undisplayed elements
    '/^\\s+|\\s+$/m',
  );
  $replace = array(
    '>',
    // remove whitespaces after tags, except space
    '<',
    // remove whitespaces before tags, except space
    '\\1',
    // shorten multiple whitespace sequences
    '$1',
    // remove whitespaces around block/undisplayed elements
    '',
  );
  $buffer = preg_replace($search, $replace, $buffer);

  /* Find and replace <textarea>, <pre>, <iframe>, <script> and <style> place holders values */
  global $placeholders;
  if (!empty($placeholders)) {
    $buffer = str_replace(array_keys($placeholders), array_values($placeholders), $buffer);
  }
  return $buffer;
}

/**
 * Remove HTML comments (not containing IE conditional comments).
 */
function _minify_html_html_comment($string) {
  return 0 === strpos($string[1], '[') || false !== strpos($string[1], '<![') ? $string[0] : '';
}

/*
 * Helper function to add place holder for <textarea> and <pre> tag
 */
function _minify_html_callback($m) {
  return _minify_reserve_place($m[0]);
}

/*
 * Helper function to add place holder for <iframe> tag
 */
function _minify_html_iframe_callback($m) {
  $iframe = preg_replace('/^\\s+|\\s+$/m', '', $m[0]);
  return _minify_reserve_place($iframe);
}

/*
 * Helper function to add place holder for <script> tag
 */
function _minify_html_script_callback($m) {
  $search = array(
    '!/\\*.*?\\*/!s',
    // remove multi-line comment
    '/^\\s+|\\s+$/m',
    // trim each line
    '/\\n(\\s*\\n)+/',
  );
  $replace = array(
    '',
    "\n",
    "\n",
  );
  $script = preg_replace($search, $replace, $m[0]);
  return _minify_reserve_place($script);
}

/*
 * Helper function to add place holder for <style> tag
 */
function _minify_html_style_callback($m) {
  $search = array(
    '!/\\*.*?\\*/!s',
    // remove multi-line comment
    '/^\\s+|\\s+$/m',
  );
  $replace = array(
    '',
  );
  $style = preg_replace($search, $replace, $m[0]);
  return _minify_reserve_place($style);
}

/*
 * Helper function to add tag key and value for further replacement
 */
function _minify_reserve_place($content) {
  global $placeholders;
  $placeholder_count = is_array($placeholders) ? count($placeholders) : 0;
  $placeholder = '%' . REPLACEMENT_HASH . $placeholder_count . '%';
  $placeholders[$placeholder] = $content;
  return $placeholder;
}

/**
 * Implements hook_js_alter().
 */
function minify_js_alter(&$scripts) {
  $cache = variable_get('minify_captured_js', array());
  $updated = false;
  foreach ($scripts as $file_path => $file_details) {
    if ('file' == $file_details['type'] && is_file(drupal_realpath($file_path))) {

      /* Set JavaScript entry into cache array if it not exist in cache */
      if (!_minify_javascript_exist($file_path, $cache)) {
        $updated = true;
        $cache[$file_path]['file_path'] = $file_path;
        $cache[$file_path]['file_name'] = drupal_basename($file_path);
        $cache[$file_path]['version'] = isset($file_details['version']) ? $file_details['version'] : '';
        $cache[$file_path]['minified_file_path'] = null;
        $cache[$file_path]['minified_size'] = 0;
        $cache[$file_path]['status'] = false;
        $cache[$file_path]['last_minify_at'] = 0;
        $cache[$file_path]['error'] = false;
        $cache[$file_path]['error_msg'] = null;
        $cache[$file_path]['skip'] = false;
        $cache[$file_path]['md5'] = md5_file(drupal_realpath($file_path));
      }
    }
  }

  /* If any change in existing array update cache */
  if ($updated) {
    variable_set('minify_captured_js', $cache);
  }
  if (intval(variable_get('minify_js', 0))) {

    /* Replace the JavaScript path by minified path into $scripts without changing the order */
    foreach ($cache as $key => $value) {
      if (isset($scripts[$key]) && $value['status']) {
        $scripts[$key]['data'] = $value['minified_file_path'];
        $scripts = _minify_replace_array_key($scripts, $key, $value['minified_file_path']);
      }
    }
  }
}

/*
 * Helper function to check wheather JavaScript entry is exist into given array
 *
 * @param $search_value
 *  JavaScript file name to search into array
 *
 * @param $cache
 *  File array to search JavaScript 
 */
function _minify_javascript_exist($search_value, $cache) {
  return array_key_exists($search_value, $cache);
}

/*
 * Helper function to change array key, without changing its order
 */
function _minify_replace_array_key($array, $old_key, $new_key) {
  $keys = array_keys($array);
  $index = array_search($old_key, $keys);
  if ($index !== false) {
    $keys[$index] = $new_key;
    $array = array_combine($keys, $array);
  }
  return $array;
}

/**
 * Implements hook_cron().
 */
function minify_cron() {
  module_load_include('inc', 'minify', 'minify.admin');
  _minify_clear_invalid_cache();
}

Functions