diff --git includes/common.inc includes/common.inc index 0b4f12a..3070cd5 100644 --- includes/common.inc +++ includes/common.inc @@ -2635,8 +2635,9 @@ function drupal_add_html_head_link($attributes, $header = FALSE) { * (optional) A string defining the 'type' of CSS that is being added in the * $data parameter ('file', 'inline', or 'external'), or an array which can * have any or all of the following keys: - * - 'type': The type of stylesheet being added. Available options are 'file', - * 'inline' or 'external'. Defaults to 'file'. + * - 'type': The type of stylesheet being added. Available options are + * 'library','file', 'inline' or 'external'. Defaults to 'file'. Type + * 'library' is never aggregated and appears above other CSS. * - 'weight': The weight of the stylesheet specifies the order in which the * CSS will appear when presented on the page. Available constants are: * - CSS_SYSTEM: Any system-layer CSS. @@ -2735,7 +2736,7 @@ function drupal_get_css($css = NULL) { // browser-caching. The string changes on every update or full cache // flush, forcing browsers to load a new copy of the files, as the // URL changed. - $query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0, 1); + $default_query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0, 2); // Allow modules to alter the css items. drupal_alter('css', $css); @@ -2771,6 +2772,12 @@ function drupal_get_css($css = NULL) { $external_css = ''; $preprocess_items = array(); foreach ($css as $data => $item) { + if (empty($item['version'])) { + $query_string = $default_query_string; + } + else { + $query_string = '?v=' . $item['version']; + } // Loop through each of the stylesheets, including them appropriately based // on their type. switch ($item['type']) { @@ -2789,6 +2796,12 @@ function drupal_get_css($css = NULL) { $rendered_css['preprocess'] = ''; } break; + case 'library': + $element = $css_element; + $element['#attributes']['media'] = $item['media']; + $element['#attributes']['href'] = file_create_url($item['data']) . $query_string; + $rendered_css[] = theme('html_tag', array('element' => $element)); + break; case 'inline': // Include inline stylesheets. $inline_css .= drupal_load_stylesheet_content($item['data'], $item['preprocess']); @@ -2809,8 +2822,8 @@ function drupal_get_css($css = NULL) { // starting with "ad*". $element = $css_element; $element['#attributes']['media'] = $media; - $filename = 'css_' . md5(serialize($items) . $query_string) . '.css'; - $element['#attributes']['href'] = file_create_url(drupal_build_css_cache($items, $filename)); + $filename = 'css_' . md5(serialize($items)) . '.css'; + $element['#attributes']['href'] = file_create_url(drupal_build_css_cache($items, $filename)) . $default_query_string; $rendered_css['preprocess'] .= theme('html_tag', array('element' => $element)); } } @@ -3164,8 +3177,9 @@ function drupal_region_class($region) { * always pass the string 'setting' only. * - type * The type of JavaScript that is to be added to the page. Allowed - * values are 'file', 'inline', 'external' or 'setting'. Defaults - * to 'file'. + * values are 'library', 'file', 'inline', 'external' or 'setting'. + * Defaults to 'file'. Type 'library' is never aggregated and will + * appear before all JavaScript of type 'file'. * - scope * The location in which you want to place the script. Possible values * are 'header' or 'footer'. If your theme implements different regions, @@ -3199,6 +3213,10 @@ function drupal_region_class($region) { * Aggregate the JavaScript if the JavaScript optimization setting has * been toggled in admin/config/development/performance. Note that * JavaScript of type 'external' is not aggregated. Defaults to TRUE. + * - version + * If not empty, the version is added as a query string insead of the + * incremental query string that changes on cache clearing. Primarily + * used for libraries. * @return * The constructed array of JavaScript files. * @see drupal_get_js() @@ -3239,12 +3257,11 @@ function drupal_add_js($data = NULL, $options = NULL) { ), 'misc/drupal.js' => array( 'data' => 'misc/drupal.js', - 'type' => 'file', + 'type' => 'library', 'scope' => 'header', 'weight' => JS_LIBRARY - 1, 'cache' => TRUE, 'defer' => FALSE, - 'preprocess' => TRUE, ), ); // Register all required libraries. @@ -3263,7 +3280,7 @@ function drupal_add_js($data = NULL, $options = NULL) { $javascript[] = $options; break; - default: // 'file' and 'external' + default: // 'library', 'file' and 'external' // Local and external files must keep their name as the associative key // so the same JavaScript file is not be added twice. $javascript[$options['data']] = $options; @@ -3340,7 +3357,8 @@ function drupal_get_js($scope = 'header', $javascript = NULL) { $output = ''; $preprocessed = ''; - $no_preprocess = "\n"; + $no_preprocess = ''; + $libraries = ''; $files = array(); $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')); $directory = file_directory_path('public'); @@ -3352,7 +3370,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL) { // URL changed. Files that should not be cached (see drupal_add_js()) // get REQUEST_TIME as query-string instead, to enforce reload on every // page request. - $query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0, 1); + $default_query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0, 2); // For inline Javascript to validate as XHTML, all Javascript containing // XHTML needs to be wrapped in CDATA. To make that backwards compatible @@ -3372,6 +3390,12 @@ function drupal_get_js($scope = 'header', $javascript = NULL) { ), ); foreach ($items as $item) { + if (empty($item['version'])) { + $query_string = $default_query_string; + } + else { + $query_string = '?v=' . $item['version']; + } switch ($item['type']) { case 'setting': $js_element = $element; @@ -3406,6 +3430,16 @@ function drupal_get_js($scope = 'header', $javascript = NULL) { } break; + case 'library': + $js_element = $element; + // Preprocessing for JavaScript libraries is ignored. + if ($item['defer']) { + $js_element['#attributes']['defer'] = 'defer'; + } + $js_element['#attributes']['src'] = file_create_url($item['data']) . $query_string; + $libraries .= theme('html_tag', array('element' => $js_element)); + break; + case 'external': $js_element = $element; // Preprocessing for external JavaScript files is ignored. @@ -3422,8 +3456,8 @@ function drupal_get_js($scope = 'header', $javascript = NULL) { if ($is_writable && $preprocess_js && count($files) > 0) { // Prefix filename to prevent blocking by firewalls which reject files // starting with "ad*". - $filename = 'js_' . md5(serialize($files) . $query_string) . '.js'; - $preprocess_file = file_create_url(drupal_build_js_cache($files, $filename)); + $filename = 'js_' . md5(serialize($files)) . '.js'; + $preprocess_file = file_create_url(drupal_build_js_cache($files, $filename)) . $default_query_string; $js_element = $element; $js_element['#attributes']['src'] = $preprocess_file; $preprocessed .= theme('html_tag', array('element' => $js_element)) . "\n"; @@ -3431,7 +3465,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL) { // Keep the order of JS files consistent as some are preprocessed and others are not. // Make sure any inline or JS setting variables appear last after libraries have loaded. - return $preprocessed . $no_preprocess . $output; + return $libraries . $preprocessed . $no_preprocess . $output; } /** @@ -3671,6 +3705,12 @@ function drupal_get_library($module, $name) { if (!empty($libraries[$module][$name]) && is_array($libraries[$module][$name])) { // Add default elements to allow for easier processing. $libraries[$module][$name] += array('dependencies' => array(), 'js' => array(), 'css' => array()); + foreach ($libraries[$module][$name]['js'] as $library => $options) { + $libraries[$module][$name]['js'][$library] += array('type' => 'library', 'version' => $libraries[$module][$name]['version']); + } + foreach ($libraries[$module][$name]['css'] as $library => $options) { + $libraries[$module][$name]['css'][$library] += array('type' => 'library', 'version' => $libraries[$module][$name]['version']); + } } else { $libraries[$module][$name] = FALSE; @@ -5718,8 +5758,8 @@ function drupal_flush_all_caches() { * * Changes the character added to all css/js files as dummy query-string, * so that all browsers are forced to reload fresh files. We keep - * 20 characters history (FIFO) to avoid repeats, but only the first - * (newest) character is actually used on urls, to keep them short. + * 20 characters history (FIFO) to avoid repeats, but only the first two + * (newest) characters are actually used on urls, to keep them short. * This is also called from update.php. */ function _drupal_flush_css_js() { diff --git modules/simpletest/tests/common.test modules/simpletest/tests/common.test index 7f66540..e13c65e 100644 --- modules/simpletest/tests/common.test +++ modules/simpletest/tests/common.test @@ -1168,7 +1168,7 @@ class JavaScriptTestCase extends DrupalWebTestCase { * Test rendering the JavaScript with a file's weight above jQuery's. */ function testRenderDifferentWeight() { - drupal_add_js('misc/collapse.js', array('weight' => JS_LIBRARY - 21)); + drupal_add_js('misc/collapse.js', array('weight' => JS_LIBRARY - 21, 'type' => 'library')); $javascript = drupal_get_js(); $this->assertTrue(strpos($javascript, 'misc/collapse.js') < strpos($javascript, 'misc/jquery.js'), t('Rendering a JavaScript file above jQuery.')); } diff --git modules/system/system.module modules/system/system.module index 5c16f70..1e7adf7 100644 --- modules/system/system.module +++ modules/system/system.module @@ -1090,7 +1090,7 @@ function system_library() { 'website' => 'http://benalman.com/projects/jquery-bbq-plugin/', 'version' => '1.0.2', 'js' => array( - 'misc/jquery.ba-bbq.js', + 'misc/jquery.ba-bbq.js' => array(), ), );