diff --git a/advagg_bundler/advagg_bundler.install b/advagg_bundler/advagg_bundler.install index ad8c182..97893dd 100644 --- a/advagg_bundler/advagg_bundler.install +++ b/advagg_bundler/advagg_bundler.install @@ -36,6 +36,8 @@ 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'); } /** @@ -50,3 +52,78 @@ function advagg_bundler_requirements($phase) { } return $requirements; } + +/** + * Implementation of 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..0946fe4 100644 --- a/advagg_bundler/advagg_bundler.module +++ b/advagg_bundler/advagg_bundler.module @@ -31,6 +31,11 @@ define('ADVAGG_BUNDLER_OUTDATED', 1209600); define('ADVAGG_BUNDLER_ACTIVE', TRUE); /** + * CSS selector limit in a single stylesheet on IE9 and below. + */ +define('SELECTOR_SPLIT_VALUE', 4095); + +/** * Implementation of hook_menu */ function advagg_bundler_menu() { @@ -131,7 +136,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. @@ -251,8 +256,10 @@ function advagg_bundler_analysis($filename = '', $force = FALSE) { * array of requested groups * @param $max * max number of grouping + * @param $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)) { @@ -374,6 +381,101 @@ function advagg_bundler_merge(&$groupings, $max) { $groupings[$merge_candidate_key] = $new_set; } + // 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); + } + // Output Debugging info to watchdog. // watchdog('debug', str_replace(' ', '    ', nl2br(htmlentities(print_r($groupings, TRUE))))); } + +/** + * 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; +}