? .cvsignore
? .project
Index: libraries.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/libraries.api.php,v
retrieving revision 1.5
diff -u -p -r1.5 libraries.api.php
--- libraries.api.php	3 Nov 2010 22:22:42 -0000	1.5
+++ libraries.api.php	8 Jan 2011 20:17:57 -0000
@@ -102,6 +102,41 @@
  *     names and whose values are sets of files to load for the module, using
  *     the same notion as the top-level 'files' property. Each specified file
  *     should contain the full path to the file.
+ *   - callbacks: An associative array whose keys are callback groups and whose
+ *     values are arrays of callbacks to apply to the library in that group.
+ *     Each callback receives the following arguments:
+ *     - $library: An array of library information belonging to the top-level
+ *       library, a specific version, a specific variant or a specific variant
+ *       of a specific version. Because library information such as the 'files'
+ *       property (see above) can be declared in all these different locations
+ *       of the library array, but a callback may have to act on all these
+ *       different parts of the library, it is called recursively for each
+ *       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.
+ *     - $variant: If the $library array belongs to a certain variant (see
+ *       above), a string containing the variant name. NULL, otherwise.
+ *     Valid callback groups are:
+ *     - prepare: Callbacks registered in this group are applied as soon as the
+ *       library information has been retrieved via hook_libraries_info() or
+ *       info files.
+ *     - detect: Callbacks registered in this group are applied as soon as
+ *       library detection has been completed. At this point the library
+ *       contains the version-specific information, if specified, and following
+ *       additional information is available:
+ *       - $library['installed']: A boolean indicating whether the library is
+ *         installed or not.
+ *       - $library['version']: If it could be detected, a string containing the
+ *         version of the library.
+ *       - $library['variants'][$variant]['installed']: For each specified
+ *         variant, a boolean indicating whether the variant is installed or
+ *         not.
+ *       Note that in this group the 'versions' property is no longer available.
+ *     - load: Callbacks registered in this group are applied directly
+ *       before this library is loaded. At this point the library contains
+ *       variant-specific information, if specified. Note that in this group the
+ *       'variants' property is no longer available.
  *   Additional top-level properties can be registered as needed.
  *
  * @see hook_library()
@@ -221,6 +256,14 @@ function hook_libraries_info() {
         'js' => 'ex_lib.inc',
       ),
     ),
+    // Optionally register callbacks to apply to the library during different
+    // stages of its lifetime ('callback groups'). Here, a callback is
+    // registered in the 'detect' group.
+    'callbacks' => array(
+      'detect' => array(
+        'mymodule_example_detect_callback',
+      ),
+    ),
   );
 
   // A very simple library. No changing APIs (hence, no versions), no variants.
Index: libraries.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/libraries.module,v
retrieving revision 1.16
diff -u -p -r1.16 libraries.module
--- libraries.module	15 Dec 2010 21:09:52 -0000	1.16
+++ libraries.module	8 Jan 2011 20:17:57 -0000
@@ -158,6 +158,62 @@ function libraries_scan_info_files() {
 
   return $files;
 }
