<?php

/**
 * @file
 * Advanced CSS/JS aggregation js compression module.
 */

/**
 * Default value to see packer is enabled.
 */
define('ADVAGG_JS_COMPRESS_PACKER', FALSE);

/**
 * Default value to see what compressor to use. 0 is Disabled.
 */
define('ADVAGG_JS_COMPRESSOR', 0);

/**
 * Default value to see what compressor to use. 0 is Disabled.
 */
define('ADVAGG_JS_COMPRESS_INLINE', 0);

/**
 * Default value to if inline compression is used if page is not cacheable.
 */
define('ADVAGG_JS_COMPRESS_INLINE_IF_NOT_CACHEABLE', FALSE);

/**
 * Default value for the compression ratio test.
 */
define('ADVAGG_JS_COMPRESS_RATIO', 0.1);

/**
 * Default value for the compression ratio test.
 */
define('ADVAGG_JS_COMPRESS_MAX_RATIO', 0.9);

/**
 * Default value for per file compression settings.
 */
define('ADVAGG_JS_COMPRESSOR_FILE_SETTINGS', -1);

/**
 * Default value to if inline compression is used if page is not cacheable.
 */
define('ADVAGG_JS_COMPRESS_ADD_LICENSE', TRUE);

/**
 * Implements hook_menu().
 */
function advagg_js_compress_menu() {
  $file_path = drupal_get_path('module', 'advagg_js_compress');
  $config_path = advagg_admin_config_root_path();

  $items[$config_path . '/advagg/js-compress'] = array(
    'title' => 'JS Compression',
    'description' => 'Adjust JS Compression settings.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('advagg_js_compress_admin_settings_form'),
    'type' => MENU_LOCAL_TASK,
    'access arguments' => array('administer site configuration'),
    'file path' => $file_path,
    'file' => 'advagg_js_compress.admin.inc',
    'weight' => 10,
  );

  return $items;
}

/**
 * Implements hook_advagg_current_hooks_hash_array_alter().
 */
function advagg_js_compress_advagg_current_hooks_hash_array_alter(&$aggregate_settings) {
  $aggregate_settings['variables']['advagg_js_compressor'] = variable_get('advagg_js_compressor', ADVAGG_JS_COMPRESSOR);
  $aggregate_settings['variables']['advagg_js_compress_packer'] = variable_get('advagg_js_compress_packer', ADVAGG_JS_COMPRESS_PACKER);
  $aggregate_settings['variables']['advagg_js_compress_max_ratio'] = variable_get('advagg_js_compress_max_ratio', ADVAGG_JS_COMPRESS_MAX_RATIO);
  $aggregate_settings['variables']['advagg_js_compressor_file_settings'] = variable_get('advagg_js_compressor_file_settings', array());
}

/**
 * Implements hook_advagg_modify_js_pre_render_alter().
 *
 * Used compress inline js.
 */
function advagg_js_compress_advagg_modify_js_pre_render_alter(&$children, &$elements) {
  // Get variables.
  $aggregate_settings['variables']['advagg_js_compressor'] = variable_get('advagg_js_compress_inline', ADVAGG_JS_COMPRESS_INLINE);
  $aggregate_settings['variables']['advagg_js_compress_max_ratio'] = variable_get('advagg_js_compress_max_ratio', ADVAGG_JS_COMPRESS_MAX_RATIO);

  // Do nothing if the compressor is disabled.
  if (empty($aggregate_settings['variables']['advagg_js_compressor'])) {
    return;
  }

  // Do nothing if the page is not cacheable and inline compress if not
  // cacheable is not checked.
  if (!variable_get('advagg_js_compress_inline_if_not_cacheable', ADVAGG_JS_COMPRESS_INLINE_IF_NOT_CACHEABLE) && !drupal_page_is_cacheable()) {
    return;
  }

  // Compress any inline JS.
  module_load_include('inc', 'advagg_js_compress', 'advagg_js_compress.advagg');
  foreach ($children as &$values) {
    // Compress onload.
    if (!empty($values['#attributes']['onload'])) {
      $contents = $values['#attributes']['onload'];
      $filename = drupal_hash_base64($contents);
      advagg_js_compress_prep($contents, $filename, $aggregate_settings, FALSE);
      $values['#attributes']['onload'] = $contents;
    }

    // Compress onerror.
    if (!empty($values['#attributes']['onerror'])) {
      $contents = $values['#attributes']['onerror'];
      $filename = drupal_hash_base64($contents);
      advagg_js_compress_prep($contents, $filename, $aggregate_settings, FALSE);
      $values['#attributes']['onerror'] = $contents;
    }

    // Compress inline.
    if (!empty($values['#value'])) {
      $contents = $values['#value'];
      $filename = drupal_hash_base64($contents);
      advagg_js_compress_prep($contents, $filename, $aggregate_settings, FALSE);
      $values['#value'] = $contents;
    }
  }
  unset($values);
}

/**
 * Test a file, making sure it is compressible.
 *
 * @param string $filename
 *   Path and filename of the js file to test.
 * @param array $compressors
 *   List of compressors to test.
 * @param string $cache_id
 *   The cache ID for this file.
 *
 * @return array
 *   Array showing the results of the compression tests.
 */
