diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index b88cf1f37e0bbfb7b101de43cfa2b16230fb053f..1d5347bd3867f0cbebdab565f7880e5c393f2cd6 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,6 +1,7 @@
 
 Libraries 7.x-2.x, xxxx-xx-xx
 -----------------------------
+#958162 by tstoeckler, sun, good_man: Allow to apply callbacks to libraries.
 #1125904 by tstoeckler, boombatower: Fix drush libraries-list.
 #1050076 by tstoeckler: Re-utilize libraries_detect() and remove libraries_detect_library().
 #466090 by tstoeckler: Add update function.
diff --git a/libraries.api.php b/libraries.api.php
index 71971e5e6962ea8bdde3ca67fb1a70763fea45fa..66fa772378c27e24a74a74da9e265f56ab7547fe 100644
--- a/libraries.api.php
+++ b/libraries.api.php
@@ -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 path to the file relative to the module it belongs to.
+ *   - 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()
@@ -213,6 +248,14 @@ function hook_libraries_info() {
         'js' => array('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.
diff --git a/libraries.module b/libraries.module
index 90a2ea1a19b76c40ad36471fda03f33927b5d1e5..e8f57f411fd224c15edc3dffe74710e4d45fdaeb 100644
--- a/libraries.module
+++ b/libraries.module
@@ -151,6 +151,60 @@ 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) {
+  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 the callback to versions.
+  if (isset($library['versions'])) {
+    foreach ($library['versions'] as $version_string => &$version) {
+      $callback($version, $version_string, NULL);
+      // Versions can include variants as well.
+      if (isset($version['variants'])) {
+        foreach ($version['variants'] as $version_variant_name => &$version_variant) {
+          $callback($version_variant, $version_string, $version_variant_name);
+        }
+      }
+    }
+  }
+
+  // Apply the callback to variants.
+  if (isset($library['variants'])) {
+    foreach ($library['variants'] as $variant_name => &$variant) {
+      $callback($variant, NULL, $variant_name);
+    }
+  }
+}
+
 /**
  * Returns information about registered libraries.
  *
@@ -205,11 +259,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 &$properties) {
+      libraries_invoke('prepare', $properties);
+    }
   }
 
   if (isset($name)) {
@@ -340,6 +404,10 @@ function libraries_detect($name) {
 
   // If we end up here, the library should be usable.
   $library['installed'] = TRUE;
+
+  // Invoke callbacks in the 'detect' group.
+  libraries_invoke('detect', $library);
+
   return $library;
 }
 
@@ -392,6 +460,9 @@ function libraries_load($name, $variant = NULL) {
     // If the library (variant) is installed, load it.
     $library['loaded'] = FALSE;
     if ($library['installed']) {
+      // Invoke callbacks in the 'load' group.
+      libraries_invoke('load', $library);
+
       $library['loaded'] = libraries_load_files($library);
     }
     $loaded[$name] = $library;
diff --git a/tests/libraries.test b/tests/libraries.test
index 14a869096f342e33f39c25f82658997ffe5675f6..ada68b9b262eea8426017dc4cb91a7c29aa619da 100644
--- a/tests/libraries.test
+++ b/tests/libraries.test
@@ -137,6 +137,81 @@ class LibrariesTestCase extends DrupalWebTestCase {
     $this->verbose(var_export($library, TRUE));
     $this->assertEqual($library['variants']['example_variant']['installed'], TRUE, 'Existing variant found.');
 
+    // Test the applying of callbacks.
+    $expected = array_merge(libraries_info('example_empty'), array(
+      'machine name' => 'example_callback',
+      'name' => 'Example callback',
+      'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+      'version' => '1',
+      'versions' => array(
+        '1' => array(
+          'variants' => array(
+            'example_variant' => array(
+              'prepare callback' => 'not applied',
+              'detect callback' => 'not applied',
+              'load callback' => 'not applied',
+            ),
+          ),
+          'prepare callback' => 'not applied',
+          'detect callback' => 'not applied',
+          'load callback' => 'not applied',
+        ),
+      ),
+      'variants' => array(
+        'example_variant' => array(
+          'prepare callback' => 'not applied',
+          'detect callback' => 'not applied',
+          'load callback' => 'not applied',
+        ),
+      ),
+      'callbacks' => array(
+        'prepare' => array('_libraries_test_prepare_callback'),
+        'detect' => array('_libraries_test_detect_callback'),
+        'load' => array('_libraries_test_load_callback'),
+      ),
+      'prepare callback' => 'not applied',
+      'detect callback' => 'not applied',
+      'load callback' => 'not applied',
+    ));
+    // Test a callback in the 'prepare' phase.
+    $expected['prepare callback'] = 'applied (top-level)';
+    $expected['versions']['1']['prepare callback'] = 'applied (version 1)';
+    $expected['versions']['1']['variants']['example_variant']['prepare callback'] = 'applied (version 1, variant example_variant)';
+    $expected['variants']['example_variant']['prepare callback'] = 'applied (variant example_variant)';
+    $library = libraries_info('example_callback');
+    $this->verbose(var_export($expected, TRUE));
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library, $expected, 'Prepare callback was applied correctly.');
+    // Test a callback in the 'detect' phase.
+    unset($expected['versions']);
+    $expected['installed'] = TRUE;
+    $expected['prepare callback'] = 'applied (version 1)';
+    $expected['variants']['example_variant']['installed'] = TRUE;
+    $expected['variants']['example_variant']['prepare callback'] = 'applied (version 1, variant example_variant)';
+    $expected['detect callback'] = 'applied (top-level)';
+    $expected['variants']['example_variant']['detect callback'] = 'applied (variant example_variant)';
+    $library = libraries_detect('example_callback');
+    $this->verbose(var_export($expected, TRUE));
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library, $expected, 'Detect callback was applied correctly.');
+
+    // Test a callback in the 'load' phase.
+    unset($expected['variants']);
+    $expected['loaded'] = 0;
+    $expected['load callback'] = 'applied (top-level)';
+    $library = libraries_load('example_callback');
+    $this->verbose(var_export($expected, TRUE));
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library, $expected, 'Load callback was applied correctly.');
+    // This is not recommended usually and is only used for testing purposes.
+    drupal_static_reset('libraries_load');
+    $expected['prepare callback'] = 'applied (version 1, variant example_variant)';
+    $expected['detect callback'] = 'applied (variant example_variant)';
+    $library = libraries_load('example_callback', 'example_variant');
+    $this->verbose(var_export($expected, TRUE));
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library, $expected, 'Load callback was applied correctly to a variant.');
+
     // Test loading of a simple library with a top-level files property.
     $this->drupalGet('libraries_test/files');
     $this->assertLibraryFiles('example_1', 'File loading');
diff --git a/tests/libraries_test.module b/tests/libraries_test.module
index 244dcfc22749569d25deacf622f0993418c2f540..bf1369f438c8f151a99b5a64a699fc6c503a81e4 100644
--- a/tests/libraries_test.module
+++ b/tests/libraries_test.module
@@ -200,6 +200,46 @@ function libraries_test_libraries_info() {
     ),
   );
 
+  // Test the applying of callbacks.
+  $libraries['example_callback'] = array(
+    'name' => 'Example callback',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '1',
+    'versions' => array(
+      '1' => array(
+        'variants' => array(
+          'example_variant' => array(
+            // These keys are for testing purposes only.
+            'prepare callback' => 'not applied',
+            'detect callback' => 'not applied',
+            'load callback' => 'not applied',
+          ),
+        ),
+        // These keys are for testing purposes only.
+        'prepare callback' => 'not applied',
+        'detect callback' => 'not applied',
+        'load callback' => 'not applied',
+      ),
+    ),
+    'variants' => array(
+      'example_variant' => array(
+        // These keys are for testing purposes only.
+        'prepare callback' => 'not applied',
+        'detect callback' => 'not applied',
+        'load callback' => 'not applied',
+      ),
+    ),
+    'callbacks' => array(
+      'prepare' => array('_libraries_test_prepare_callback'),
+      'detect' => array('_libraries_test_detect_callback'),
+      'load' => array('_libraries_test_load_callback'),
+    ),
+    // These keys are for testing purposes only.
+    'prepare callback' => 'not applied',
+    'detect callback' => 'not applied',
+    'load callback' => 'not applied',
+  );
+
   // This library is used together with libraries_info() to be populated with
   // the defaults.
   $libraries['example_empty'] = array();
@@ -285,6 +325,107 @@ function _libraries_test_return_installed($library, $name, $installed) {
   return $installed;
 }
 
+/**
+ * Sets the 'prepare callback' key.
+ *
+ * This function is used as a test callback for the 'prepare' callback group.
+ *
+ * @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.
+ *
+ * @see _libraries_test_callback()
+ */
+function _libraries_test_prepare_callback(&$library, $version, $variant) {
+  _libraries_test_callback($library, $version, $variant, 'prepare');
+}
+
+/**
+ * Sets the 'detect callback' key.
+ *
+ * This function is used as a test callback for the 'detect' callback group.
+ *
+ * @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.
+ *
+ * @see _libraries_test_callback()
+ */
+function _libraries_test_detect_callback(&$library, $version, $variant) {
+  _libraries_test_callback($library, $version, $variant, 'detect');
+}
+
+/**
+ * Sets the 'load callback' key.
+ *
+ * This function is used as a test callback for the 'load' callback group.
+ *
+ * @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.
+ *
+ * @see _libraries_test_callback()
+ */
+function _libraries_test_load_callback(&$library, $version, $variant) {
+  _libraries_test_callback($library, $version, $variant, 'load');
+}
+
+/**
+ * Sets the '[group] callback' key, where [group] is prepare, detect, or load.
+ *
+ * This function is used as a test callback for the all callback groups.
+ *
+ * It sets the '[group] callback' (see above) key to 'applied ([part])' where
+ * [part] is either 'top-level', 'version x.y' (where x.y is the passed-in
+ * version string), 'variant example' (where example is the passed-in variant
+ * name), or 'version x.y, variant example' (see above), depending on the part
+ * of the library the passed-in library information belongs to.
+ *
+ * @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, $group) {
+  $string = 'applied';
+  if (isset($version) && isset($variant)) {
+    $string .= " (version $version, variant $variant)";
+  }
+  elseif (isset($version)) {
+    $string .= " (version $version)";
+  }
+  elseif (isset($variant)) {
+    $string .= " (variant $variant)";
+  }
+  else {
+    $string .= ' (top-level)';
+  }
+  $library["$group callback"] = $string;
+}
+
 /**
  * Implements hook_menu().
  */