+
+/**
+ * Invokes library callbacks.
+ *
+ * @param $group
+ *   A string containing the group of callbacks that is to be applied. Should be
+ *   either 'prepare', 'detect', or 'load'.
+ * @param $library
+ *   An array of library information, passed by reference.
+ */
+function libraries_invoke($group, &$library) {
+  if (!empty($library['callbacks'][$group])) {
+    foreach ($library['callbacks'][$group] as $callback) {
+      libraries_traverse_library($library, $callback);
+    }
+  }
+}
+
+/**
+ * Helper function to apply a callback to all parts of a library.
+ *
+ * Because library declarations can include variants and versions, and those
+ * version declarations can in turn include variants, modifying e.g. the 'files'
+ * property everywhere it is declared can be quite cumbersome, in which case
+ * this helper function is useful.
+ *
+ * @param $library
+ *   An array of library information, passed by reference.
+ * @param $callback
+ *   A string containing the callback to apply to all parts of a library.
+ */
+function libraries_traverse_library(&$library, $callback) {
+  // Always apply the callback to the top-level library.
+  $callback($library, NULL, NULL);
+
+  // Apply callback to versions.
+  if (!empty($library['versions'])) {
+    foreach ($library['versions'] as $version_string => &$version) {
+      $callback($version, $version_string, NULL);
+      // Versions can include variants as well.
+      if (!empty($version['variants'])) {
+        foreach ($version['variants'] as $version_variant_name => &$version_variant) {
+          $callback($version_variant, $version, $version_variant_name);
+        }
+      }
+    }
+  }
+
+  // Apply callback to variants.
+  if (!empty($library['variants'])) {
+    foreach ($library['variants'] as $variant_name => &$variant) {
+      $callback($variant, NULL, $variant_name);
+    }
+  }
+}
+
 /**
  * Returns information about registered libraries.
  *
@@ -212,11 +268,21 @@ function libraries_info($name = NULL) {
         'variants' => array(),
         'versions' => array(),
         'integration files' => array(),
+        'callbacks' => array(
+          'prepare' => array(),
+          'detect' => array(),
+          'load' => array(),
+        ),
       );
     }
 
     // Allow modules to alter the registered libraries.
     drupal_alter('libraries_info', $libraries);
+
+    // Invoke callbacks in the 'prepare' group.
+    foreach ($libraries as $machine_name => &$properties) {
+      libraries_invoke('prepare', $properties);
+    }
   }
 
   if (isset($name)) {
@@ -348,6 +414,9 @@ function libraries_detect_library(&$libr
 
   // If we end up here, the library should be usable.
   $library['installed'] = TRUE;
+
+  // Invoke callbacks in the 'detect' group.
+  libraries_invoke('detect', $library);
 }
 
 /**
@@ -377,6 +446,9 @@ function libraries_load($name, $variant 
     $library = array_merge($library, $library['variants'][$variant]);
   }
 
+  // Invoke callbacks in the 'load' group.
+  libraries_invoke('load', $library);
+
   return libraries_load_files($library, $variant);
 }
 
Index: tests/libraries.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/libraries.test,v
retrieving revision 1.11
diff -u -p -r1.11 libraries.test
--- tests/libraries.test	10 Nov 2010 20:57:00 -0000	1.11
+++ tests/libraries.test	8 Jan 2011 20:17:58 -0000
@@ -151,6 +151,27 @@ class LibrariesTestCase extends DrupalWe
     $this->verbose(var_export($library, TRUE));
     $this->assertEqual($library['variants']['example_variant']['installed'], TRUE, 'Existing variant found.');
 
+    // Test a callback in the 'prepare' phase.
+    $library = libraries_info('example_callback_prepare');
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library['callback applied'], TRUE, 'Prepare callback was applied correctly.');
+
+    // Test a callback in the 'detect' phase.
+    $library = libraries_info('example_callback_detect');
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library['callback applied'], FALSE, 'Detect callback was not run in the prepare phase.');
+    libraries_detect_library($library);
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library['callback applied'], TRUE, 'Detect callback was applied correctly.');
+    
+    // Test a callback in the 'load' phase.
+    $library = libraries_info('example_callback_load');
+    libraries_detect_library($library);
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library['callback applied'], FALSE, 'Load callback was not run in the prepare or detect phase.');
+    $this->drupalGet('libraries_test/callback_load');
+    $this->assertText('Callback applied!', 'Load callback was applied correctly.');
+
     // Test loading of a simple library with a top-level files property.
     $this->drupalGet('libraries_test/simple');
     $this->assertLibraryFiles('example_1', 'Simple library loading');
Index: tests/libraries_test.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/libraries_test.module,v
retrieving revision 1.7
diff -u -p -r1.7 libraries_test.module
--- tests/libraries_test.module	3 Nov 2010 22:22:42 -0000	1.7
+++ tests/libraries_test.module	8 Jan 2011 20:17:58 -0000
@@ -215,6 +215,41 @@ function libraries_test_libraries_info()
     ),
   );
 
+  // Test the applying of callbacks.
+  $libraries['example_callback_prepare'] = array(
+    'name' => 'Example prepare callback',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version callback' => '_libraries_test_return_version',
+    'version arguments' => array('1'),
+    'callbacks' => array(
+      'prepare' => array('_libraries_test_callback'),
+    ),
+    // This key is for testing purposes only.
+    'callback applied' => FALSE,
+  );
+  $libraries['example_callback_detect'] = array(
+    'name' => 'Example detect callback',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version callback' => '_libraries_test_return_version',
+    'version arguments' => array('1'),
+    'callbacks' => array(
+      'detect' => array('_libraries_test_callback'),
+    ),
+    // This key is for testing purposes only.
+    'callback applied' => FALSE,
+  );
+  $libraries['example_callback_load'] = array(
+    'name' => 'Example load callback',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version callback' => '_libraries_test_return_version',
+    'version arguments' => array('1'),
+    'callbacks' => array(
+      'load' => array('_libraries_test_callback'),
+    ),
+    // This key is for testing purposes only.
+    'callback applied' => FALSE,
+  );
+
   // This library is used together with libraries_info() to be populated with
   // the defaults.
   $libraries['example_empty'] = array();
@@ -301,6 +336,26 @@ function _libraries_test_return_installe
 }
 
 /**
+ * Sets the 'callback applied' of a library to TRUE.
+ *
+ * This function is used as test callback for all callback groups.
+ *
+ * @param $library
+ *   An array of library information, which may be version- or variant-specific.
+ *   Passed by reference.
+ * @param $version
+ *   The version the library information passed in $library belongs to, or NULL
+ *   if the passed library information is not version-specific.
+ * @param $variant
+ *   The variant the library information passed in $library belongs to, or NULL
+ *   if the passed library information is not variant-specific.
+ */
+function _libraries_test_callback(&$library, $version, $variant) {
+  $library['callback applied'] = TRUE;
+  drupal_set_message('Callback applied!');
+}
+
+/**
  * Implements hook_menu().
  */
 function libraries_test_menu() {
@@ -334,6 +389,12 @@ function libraries_test_menu() {
     'page arguments' => array('example_versions_and_variants', 'example_variant_2'),
     'access callback' => TRUE,
   );
+  $items['libraries_test/callback_load'] = array(
+    'title' => 'Test load callback',
+    'page callback' => '_libraries_test_load',
+    'page arguments' => array('example_callback_load'),
+    'access callback' => TRUE,
+  );
   return $items;
 }
 
