diff --git a/libraries.api.php b/libraries.api.php index 1182f7b..910ec6c 100644 --- a/libraries.api.php +++ b/libraries.api.php @@ -114,9 +114,11 @@ * library with a certain part of the libraries array passed as $library * each time. * - $version: If the $library array belongs to a certain version (see - * above), a string containing the version. NULL, otherwise. + * above), a string containing the version. This argument may be empty, so + * NULL should be specified as default value. * - $variant: If the $library array belongs to a certain variant (see - * above), a string containing the variant name. NULL, otherwise. + * above), a string containing the variant name. This argument may be + * empty, so NULL should be specified as default value. * Valid callback groups are: * - info: Callbacks registered in this group are applied after the library * information has been retrieved via hook_libraries_info() or info files. diff --git a/libraries.module b/libraries.module index 2255b4a..f39e790 100644 --- a/libraries.module +++ b/libraries.module @@ -180,12 +180,12 @@ function libraries_invoke($group, &$library) { */ function libraries_traverse_library(&$library, $callback) { // Always apply the callback to the top-level library. - $callback($library, NULL, NULL); + $callback($library); // Apply the callback to versions. if (isset($library['versions'])) { foreach ($library['versions'] as $version_string => &$version) { - $callback($version, $version_string, NULL); + $callback($version, $version_string); // Versions can include variants as well. if (isset($version['variants'])) { foreach ($version['variants'] as $version_variant_name => &$version_variant) { @@ -204,6 +204,89 @@ function libraries_traverse_library(&$library, $callback) { } /** +* Helper callback to make 'files' property of libraries consistent. +* +* This turns libraries' file information declared as e.g. +* @code +* $library['files']['js'] = array('example_1.js', 'example_2.js'); +* @endcode +* into +* @code +* $library['files']['js'] = array( +* 'example_1.js' => array(), +* 'example_2.js' => array(), +* ); +* @endcode +* It does the same for the 'integration files' property. +* +* @param $library +* An associative array of library information or a part of it, passed by +* reference. +* @param $version +* If the library information belongs to a specific version, the version +* string. NULL otherwise. +* @param $variant +* If the library information belongs to a specific variant, the variant name. +* NULL otherwise. +* +* @see libraries_info() +* @see libraries_invoke() +*/ +function libraries_prepare_files(&$library, $version = NULL, $variant = NULL) { + // Both the 'files' property and the 'integration files' property contain file + // declarations, and we want to make both consistent. + $file_types = array(); + if (isset($library['files'])) { + $file_types[] = &$library['files']; + } + if (isset($library['integration files'])) { + // Integration files are additionally keyed by module. + foreach ($library['integration files'] as &$integration_files) { + $file_types[] = &$integration_files; + } + } + foreach ($file_types as &$files) { + // Go through all supported types of files. + foreach (array('js', 'css', 'php') as $type) { + if (isset($files[$type])) { + foreach ($files[$type] as $key => $value) { + // Entries with explicit (i.e., non-integer) keys, are assumed tto be + // fine the way they are. + if (is_int($key)) { + // JavaScript settings. + if ($type == 'js' && is_array($value)) { + $files[$type][$key] = array( + 'type' => 'setting', + 'data' => $value, + ); + } + else { + // External files. + if (url_is_external($value)) { + $files[$type][$value] = array('type' => 'external'); + } + // Local files. + elseif(!preg_match('$[^a-zA-Z0-9-_.\/]$', $value)) { + $files[$type][$value] = array('type' => 'file'); + } + // Inline JavaScript and CSS. + elseif ($type == 'js' || $type == 'css') { + $files[$type][$value] = array('type' => 'inline'); + } + unset($files[$type][$key]); + } + // Apply the default group if the group isn't explicitly given. + if (($type == 'js' || $type == 'css') && !isset($options['group'])) { + $options['group'] = ($type == 'js') ? JS_DEFAULT : CSS_DEFAULT; + } + } + } + } + } + } +} + +/** * Returns information about registered libraries. * * The returned information is unprocessed, i.e. as registered by modules. @@ -292,6 +375,10 @@ function libraries_info_defaults(&$library, $name) { 'post-detect' => array(), 'load' => array(), ); + + // Add our own callbacks before any others. + $library['callbacks']['info'] = array_merge(array('libraries_prepare_files'), $library['callbacks']['info']); + return $library; } @@ -522,23 +609,12 @@ function libraries_load_files($library) { foreach (array('js', 'css') as $type) { if (!empty($library['files'][$type])) { foreach ($library['files'][$type] as $data => $options) { - // If the value is not an array, it's a filename and passed as first - // (and only) argument. - if (!is_array($options)) { - // Prepend the library path to the file name. - $data = "$path/$options"; - $options = NULL; + if ($options['type'] == 'file') { + $data = "$path/$data"; } - // In some cases, the first parameter ($data) is an array. Arrays can't - // be passed as keys in PHP, so we have to get $data from the value - // array. - if (is_numeric($data)) { + if ($options['type'] == 'setting') { $data = $options['data']; - unset($options['data']); - } - // Apply the default group if the group isn't explicitly given. - if (!isset($options['group'])) { - $options['group'] = ($type == 'js') ? JS_DEFAULT : CSS_DEFAULT; + $options = 'setting'; } call_user_func('drupal_add_' . $type, $data, $options); $count++; @@ -548,7 +624,7 @@ function libraries_load_files($library) { // Load PHP files. if (!empty($library['files']['php'])) { - foreach ($library['files']['php'] as $file) { + foreach ($library['files']['php'] as $file => $array) { $file_path = DRUPAL_ROOT . '/' . $path . '/' . $file; if (file_exists($file_path)) { require_once $file_path; diff --git a/tests/libraries.test b/tests/libraries.test index d70a10e..2a2e2cf 100644 --- a/tests/libraries.test +++ b/tests/libraries.test @@ -32,15 +32,33 @@ class LibrariesTestCase extends DrupalWebTestCase { // Test libraries_get_path(). $this->assertEqual(libraries_get_path('example'), FALSE, 'libraries_get_path() returns FALSE for a missing library.'); + // Test libraries_prepare_files(). + $expected = array( + 'files' => array( + 'js' => array('example.js' => array()), + 'css' => array('example.css' => array()), + 'php' => array('example.php' => array()), + ), + ); + $library = array( + 'files' => array( + 'js' => array('example.js'), + 'css' => array('example.css'), + 'php' => array('example.php'), + ), + ); + libraries_prepare_files($library, NULL, NULL); + $this->assertEqual($expected, $library, 'libraries_prepare_files() works correctly.'); + // Test that library information is found correctly. $expected = array( 'name' => 'Example files', 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', 'version' => '1', 'files' => array( - 'js' => array('example_1.js'), - 'css' => array('example_1.css'), - 'php' => array('example_1.php'), + 'js' => array('example_1.js' => array()), + 'css' => array('example_1.css' => array()), + 'php' => array('example_1.php' => array()), ), 'module' => 'libraries_test', ); @@ -107,9 +125,9 @@ class LibrariesTestCase extends DrupalWebTestCase { // Test a top-level files property. $library = libraries_detect('example_files'); $files = array( - 'js' => array('example_1.js'), - 'css' => array('example_1.css'), - 'php' => array('example_1.php'), + 'js' => array('example_1.js' => array()), + 'css' => array('example_1.css' => array()), + 'php' => array('example_1.php' => array()), ); $this->verbose('
' . var_export($library, TRUE) . '
'); $this->assertEqual($library['files'], $files, 'Top-level files property works.'); @@ -117,9 +135,9 @@ class LibrariesTestCase extends DrupalWebTestCase { // Test version-specific library files. $library = libraries_detect('example_versions'); $files = array( - 'js' => array('example_2.js'), - 'css' => array('example_2.css'), - 'php' => array('example_2.php'), + 'js' => array('example_2.js' => array()), + 'css' => array('example_2.css' => array()), + 'php' => array('example_2.php' => array()), ); $this->verbose('
' . var_export($library, TRUE) . '
'); $this->assertEqual($library['files'], $files, 'Version-specific library files found.'); diff --git a/tests/libraries_test.module b/tests/libraries_test.module index 4893b11..a7bb1c7 100644 --- a/tests/libraries_test.module +++ b/tests/libraries_test.module @@ -73,6 +73,44 @@ function libraries_test_libraries_info() { ), ); + // Test all types of JavaScript and CSS supported by drupal_add_js() and + // drupal_add_css(). + $libraries['example_js_css'] = array( + 'name' => 'Example JavaScript and CSS', + 'library path' => drupal_get_path('module', 'libraries') . '/tests/example', + 'version' => 1, + 'variants' => array( + 'file' => array( + 'files' => array( + 'js' => array('example_1.js'), + 'css' => array('example_1.css'), + ), + ), + 'external' => array( + 'files' => array( + 'js' => array(url(NULL, array('absolute' => TRUE)) . '/' . drupal_get_path('module', 'libraries') . '/tests/example/example_1.js'), + 'css' => array(url(NULL, array('absolute' => TRUE)) . '/' . drupal_get_path('module', 'libraries') . '/tests/example/example_1.css'), + ), + ), + 'inline' => array( + 'files' => array( + 'js' => array('jQuery(document).ready(function(){jQuery(".libraries-test-javascript").text("If this text shows up, inline JavaScript code was loaded successfully.");});'), + // This is the content of example_1.css. + 'css' => array('.libraries-test-css{color:red;}'), + ), + ), + // There is no CSS equivalent of JavaScript settings. + 'setting' => array( + 'files' => array( + 'js' => array( + array('librariesTest' => array('testSetting' => TRUE)), + 'jQuery(document).ready(function(){if(Drupal.settings.librariesTest.testSetting){jQuery(".libraries-test-javascript").text("If this text shows up, JavaScript settings were loaded successfully.");}});', + ), + ), + ) + ), + ); + // Test loading of integration files. // Normally added by the corresponding module via hook_libraries_info_alter(), // these files should be automatically loaded when the library is loaded. @@ -333,7 +371,7 @@ function _libraries_test_return_installed($library, $name, $installed) { * * @see _libraries_test_callback() */ -function _libraries_test_info_callback(&$library, $version, $variant) { +function _libraries_test_info_callback(&$library, $version = NULL, $variant = NULL) { _libraries_test_callback($library, $version, $variant, 'info'); } @@ -344,7 +382,7 @@ function _libraries_test_info_callback(&$library, $version, $variant) { * * @see _libraries_test_callback() */ -function _libraries_test_pre_detect_callback(&$library, $version, $variant) { +function _libraries_test_pre_detect_callback(&$library, $version = NULL, $variant = NULL) { _libraries_test_callback($library, $version, $variant, 'pre-detect'); } @@ -355,7 +393,7 @@ function _libraries_test_pre_detect_callback(&$library, $version, $variant) { * * @see _libraries_test_callback() */ -function _libraries_test_post_detect_callback(&$library, $version, $variant) { +function _libraries_test_post_detect_callback(&$library, $versio = NULL, $variant = NULL) { _libraries_test_callback($library, $version, $variant, 'post-detect'); } @@ -366,7 +404,7 @@ function _libraries_test_post_detect_callback(&$library, $version, $variant) { * * @see _libraries_test_callback() */ -function _libraries_test_load_callback(&$library, $version, $variant) { +function _libraries_test_load_callback(&$library, $version = NULL, $variant = NULL) { _libraries_test_callback($library, $version, $variant, 'load'); } @@ -442,6 +480,30 @@ function libraries_test_menu() { 'page arguments' => array('example_versions_and_variants', 'example_variant_2'), 'access callback' => TRUE, ); + $items['libraries_test/js_css/file'] = array( + 'title' => 'Test JavaScript and CSS: Local files', + 'page callback' => '_libraries_test_load', + 'page arguments' => array('example_js_css', 'file'), + 'access callback' => TRUE, + ); + $items['libraries_test/js_css/external'] = array( + 'title' => 'Test JavaScript and CSS: External files', + 'page callback' => '_libraries_test_load', + 'page arguments' => array('example_js_css', 'external'), + 'access callback' => TRUE, + ); + $items['libraries_test/js_css/inline'] = array( + 'title' => 'Test JavaScript and CSS: Inline code', + 'page callback' => '_libraries_test_load', + 'page arguments' => array('example_js_css', 'inline'), + 'access callback' => TRUE, + ); + $items['libraries_test/js_css/setting'] = array( + 'title' => 'Test JavaScript and CSS: JavaScript settings', + 'page callback' => '_libraries_test_load', + 'page arguments' => array('example_js_css', 'setting'), + 'access callback' => TRUE, + ); return $items; }