? .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	15 Dec 2010 20:59:30 -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.15
diff -u -p -r1.15 libraries.module
--- libraries.module	3 Dec 2010 23:16:38 -0000	1.15
+++ libraries.module	15 Dec 2010 20:59:30 -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,22 @@ 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) {
+      // Add own callback to run before all others.
+      array_unshift($properties['callbacks']['prepare'], 'libraries_prepare_files');
+      libraries_invoke('prepare', $properties);
+    }
   }
 
   if (isset($name)) {
@@ -226,6 +293,64 @@ function libraries_info($name = NULL) {
 }
 
 /**
+ * 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, $variant) {
+  // 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 (!empty($files[$type])) {
+        foreach ($files[$type] as $key => $value) {
+          // Unset numeric keys and turn the respective values into keys.
+          if (is_numeric($key)) {
+            $files[$type][$value] = array();
+            unset($files[$type][$key]);
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
  * Detect libraries and library versions.
  *
  * @todo It makes little sense to pass $libraries by reference here. We need to
@@ -348,6 +473,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);
 }
 
 /**
@@ -375,8 +503,12 @@ function libraries_load($name, $variant 
       return FALSE;
     }
     $library = array_merge($library, $library['variants'][$variant]);
+    unset($library['variants']);
   }
 
+  // Invoke callbacks in the 'load' group.
+  libraries_invoke('load', $library);
+
   return libraries_load_files($library, $variant);
 }
 
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	15 Dec 2010 20:59:31 -0000
@@ -71,15 +71,9 @@ function libraries_test_libraries_info()
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array('1'),
     'files' => array(
-      'js' => array(
-        'example_1.js',
-      ),
-      'css' => array(
-        'example_1.css',
-      ),
-      'php' => array(
-        'example_1.php'
-      ),
+      'js' => array('example_1.js'),
+      'css' => array('example_1.css'),
+      'php' => array('example_1.php'),
     ),
   );
 