function advagg_js_compress_test_file($filename, array $compressors, $cache_id) {
  $contents = file_get_contents($filename);
  // Get the JS string length before the compression operation.
  $contents_before = $contents;
  $before = strlen($contents);

  module_load_include('inc', 'advagg_js_compress', 'advagg_js_compress.advagg');

  $results = array();
  foreach ($compressors as $key => $name) {
    $contents = $contents_before;
    $aggregate_settings['variables']['advagg_js_compressor'] = $key;
    $aggregate_settings['variables']['advagg_js_compress_max_ratio'] = variable_get('advagg_js_compress_max_ratio', ADVAGG_JS_COMPRESS_MAX_RATIO);

    // Compress it.
    advagg_js_compress_prep($contents, $filename, $aggregate_settings, FALSE, FALSE, FALSE);
    $after = strlen($contents);

    $ratio = 0;
    if ($before != 0) {
      $ratio = ($before - $after) / $before;
    }
    // Set to "-2" if compression ratio sucks (it's already compressed).
    if ($ratio < variable_get('advagg_js_compress_ratio', ADVAGG_JS_COMPRESS_RATIO)) {
      $results[$key] = array(
        'code' => -2,
        'ratio' => round($ratio, 5),
        'name' => $name,
      );
    }
    // Set to "-3" if the compression ratio is way too good (bad js output).
    elseif ($ratio > variable_get('advagg_js_compress_max_ratio', ADVAGG_JS_COMPRESS_MAX_RATIO)) {
      $results[$key] = array(
        'code' => -3,
        'ratio' => round($ratio, 5),
        'name' => $name,
      );
    }
    // Set to "1". Everything worked, mark this file as compressible.
    else {
      $results[$key] = array(
        'code' => 1,
        'ratio' => round($ratio, 5),
        'name' => $name,
      );
    }
  }

  $cache = cache_get($cache_id, 'cache_advagg_info');
  // Merge in old cached data.
  if (!empty($cache->data)) {
    // Do not merge in -1 code.
    foreach ($cache->data as $key => $value) {
      if ($value['code'] == -1) {
        unset($cache->data[$key]);
      }
    }
    $results += $cache->data;
  }

  // CACHE_PERMANENT isn't good here. Use 2 weeks from now + 0-45 days.
  // The random 0 to 45 day addition is to prevent a cache stampeed.
  cache_set($cache_id, $results, 'cache_advagg_info', round(REQUEST_TIME + 1209600 + mt_rand(0, 3888000), -3));

  return $results;
}

/**
 * Generate the js compress configuration.
 *
 * @return array
 *   Array($options, $description, $compressors, $functions).
 */
function advagg_js_compress_configuration() {
  // Set the defaults.
  $description = '';
  $options = array(
    0 => t('Disabled'),
    1 => t('JSMin+ ~1300ms'),
    // 2 => t('Packer ~500ms'),
    // 3 is JSMin c extension.
    // 4 is JShrink.
    // 5 is JSqueeze.
  );
  if (function_exists('jsmin')) {
    $options[3] = t('JSMin ~2ms');
    $description .= t('JSMin is the very fast C complied version. Recommend using it.');
  }
  else {
    if (!defined('PHP_VERSION_ID') || constant('PHP_VERSION_ID') < 50310) {
      $link = 'http://www.ypass.net/software/php_jsmin/';
    }
    else {
      $link = 'https://github.com/sqmk/pecl-jsmin/';
    }
    $description .= t('You can use the much faster C version of JSMin (~2ms) by installing the <a href="@php_jsmin">JSMin PHP Extension</a> on this server.', array('@php_jsmin' => $link));
  }
  // Add in JShrink and JSqueeze if using php 5.3 or higher.
  if (defined('PHP_VERSION_ID') && constant('PHP_VERSION_ID') >= 50300) {
    $options += array(
      4 => t('JShrink ~1000ms'),
      5 => t('JSqueeze ~600ms'),
    );
  }

  $compressors = array(
    1 => 'jsminplus',
    2 => 'packer',
  );
  if (function_exists('jsmin')) {
    $compressors[3] = 'jsmin';
  }
  if (defined('PHP_VERSION_ID') && constant('PHP_VERSION_ID') >= 50300) {
    $compressors += array(
      4 => 'jshrink',
      5 => 'jsqueeze',
    );
  }

  $functions = array(
    1 => 'advagg_js_compress_jsminplus',
    2 => 'advagg_js_compress_jspacker',
    3 => 'advagg_js_compress_jsmin',
    4 => 'advagg_js_compress_jshrink',
    5 => 'advagg_js_compress_jsqueeze',
  );

  // Allow for other modules to alter this list.
  $options_desc = array($options, $description);
  // Call hook_advagg_js_compress_configuration_alter().
  drupal_alter('advagg_js_compress_configuration', $options_desc, $compressors, $functions);
  list($options, $description) = $options_desc;

  return array($options, $description, $compressors, $functions);
}
