diff --git a/advagg_bundler/advagg_bundler.admin.inc b/advagg_bundler/advagg_bundler.admin.inc index 97e94f3..1f9ec05 100644 --- a/advagg_bundler/advagg_bundler.admin.inc +++ b/advagg_bundler/advagg_bundler.admin.inc @@ -23,10 +23,10 @@ function advagg_bundler_admin_settings_form() { $form = array(); $form['advagg_bundler_active'] = array( - '#type' => 'checkbox', - '#title' => t('Bundler is Active'), - '#default_value' => variable_get('advagg_bundler_active', ADVAGG_BUNDLER_ACTIVE), - '#description' => t('If not checked, the bundler will passively monitor your site, but it will not split up aggregates.'), + '#type' => 'checkbox', + '#title' => t('Bundler is Active'), + '#default_value' => variable_get('advagg_bundler_active', ADVAGG_BUNDLER_ACTIVE), + '#description' => t('If not checked, the bundler will passively monitor your site, but it will not split up aggregates.'), ); $options = array( @@ -43,25 +43,25 @@ function advagg_bundler_admin_settings_form() { 10 => 10, ); $form['advagg_bundler_max_css'] = array( - '#type' => 'select', - '#title' => t('Max Number Of CSS Bundles Per Page'), - '#default_value' => variable_get('advagg_bundler_max_css', ADVAGG_BUNDLER_MAX_CSS), - '#options' => $options, - '#description' => t('If 0 is selected then the bundler is disabled'), + '#type' => 'select', + '#title' => t('Max Number Of CSS Bundles Per Page'), + '#default_value' => variable_get('advagg_bundler_max_css', ADVAGG_BUNDLER_MAX_CSS), + '#options' => $options, + '#description' => t('If 0 is selected then the bundler is disabled'), ); $form['advagg_bundler_max_js'] = array( - '#type' => 'select', - '#title' => t('Max Number Of JS Bundles Per Page'), - '#default_value' => variable_get('advagg_bundler_max_js', ADVAGG_BUNDLER_MAX_JS), - '#options' => $options, - '#description' => t('If 0 is selected then the bundler is disabled'), + '#type' => 'select', + '#title' => t('Max Number Of JS Bundles Per Page'), + '#default_value' => variable_get('advagg_bundler_max_js', ADVAGG_BUNDLER_MAX_JS), + '#options' => $options, + '#description' => t('If 0 is selected then the bundler is disabled'), ); $form['info'] = array( - '#type' => 'fieldset', - '#collapsible' => TRUE, - '#collapsed' => TRUE, - '#title' => t('Raw Grouping Info'), + '#type' => 'fieldset', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#title' => t('Raw Grouping Info'), ); module_load_include('inc', 'advagg', 'advagg.admin'); $analysis = advagg_bundler_analysis('', TRUE); @@ -74,10 +74,10 @@ function advagg_bundler_admin_settings_form() { list($rawtext, $rows) = advagg_form_print_r($data); $form['info']['advagg_bundler_info'] = array( - '#type' => 'textarea', - '#title' => t('%count different groupings', array('%count' => count($data))), - '#default_value' => $rawtext, - '#rows' => $rows -1, + '#type' => 'textarea', + '#title' => t('%count different groupings', array('%count' => count($data))), + '#default_value' => $rawtext, + '#rows' => $rows-1, ); return system_settings_form($form); diff --git a/advagg_bundler/advagg_bundler.info b/advagg_bundler/advagg_bundler.info index 078885a..25f8292 100644 --- a/advagg_bundler/advagg_bundler.info +++ b/advagg_bundler/advagg_bundler.info @@ -3,3 +3,10 @@ description = Provides intelligent bundling of CSS and JS files by grouping file package = Advanced CSS/JS Aggregation core = 6.x dependencies[] = advagg + +; Information added by drupal.org packaging script on 2011-10-26 +version = "6.x-1.6" +core = "6.x" +project = "advagg" +datestamp = "1319670029" + diff --git a/advagg_bundler/advagg_bundler.install b/advagg_bundler/advagg_bundler.install index ad8c182..95430c7 100644 --- a/advagg_bundler/advagg_bundler.install +++ b/advagg_bundler/advagg_bundler.install @@ -6,7 +6,14 @@ */ /** - * Implementation of hook_enable(). + * Implements hook_install(). + */ +function advagg_bundler_install() { + drupal_install_schema('advagg_bundler'); +} + +/** + * Implements hook_enable(). */ function advagg_bundler_enable() { // Flush advagg caches. @@ -17,7 +24,7 @@ function advagg_bundler_enable() { } /** - * Implementation of hook_disable(). + * Implements hook_disable(). */ function advagg_bundler_disable() { // Flush advagg caches. @@ -28,7 +35,7 @@ function advagg_bundler_disable() { } /** - * Implementation of hook_uninstall(). + * Implements hook_uninstall(). */ function advagg_bundler_uninstall() { // Remove variables. @@ -36,17 +43,94 @@ function advagg_bundler_uninstall() { variable_del('advagg_bundler_max_css'); variable_del('advagg_bundler_max_js'); variable_del('advagg_bundler_active'); + + drupal_uninstall_schema('advagg_bundler'); } /** - * Implementation of hook_requirements(). + * Implements hook_requirements(). */ function advagg_bundler_requirements($phase) { $requirements = array(); - // Ensure translations don't break at install time + // Ensure translations don't break at install time. $t = get_t(); if ($phase == 'runtime') { } return $requirements; } + +/** + * Implements hook_schema(). + */ +function advagg_bundler_schema() { + $schema['advagg_bundler_selector_count'] = array( + 'description' => 'Keep track of when ware the files modified.', + 'fields' => array( + 'filename' => array( + 'description' => 'Path of the file relative to Drupal webroot.', + 'type' => 'text', + 'size' => 'normal', + 'not null' => TRUE, + ), + 'filename_md5' => array( + 'description' => 'MD5 hash of filename', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'selector_count' => array( + 'description' => 'CSS selector count of the file.', + 'type' => 'int', + 'not null' => TRUE, + ), + 'timestamp' => array( + 'description' => 'Last modified timestamp of the file.', + 'type' => 'int', + 'not null' => TRUE, + ), + ), + 'primary key' => array('filename_md5'), + ); + + return $schema; +} + +/** + * Add advagg_bundler_selector_count table. + */ +function advagg_bundler_update_6000() { + $schema['advagg_bundler_selector_count'] = array( + 'description' => 'Keep track of when ware the files modified.', + 'fields' => array( + 'filename' => array( + 'description' => 'Path of the file relative to Drupal webroot.', + 'type' => 'text', + 'size' => 'normal', + 'not null' => TRUE, + ), + 'filename_md5' => array( + 'description' => 'MD5 hash of filename', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + 'default' => '', + ), + 'selector_count' => array( + 'description' => 'CSS selector count of the file.', + 'type' => 'int', + 'not null' => TRUE, + ), + 'timestamp' => array( + 'description' => 'Last modified timestamp of the file.', + 'type' => 'int', + 'not null' => TRUE, + ), + ), + 'primary key' => array('filename_md5'), + ); + $ret = array(); + db_create_table($ret, 'advagg_bundler_selector_count', $schema['advagg_bundler_selector_count']); + return $ret; +} diff --git a/advagg_bundler/advagg_bundler.module b/advagg_bundler/advagg_bundler.module index 6d74a0b..51f4c3a 100644 --- a/advagg_bundler/advagg_bundler.module +++ b/advagg_bundler/advagg_bundler.module @@ -31,7 +31,12 @@ define('ADVAGG_BUNDLER_OUTDATED', 1209600); define('ADVAGG_BUNDLER_ACTIVE', TRUE); /** - * Implementation of hook_menu + * CSS selector limit in a single stylesheet on IE9 and below. + */ +define('SELECTOR_SPLIT_VALUE', 4095); + +/** + * Implements hook_menu(). */ function advagg_bundler_menu() { $items = array(); @@ -52,13 +57,12 @@ function advagg_bundler_menu() { } /** - * Implement hook_advagg_filenames_alter. + * Implements hook_advagg_filenames_alter(). */ function advagg_bundler_advagg_filenames_alter(&$filenames) { // Get max number of sub aggregates to create. $max_css = variable_get('advagg_bundler_max_css', ADVAGG_BUNDLER_MAX_CSS); $max_js = variable_get('advagg_bundler_max_js', ADVAGG_BUNDLER_MAX_JS); - $schema = advagg_get_server_schema(); $output = array(); foreach ($filenames as $values) { @@ -66,7 +70,7 @@ function advagg_bundler_advagg_filenames_alter(&$filenames) { $filetype = $values['filetype']; $files = $values['files']; $bundle_md5 = $values['bundle_md5']; - $cached_data_key = 'bundler_' . $schema . '_' . $bundle_md5; + $cached_data_key = 'bundler_' . $bundle_md5; // Try cache first; cache table is cache_advagg_bundle_reuse. $cached_data = advagg_cached_bundle_get($cached_data_key, 'advagg_bundler_filenames_alter'); @@ -131,7 +135,7 @@ function advagg_bundler_advagg_filenames_alter(&$filenames) { // Make sure we didn't go over the max; if we did merge the smallest bundles // together. - advagg_bundler_merge($groupings, $max); + advagg_bundler_merge($groupings, $max, $filetype); // If only one group then don't do any more processing. The merge algorithm // could have reduce the groupings down to one. @@ -146,7 +150,7 @@ function advagg_bundler_advagg_filenames_alter(&$filenames) { $data = array(); foreach ($groupings as $bundle) { $values['files'] = $bundle; - $values['bundle_md5'] = md5($schema . implode('', $bundle)); + $values['bundle_md5'] = md5(implode('', $bundle)); $data[] = $values; $output[] = $values; } @@ -162,12 +166,13 @@ function advagg_bundler_advagg_filenames_alter(&$filenames) { /** * Given a filename return a bundle key. * - * @param $filename + * @param string $filename * filename - * @param $force + * @param bool $force * bypass the cache and get a fresh version of the analysis. - * @return - * string to be used for the grouping key. + * + * @return string + * Value to be used for the grouping key. */ function advagg_bundler_analysis($filename = '', $force = FALSE) { // Cache query in a static. @@ -196,7 +201,7 @@ function advagg_bundler_analysis($filename = '', $force = FALSE) { filename FROM ( SELECT - LPAD(CAST(COUNT(*) AS char(8)), 8, '0') AS count, + LPAD(COUNT(*), 8, '00000000') AS count, bundle_md5, filename_md5 FROM @@ -247,12 +252,14 @@ function advagg_bundler_analysis($filename = '', $force = FALSE) { * * This preserves the order. * - * @param $groupings + * @param array $groupings * array of requested groups - * @param $max + * @param int $max * max number of grouping + * @param string $filetype + * String with the file type: css or js */ -function advagg_bundler_merge(&$groupings, $max) { +function advagg_bundler_merge(&$groupings, $max, $filetype) { $group_count = count($groupings); if (!empty($max)) { @@ -304,9 +311,7 @@ function advagg_bundler_merge(&$groupings, $max) { } } -// watchdog('debug', $first . "
\n" . $last . "
\n" . str_replace(' ', '    ', nl2br(htmlentities(print_r(array($groupings, $map, $counts), TRUE))))); - - // Create the new merged set + // Create the new merged set. $a = $groupings[$first]; $b = $groupings[$last]; $new_set = array_merge($a, $b); @@ -374,6 +379,98 @@ function advagg_bundler_merge(&$groupings, $max) { $groupings[$merge_candidate_key] = $new_set; } - // Output Debugging info to watchdog. - // watchdog('debug', str_replace(' ', '    ', nl2br(htmlentities(print_r($groupings, TRUE))))); + // Prevent CSS selectors exceeding 4095 due to limits with IE9 and below. + if ($filetype == 'css') { + + // Check each group to see if it exceeds the selector limit. + do { + $groupings_edited = FALSE; + foreach ($groupings as $key => $group) { + + // Restart the selector limit check if the grouping was edited. + if ($groupings_edited) { + break; + } + + $group_selector_counter = 0; + + $selector_counts = advagg_bundler_get_css_selector_count($group); + + for ($i = 0; $i < count($group) && !$groupings_edited; $i++) { + + $selector_count = isset($selector_counts[$group[$i]]) ? $selector_counts[$group[$i]] : 0; + + if ($group_selector_counter + $selector_count > SELECTOR_SPLIT_VALUE) { + $groupings_edited = TRUE; + + // Divide the group. + $first_group = array_splice($group, 0, $i); + $second_group = array_splice($group, 0); + + // Rebuild the array with the new set in the correct place. + $new_groupings = array(); + foreach ($groupings as $k => $files) { + if ($k == $key) { + $new_groupings[$k . '_1'] = $first_group; + $new_groupings[$k . '_2'] = $second_group; + } + else { + $new_groupings[$k] = $files; + } + } + $groupings = $new_groupings; + } + else { + $group_selector_counter += $selector_count; + } + } + } + } while ($groupings_edited); + } +} + +/** + * Gets the selector count of the provided files. + * + * @param array $files + * Array of files to use. + * + * @return array + * The selector counts of each file. + */ +function advagg_bundler_get_css_selector_count($files) { + $results = array(); + $placeholders = db_placeholders($files); + $result = db_query("SELECT filename, selector_count, timestamp FROM {advagg_bundler_selector_count} WHERE filename IN ($placeholders)", $files); + + while ($row = db_fetch_array($result)) { + $modified = filemtime($row['filename']); + if ($modified > $row['timestamp']) { + $css = advagg_build_css_bundle(array($row['filename']), TRUE); + + // Get the number of selectors. + // http://stackoverflow.com/questions/12567000/regex-matching-for-counting-css-selectors/12567381#12567381 + $selector_count = preg_match_all('/\{.+?\}|,/s', $css, $matched); + + db_query("UPDATE {advagg_bundler_selector_count} SET timestamp = %d, selector_count = %d WHERE filename LIKE '%s'", $modified, $selector_count, $row['filename']); + + $results[$row['filename']] = $selector_count; + } + else { + $results[$row['filename']] = $row['selector_count']; + } + } + + foreach ($files as $file) { + if (!isset($results[$file]) && file_exists($file)) { + $css = advagg_build_css_bundle(array($file), TRUE); + $selector_count = preg_match_all('/\{.+?\}|,/s', $css, $matched); + + db_query("INSERT INTO {advagg_bundler_selector_count} VALUES('%s', '%s', %d, %d)", $file, md5($file), $selector_count, filemtime($file)); + + $results[$file] = $selector_count; + } + } + + return $results; }