? 864376-external-files-46.patch
? 864376-external-files.patch
? 864376-libraries-cleanup-36.patch
? 864376-libraries-external-slim-32.patch
? 919632-libraries_info_files-30.patch
? libraries-HEAD.external.43.patch
Index: libraries.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/libraries.api.php,v
retrieving revision 1.4
diff -u -p -r1.4 libraries.api.php
--- libraries.api.php	9 Oct 2010 22:26:03 -0000	1.4
+++ libraries.api.php	26 Oct 2010 15:36:30 -0000
@@ -17,9 +17,21 @@
  *   - title: The official, human-readable name of the library.
  *   - vendor url: The URL of the homepage of the library.
  *   - download url: The URL of a web page on which the library can be obtained.
+ *   - library path: (optional) The absolute path to the library directory. This
+ *     should not be declared normally, as it is automatically detected, to
+ *     allow for multiple possible library locations. A valid use-case is an
+ *     external library, in which case the full URL to the library should be
+ *     specified here.
  *   - path: (optional) A relative path from the directory of the library to the
  *     actual library. Only required if the extracted download package contains
  *     the actual library files in a sub-directory.
+ *   - version: (optional) The version of the library. This should not be
+ *     declared normally, as it is automatically detected (see 'version
+ *     callback' below) to allow for version changes of libraries without code
+ *     changes of implementing modules and to support different versions of a
+ *     library simultaneously (though only one version can be installed per
+ *     site). A valid use-case is an external library whose version cannot be
+ *     determined programatically.
  *   - version callback: (optional) The name of a function that detects and
  *     returns the full version string of the library. The first argument is
  *     always $library, an array containing all library information as described
@@ -311,6 +323,29 @@ function hook_libraries_info() {
       ),
     ),
   );
+  // An example library that is external. It has a local and a remote variant.
+  $libraries['openlayers'] = array(
+    'title' => 'OpenLayers',
+    'vendor url' => 'http://www.openlayers.org/',
+    'download url' => 'http://trac.osgeo.org/openlayers/wiki/HowToDownload',
+    // This makes Libraries API not scan the filesystem for the library.
+    'library path' => 'http://openlayers.org/api/OpenLayers.js',
+    // This should determine which variant is available, the local or the remote
+    // one, (with libraries_get_path()) and then conditionally determine the
+    // version (with libraries_get_version() and the respective parameters).
+    'version callback' => 'openlayers_get_version',
+    'files' => array(
+      'js' => array('OpenLayers.js'),
+    ),
+    'variants' => array(
+      'local' => array(
+        'library path' => libraries_get_path('openlayers'),
+      ),
+      'remote' => array(
+        'library path' => 'http://openlayers.org/api',
+      ),
+    ),
+  );
   return $libraries;
 }
 
Index: libraries.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/libraries.module,v
retrieving revision 1.9
diff -u -p -r1.9 libraries.module
--- libraries.module	15 Oct 2010 15:15:53 -0000	1.9
+++ libraries.module	26 Oct 2010 15:36:31 -0000
@@ -15,7 +15,7 @@
  *   Whether to prefix the resulting path with base_path().
  *
  * @return
- *   The path to the specified library.
+ *   The path to the specified library, or FALSE, if it wasn't found.
  *
  * @ingroup libraries
  */
@@ -28,9 +28,7 @@ function libraries_get_path($library, $b
 
   $path = ($base_path ? base_path() : '');
   if (!isset($libraries[$library])) {
-    // Most often, external libraries can be shared across multiple sites, so
-    // we return sites/all/libraries as the default path.
-    $path .= 'sites/all/libraries/' . $library;
+    return FALSE;
   }
   else {
     $path .= $libraries[$library];
@@ -131,26 +129,22 @@ function libraries_info_files() {
   if (!isset($profile)) {
     $profile = variable_get('install_profile', 'default');
   }
-  $config = conf_path();
+  $site = conf_path();
 
   // Build a list of directories.
   $directories = module_invoke_all('libraries_info_file_paths');
   $directories[] = 'libraries';
   $directories[] = "libraries/$profile/libraries";
   $directories[] = 'sites/all/libraries';
-  $directories[] = "sites/$config/libraries";
+  $directories[] = "sites/$site/libraries";
 
   // Scan for info files.
   $files = array();
   foreach ($directories as $dir) {
-    $files += file_scan_directory($dir, '/[a-z[a-z0-9_]+.info/', array(
+    $files = array_merge($files, file_scan_directory($dir, '/^[a-zA-Z0-9_-]+\.info$/', array(
       'key' => 'name',
       'recurse' => FALSE,
-    ));
-  }
-
-  foreach ($files as &$file) {
-    $file = $file->uri;
+    )));
   }
 
   return $files;
@@ -187,9 +181,10 @@ function libraries_info($library = NULL)
       }
     }
     // Gather information from .info files.
-    foreach (libraries_info_files() as $name => $path) {
-      $file = "$path/$name.info";
-      $libraries[$name] = drupal_parse_info_file($file);
+    foreach (libraries_info_files() as $name => $file) {
+      $properties = drupal_parse_info_file($file->uri);
+      $properties['info file'] = $file->uri;
+      $libraries[$name] = $properties;      
     }
 
     // Provide defaults.
@@ -199,6 +194,7 @@ function libraries_info($library = NULL)
         'vendor url' => '',
         'download url' => '',
         'path' => '',
+        'library path' => FALSE,
         'version callback' => 'libraries_get_version',
         'version arguments' => array(),
         'files' => array(),
@@ -256,10 +252,23 @@ function libraries_detect_library(&$libr
   $name = $library['name'];
 
   // Check whether the library exists.
-  if (!isset($library['library path'])) {
-    $library['library path'] = libraries_get_path($name);
+  $found = FALSE;
+  if (!$library['library path']) {
+    // Check the variants for library paths as well.
+    foreach ($library['variants'] as $variant => $info) {
+      if (isset($info['library path']) && libraries_file_exists($info['library path'])) {
+        $found = TRUE;
+        break;
+      }
+    }
+    if (!$found) {
+      $library['library path'] = libraries_get_path($name);
+    }
   }
-  if (!file_exists($library['library path'])) {
+  if (libraries_file_exists($library['library path'])) {
+    $found = TRUE;
+  }
+  if (!$found) {
     $library['error'] = 'not found';
     $library['error message'] = t('%library could not be found.', array('%library' => $library['title']));
     return;
@@ -364,9 +373,6 @@ function libraries_load($library, $varia
  *   The name of the variant to load.
  */
 function libraries_load_files($library, $variant = NULL) {
-  // Construct the full path to the library for later use.
-  $path = (!empty($library['path']) ? $library['library path'] . '/' . $library['path'] : $library['library path']);
-
   // If a variant was specified, override the top-level properties with the
   // variant properties.
   if (!empty($variant) && !empty($library['variants'][$variant]['installed'])) {
@@ -383,6 +389,9 @@ function libraries_load_files($library, 
     }
   }
 
+  // Construct the full path to the library for later use.
+  $path = (!empty($library['path']) ? $library['library path'] . '/' . $library['path'] : $library['library path']);
+
   // Load both the JavaScript and the CSS files.
   // The parameters for drupal_add_js() and drupal_add_css() require special
   // handling.
@@ -393,9 +402,15 @@ function libraries_load_files($library, 
         // 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;
+          // This is needed because drupal_add_css() does not detect external
+          // paths automatically. See http://drupal.org/node/953340
+          if (url_is_external("$path/$options")) {
+            $options = array('type' => 'external');
+          }
+          else {
+            $options = NULL;
+          }
         }
         // 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
@@ -416,9 +431,9 @@ function libraries_load_files($library, 
   // Load PHP files.
   if (!empty($library['files']['php'])) {
     foreach ($library['files']['php'] as $file) {
-      $file_path = DRUPAL_ROOT . '/' . $path . '/' . $file;
-      if (file_exists($file_path)) {
-        require_once $file_path;
+      $filepath = (url_is_external($file) ? $file : DRUPAL_ROOT . '/' . $path . '/' . $file);
+      if (libraries_file_exists($filepath)) {
+        include_once $filepath;
       }
     }
   }
@@ -454,12 +469,12 @@ function libraries_get_version($library,
     'lines' => 20,
     'cols' => 200,
   );
-
-  $file = DRUPAL_ROOT . '/' . $library['library path'] . '/' . $options['file'];
-  if (empty($options['file']) || !file_exists($file)) {
+  $filepath = $library['library path'] . '/' . $options['file'];
+  $filepath = (url_is_external($filepath) ? $filepath : DRUPAL_ROOT . '/' . $filepath);
+  if (empty($options['file']) || !libraries_file_exists($filepath)) {
     return;
   }
-  $file = fopen($file, 'r');
+  $file = fopen($filepath, 'r');
   while ($options['lines'] && $line = fgets($file, $options['cols'])) {
     if (preg_match($options['pattern'], $line, $version)) {
       fclose($file);
@@ -470,3 +485,19 @@ function libraries_get_version($library,
   fclose($file);
 }
 
+/**
+ * Wrapper function for file_exists() with support for external files.
+ *
+ * For external files, it doesn't actually check their availability, as that
+ * would have to be done with fopen, which causes significant overhead. Instead,
+ * it simply assumes all external files to exist.
+ *
+ * @param $filepath
+ *   The path to the file. In case of an external file the full URL.
+ *
+ * @return
+ *   A boolean indicating whether this file exists or not.
+ */
+function libraries_file_exists($filepath) {
+  return url_is_external($filepath) || file_exists($filepath);
+}
Index: tests/libraries.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/libraries.test,v
retrieving revision 1.8
diff -u -p -r1.8 libraries.test
--- tests/libraries.test	15 Oct 2010 15:15:53 -0000	1.8
+++ tests/libraries.test	26 Oct 2010 15:36:31 -0000
@@ -31,25 +31,23 @@ class LibrariesTestCase extends DrupalWe
    */
   function testLibraries() {
     // Test a library specified with an .info file gets detected.
-    $library = libraries_info('example');
-    $expected = array(
-      'title' => 'example',
-      'vendor url' => '',
-      'download url' => '',
-      'path' => '',
-      'version callback' => 'libraries_get_version',
-      'version arguments' => array(),
-      'files' => array(),
-      'variants' => array(),
-      'versions' => array(),
-      'integration files' => array(),
-    );
+    $library = libraries_info('libraries_info_example');
+    $expected = array_merge(libraries_info('empty'), array(
+      'name' => 'libraries_info_example',
+      'title' => 'Example info file',
+      'info file' => drupal_get_path('module', 'libraries_test') . '/libraries_info_example/libraries_info_example.info',
+      'hidden' => TRUE,
+    ));
+    unset($expected['module']);
+    $this->verbose(var_export($expected, TRUE));
+    $this->verbose(var_export($library, TRUE));
     $this->assertEqual($library, $expected, 'Library specified with an .info file found');
 
     // Test missing library.
     $library = libraries_info('example_missing');
     libraries_detect_library($library);
-    $this->assertEqual($library['error'], 'not found', 'Non-existing library not found.');
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library['error'], 'not found', 'Missing library not found.');
     $error_message = t('%library could not be found.', array(
       '%library' => $library['title'],
     ));
@@ -58,7 +56,8 @@ class LibrariesTestCase extends DrupalWe
     // Test unknown library version.
     $library = libraries_info('example_undetected_version');
     libraries_detect_library($library);
-    $this->assertEqual($library['error'], 'not detected', 'Library version not found.');
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library['error'], 'not detected', 'Undetected version detected as such.');
     $error_message = t('The version of %library could not be detected.', array(
       '%library' => $library['title'],
     ));
@@ -67,7 +66,8 @@ class LibrariesTestCase extends DrupalWe
     // Test unsupported library version.
     $library = libraries_info('example_unsupported_version');
     libraries_detect_library($library);
-    $this->assertEqual($library['error'], 'not supported', 'Library version not supported.');
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library['error'], 'not supported', 'Unsupported version detected as such.');
     $error_message = t('The installed version %version of %library is not supported.', array(
       '%version' => $library['version'],
       '%library' => $library['title'],
@@ -77,18 +77,21 @@ class LibrariesTestCase extends DrupalWe
     // Test supported library version.
     $library = libraries_info('example_supported_version');
     libraries_detect_library($library);
+    $this->verbose(var_export($library, TRUE));
     $this->assertEqual($library['installed'], TRUE, 'Supported library version found.');
 
     // Test libraries_get_version().
     $library = libraries_info('example_default_version_callback');
     libraries_detect_library($library);
     $version = '2';
+    $this->verbose(var_export($library, TRUE));
     $this->assertEqual($library['version'], $version, 'Expected version returned by default version callback.');
 
     // Test a multiple-parameter version callback.
     $library = libraries_info('example_multiple_parameter_version_callback');
     libraries_detect_library($library);
     $version = '2';
+    $this->verbose(var_export($library, TRUE));
     $this->assertEqual($library['version'], $version, 'Expected version returned by multiple parameter version callback.');
 
     // Test a top-level files property.
@@ -99,6 +102,7 @@ class LibrariesTestCase extends DrupalWe
       'css' => array('example_1.css'),
       'php' => array('example_1.php'),
     );
+    $this->verbose(var_export($library, TRUE));
     $this->assertEqual($library['files'], $files, 'Top-level files property works.');
 
     // Test version-specific library files.
@@ -109,12 +113,14 @@ class LibrariesTestCase extends DrupalWe
       'css' => array('example_2.css'),
       'php' => array('example_2.php'),
     );
+    $this->verbose(var_export($library, TRUE));
     $this->assertEqual($library['files'], $files, 'Version-specific library files found.');
 
     // Test missing variant.
     $library = libraries_info('example_variant_missing');
     libraries_detect_library($library);
     $variants = array_keys($library['variants']);
+    $this->verbose(var_export($library, TRUE));
     $this->assertEqual($library['variants']['example_variant']['error'], 'not found', 'Missing variant not found');
     $error_message = t('The %variant variant of %library could not be found.', array(
       '%variant' => $variants[0],
@@ -125,29 +131,60 @@ class LibrariesTestCase extends DrupalWe
     // Test existing variant.
     $library = libraries_info('example_variant');
     libraries_detect_library($library);
+    $this->verbose(var_export($library, TRUE));
     $this->assertEqual($library['variants']['example_variant']['installed'], TRUE, 'Existing variant found.');
 
+    // Test external library.
+    $library = libraries_info('example_external');
+    libraries_detect_library($library);
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library['installed'], TRUE, 'External library found.');
+
+    // Test external library with a local and remote variant
+    $library = libraries_info('example_external_variant');
+    libraries_detect_library($library);
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library['installed'], TRUE, 'External library with local and remote variant found.');
+
+    // Test external library with version callback.
+    $library = libraries_info('example_external_version');
+    libraries_detect_library($library);
+    $this->verbose(var_export($library, TRUE));
+    $this->assertEqual($library['version'], '2', 'Library version of an external library found.');
+
     // Test loading of a simple library with a top-level files property.
     $this->drupalGet('libraries_test/simple');
-    $this->assertLibraryFiles('example_1');
+    $this->assertLibraryFiles('example_1', 'Simple library loading: ');
 
     // Test loading of integration files.
     $this->drupalGet('libraries_test/integration_files');
-    $this->assertRaw('libraries_test.js', 'The JavaScript integration file is loaded.');
-    $this->assertRaw('libraries_test.css', 'The CSS integration file is loaded.');
-    $this->assertText('libraries_test.inc', 'The PHP integration file is loaded.');
+    $this->assertRaw('libraries_test.js', 'Integration file loading: libraries_test.js found');
+    $this->assertRaw('libraries_test.css', 'Integration file loading: libraries_test.css found');
+    $this->assertRaw('libraries_test.inc', 'Integration file loading: libraries_test.inc found');
 
     // Test version overloading.
     $this->drupalGet('libraries_test/versions');
-    $this->assertLibraryFiles('example_2');
+    $this->assertLibraryFiles('example_2', 'Version overloading: ');
 
     // Test variant loading.
     $this->drupalGet('libraries_test/variant');
-    $this->assertLibraryFiles('example_3');
+    $this->assertLibraryFiles('example_3', 'Variant loading: ');
 
     // Test version overloading and variant loading.
     $this->drupalGet('libraries_test/versions_and_variants');
-    $this->assertLibraryFiles('example_4');
+    $this->assertLibraryFiles('example_4', 'Concurrent version and variant overloading: ');
+
+    // Test loading of an external library.
+    $this->drupalGet('libraries_test/external');
+    $this->assertLibraryFiles('example_1', 'External library loading: ', array('js', 'css'));
+
+    // Test the local variant of an external library.
+    $this->drupalGet('libraries_test/external_variant_local');
+    $this->assertLibraryFiles('example_1', 'Loading of a local variant of an external library: ', array('js', 'css'));
+
+    // Test the remote variant of an external library.
+    $this->drupalGet('libraries_test/external_variant_remote');
+    $this->assertLibraryFiles('example_1', 'Loading of a remote variant of an external library: ', array('js', 'css'));
   }
 
   /**
@@ -163,22 +200,27 @@ class LibrariesTestCase extends DrupalWe
    *   other files will be asserted to not be loaded. See
    *   tests/example/README.txt for more information on how the loading of the
    *   files is tested.
+   * @param $prefix
+   *   A string to prepend to the assertion messages, to make them less ambiguous.
+   * @param $extensions
+   *   (optional) The expected file extensions of $name. Defaults to
+   *   array('js', 'css', 'php').
    */
-  function assertLibraryFiles($name) {
+  function assertLibraryFiles($name, $prefix = '', $extensions = array('js', 'css', 'php')) {
     $names = drupal_map_assoc(array('example_1', 'example_2', 'example_3', 'example_4'));
     unset($names[$name]);
 
     // Test that the wrong files are not loaded.
     foreach ($names as $filename) {
-      $this->assertNoRaw("$filename.js", 'A wrong JavaScript file is not loaded.');
-      $this->assertNoRaw("$filename.css", 'A wrong CSS file is not loaded.');
-      $this->assertNoText("$filename.php", 'A wrong PHP file is not loaded.');
+      foreach ($extensions as $extension) {
+        $this->assertNoRaw("$filename.$extension", $prefix . "$filename.$extension not found");
+      }
     }
 
     // Test that the correct files are loaded.
-    $this->assertRaw("$name.js", 'The correct JavaScript file is loaded.');
-    $this->assertRaw("$name.css", 'The correct CSS file is loaded.');
-    $this->assertText("$name.php", 'The correct PHP file is loaded.');
+    foreach ($extensions as $extension) {
+      $this->assertRaw("$name.$extension", $prefix . "$name.$extension found");
+    }
   }
 
 }
Index: tests/libraries_test.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/libraries_test.css,v
retrieving revision 1.2
diff -u -p -r1.2 libraries_test.css
--- tests/libraries_test.css	23 Jul 2010 12:57:01 -0000	1.2
+++ tests/libraries_test.css	26 Oct 2010 15:36:31 -0000
@@ -4,12 +4,9 @@
  * @file
  * Test CSS file for Libraries loading.
  *
- * Because we cannot test CSS programatically with SimpleTest, the CSS below can
- * be useful for debugging with SimpleTest's verbose mode. Note that since the
- * DOM cannot be manipulated via CSS, JavaScript loading needs to be functional
- * for this to have any visible effect.
+ * Color the 'libraries-test-css' div blue. See README.txt for more information.
  */
 
-div#libraries-test {
+.libraries-test-css {
   color: purple;
 }
Index: tests/libraries_test.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/libraries_test.js,v
retrieving revision 1.3
diff -u -p -r1.3 libraries_test.js
--- tests/libraries_test.js	22 Sep 2010 17:00:56 -0000	1.3
+++ tests/libraries_test.js	26 Oct 2010 15:36:31 -0000
@@ -4,15 +4,15 @@
  * @file
  * Test JavaScript file for Libraries loading.
  *
- * Because we cannot test JavaScript programatically with SimpleTest, the
- * JavaScript below can be useful for debugging with SimpleTest's verbose mode.
+ * Replace the text in the 'libraries-test-javascript' div. See README.txt for
+ * more information.
  */
 
 (function ($) {
 
 Drupal.behaviors.librariesTest = {
   attach: function(context, settings) {
-    $('h1#page-title').after('<div id="libraries-test">If this text shows up, the JavaScript file was loaded successfully. If this text is purple, the CSS file was loaded successfully.</div>')
+    $('.libraries-test-javascript').text('If this text shows up, libraries_test.js was loaded successfully')
   }
 };
 
Index: tests/libraries_test.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/libraries_test.module,v
retrieving revision 1.6
diff -u -p -r1.6 libraries_test.module
--- tests/libraries_test.module	15 Oct 2010 15:15:53 -0000	1.6
+++ tests/libraries_test.module	26 Oct 2010 15:36:32 -0000
@@ -13,21 +13,18 @@ function libraries_test_libraries_info()
   // Test library detection.
   $libraries['example_missing'] = array(
     'title' => 'Example missing',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests/missing',
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array('1'),
   );
   $libraries['example_undetected_version'] = array(
     'title' => 'Example undetected version',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests',
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array(FALSE),
   );
   $libraries['example_unsupported_version'] = array(
     'title' => 'Example unsupported version',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests',
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array('1'),
@@ -38,7 +35,6 @@ function libraries_test_libraries_info()
 
   $libraries['example_supported_version'] = array(
     'title' => 'Example supported version',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests',
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array('2'),
@@ -47,10 +43,46 @@ function libraries_test_libraries_info()
     ),
   );
 
+  // Test external libraries.
+  $local_path = drupal_get_path('module', 'libraries_test') . '/example';
+  $remote_path = $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'libraries_test') . '/example';
+  $libraries['example_external'] = array(
+    'library path' => $remote_path,
+    'version' => '2',
+    'files' => array(
+      'js' => array('example_1.js'),
+      'css' => array('example_1.css'),
+    ),
+  );
+  $libraries['example_external_variant'] = array(
+    'version' => '2',
+    'files' => array(
+      'js' => array('example_1.js'),
+      'css' => array('example_1.css'),
+    ),
+    'variants' => array(
+      'local' => array(
+        'library path' => $local_path,
+      ),
+      'remote' => array(
+        'library path' => $remote_path,
+      ),
+    ),
+  );
+  $libraries['example_external_version'] = array(
+    'library path' => $remote_path,
+    'version callback' => 'libraries_get_version',
+    'version arguments' => array(
+      'file' => 'README.txt',
+      // Version 2
+      'pattern' => '/Version (\d+)/',
+      'lines' => 5,
+    ),
+  );
+
   // Test the default version callback.
   $libraries['example_default_version_callback'] = array(
     'title' => 'Example default version callback',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
     'version arguments' => array(
       'file' => 'README.txt',
@@ -63,7 +95,6 @@ function libraries_test_libraries_info()
   // Test a multiple-parameter version callback.
   $libraries['example_multiple_parameter_version_callback'] = array(
     'title' => 'Example_multiple_parameter_version_callback',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
     // Version 2
     'version callback' => '_libraries_test_get_version',
@@ -73,7 +104,6 @@ function libraries_test_libraries_info()
   // Test a top-level files property.
   $libraries['example_simple'] = array(
     'title' => 'Example simple',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array('1'),
@@ -95,7 +125,6 @@ function libraries_test_libraries_info()
   // these files should be automatically loaded when the library is loaded.
   $libraries['example_integration_files'] = array(
     'title' => 'Example integration files',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array('2'),
@@ -111,7 +140,6 @@ function libraries_test_libraries_info()
   // Test version overloading.
   $libraries['example_versions'] = array(
     'title' => 'Example versions',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array('2'),
@@ -136,7 +164,6 @@ function libraries_test_libraries_info()
   // Test variant detection.
   $libraries['example_variant_missing'] = array(
     'title' => 'Example variant missing',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array('2'),
@@ -155,7 +182,6 @@ function libraries_test_libraries_info()
 
   $libraries['example_variant'] = array(
     'title' => 'Example variant',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array('2'),
@@ -175,7 +201,6 @@ function libraries_test_libraries_info()
   // Test correct behaviour with multiple versions and multiple variants.
   $libraries['example_versions_and_variants'] = array(
     'title' => 'Example versions and variants',
-    // Never declare library path manually. It is detected automatically.
     'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
     'version callback' => '_libraries_test_return_version',
     'version arguments' => array('2'),
@@ -227,6 +252,10 @@ function libraries_test_libraries_info()
     ),
   );
 
+  // This library is used together with libraries_info() to be populated with
+  // the defaults.
+  $libraries['empty'] = array();
+
   return $libraries;
 }
 
@@ -234,7 +263,7 @@ function libraries_test_libraries_info()
  * Implements hook_libraries_info_file_paths()
  */
 function libraries_test_libraries_info_file_paths() {
-  return array(drupal_get_path('module', 'libraries_test') . '/example');
+  return array(drupal_get_path('module', 'libraries_test') . '/libraries_info_example');
 }
 
 /**
@@ -342,31 +371,72 @@ function libraries_test_menu() {
     'page arguments' => array('example_versions_and_variants', 'example_variant_2'),
     'access callback' => TRUE,
   );
+  $items['libraries_test/external'] = array(
+    'title' => 'Test external library',
+    'page callback' => '_libraries_test_load',
+    'page arguments' => array('example_external'),
+    'access callback' => TRUE,
+  );
+  $items['libraries_test/external_variant_local'] = array(
+    'title' => 'Test local variant of an external library',
+    'page callback' => '_libraries_test_load',
+    'page arguments' => array('example_external_variant', 'local'),
+    'access callback' => TRUE,
+  );
+  $items['libraries_test/external_variant_remote'] = array(
+    'title' => 'Test remote variant of an external library',
+    'page callback' => '_libraries_test_load',
+    'page arguments' => array('example_external_variant', 'remote'),
+    'access callback' => TRUE,
+  );
   return $items;
 }
 
 /**
- * Loads the test library with multiple versions.
+ * Loads a specified library (variant) for testing.
+ *
+ * JavaScript and CSS files can be checked directly by SimpleTest, so we only
+ * need to manually check for PHP files.
  */
 function _libraries_test_load($library, $variant = NULL) {
   libraries_load($library, $variant);
-  // JavaScript and CSS files can be checked directly by SimpleTest, so we only
-  // need to manually check for PHP files.
+
   $output = '';
-  if (function_exists('_libraries_test_example_1')) {
-    $output .= 'example_1.php';
-  }
-  if (function_exists('_libraries_test_example_2')) {
-    $output .= 'example_2.php';
-  }
-  if (function_exists('_libraries_test_example_3')) {
-    $output .= 'example_3.php';
-  }
-  if (function_exists('_libraries_test_example_4')) {
-    $output .= 'example_4.php';
-  }
-  if (function_exists('_libraries_test_integration_file')) {
-    $output .= 'libraries_test.inc';
+
+  // For easer debugging of JS loading, a text is shown that the JavaScript will
+  // replace.
+  $output .= '<h2>JavaScript</h2>';
+  $output .= '<div class="libraries-test-javascript">';
+  $output .= 'If this text shows up, no JavaScript test file was loaded.';
+  $output .= '</div>';
+
+  // For easier debugging of CSS loading, the loaded CSS files will color the
+  // following text.
+  $output .= '<h2>CSS</h2>';
+  $output .= '<div class="libraries-test-css">';
+  $output .= 'If one of the CSS test files has been loaded, this text will be colored:';
+  $output .= '<ul>';
+  // Do not reference the actual CSS files (i.e. including '.css'), because that
+  // breaks testing.
+  $output .= '<li>example_1: red</li>';
+  $output .= '<li>example_2: green</li>';
+  $output .= '<li>example_3: orange</li>';
+  $output .= '<li>example_4: blue</li>';
+  $output .= '<li>libraries_test: purple</li>';
+  $output .= '</ul>';
+  $output .= '</div>';
+
+  $output .= '<h2>PHP</h2>';
+  $output .= '<div class="libraries-test-php">';
+  $output .= 'The following is a list of all loaded test PHP files:';
+  $output .= '<ul>';
+  $files = get_included_files();
+  foreach ($files as $file) {
+    if (strpos($file, 'libraries/test') && !strpos($file, 'libraries_test.module')) {
+      $output .= '<li>' . strstr($file, 'libraries/test') . '</li>';
+    }
   }
+  $output .= '</ul>';
+  $output .= '</div>';
   return $output;
 }
Index: tests/example/example_1.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/example/example_1.css,v
retrieving revision 1.1
diff -u -p -r1.1 example_1.css
--- tests/example/example_1.css	22 Sep 2010 17:30:38 -0000	1.1
+++ tests/example/example_1.css	26 Oct 2010 15:36:32 -0000
@@ -4,10 +4,9 @@
  * @file
  * Test CSS file for Libraries loading.
  *
- * Color the 'libraries-test' div red. See example_installed.txt for more
- * information.
+ * Color the 'libraries-test-css' div red. See README.txt for more information.
  */
 
-div#libraries-test {
+.libraries-test-css {
   color: red;
 }
Index: tests/example/example_1.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/example/example_1.js,v
retrieving revision 1.1
diff -u -p -r1.1 example_1.js
--- tests/example/example_1.js	22 Sep 2010 17:30:38 -0000	1.1
+++ tests/example/example_1.js	26 Oct 2010 15:36:32 -0000
@@ -4,15 +4,15 @@
  * @file
  * Test JavaScript file for Libraries loading.
  *
- * Insert a 'libraries-test' div and some text below the page title. See
- * example_installed.txt for more information.
+ * Replace the text in the 'libraries-test-javascript' div. See README.txt for
+ * more information.
  */
 
 (function ($) {
 
 Drupal.behaviors.librariesTest = {
   attach: function(context, settings) {
-    $('h1#page-title').after('<div id="libraries-test">If this text shows up, the JavaScript file was loaded successfully. If this text is red, the CSS file was loaded successfully.</div>')
+    $('.libraries-test-javascript').text('If this text shows up, example_1.js was loaded successfully')
   }
 };
 
Index: tests/example/example_2.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/example/example_2.css,v
retrieving revision 1.1
diff -u -p -r1.1 example_2.css
--- tests/example/example_2.css	22 Sep 2010 17:30:38 -0000	1.1
+++ tests/example/example_2.css	26 Oct 2010 15:36:32 -0000
@@ -4,10 +4,9 @@
  * @file
  * Test CSS file for Libraries loading.
  *
- * Color the 'libraries-test' div green. See example_installed.txt for more
- * information.
+ * Color the 'libraries-test-css' div green. See README.txt for more information.
  */
 
-div#libraries-test {
+.libraries-test-css {
   color: green;
 }
Index: tests/example/example_2.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/example/example_2.js,v
retrieving revision 1.1
diff -u -p -r1.1 example_2.js
--- tests/example/example_2.js	22 Sep 2010 17:30:38 -0000	1.1
+++ tests/example/example_2.js	26 Oct 2010 15:36:32 -0000
@@ -4,15 +4,15 @@
  * @file
  * Test JavaScript file for Libraries loading.
  *
- * Insert a 'libraries-test' div and some text below the page title. See
- * example_installed.txt for more information.
+ * Replace the text in the 'libraries-test-javascript' div. See README.txt for
+ * more information.
  */
 
 (function ($) {
 
 Drupal.behaviors.librariesTest = {
   attach: function(context, settings) {
-    $('h1#page-title').after('<div id="libraries-test">If this text shows up, the JavaScript file was loaded successfully. If this text is green, the CSS file was loaded successfully.</div>')
+    $('.libraries-test-javascript').text('If this text shows up, example_2.js was loaded successfully')
   }
 };
 
Index: tests/example/example_3.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/example/example_3.css,v
retrieving revision 1.1
diff -u -p -r1.1 example_3.css
--- tests/example/example_3.css	22 Sep 2010 17:30:38 -0000	1.1
+++ tests/example/example_3.css	26 Oct 2010 15:36:32 -0000
@@ -4,10 +4,9 @@
  * @file
  * Test CSS file for Libraries loading.
  *
- * Color the 'libraries-test' div orange. See example_installed.txt for more
- * information.
+ * Color the 'libraries-test-css' div orange. See README.txt for more information.
  */
 
-div#libraries-test {
+.libraries-test-css {
   color: orange;
 }
Index: tests/example/example_3.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/example/example_3.js,v
retrieving revision 1.1
diff -u -p -r1.1 example_3.js
--- tests/example/example_3.js	22 Sep 2010 17:30:38 -0000	1.1
+++ tests/example/example_3.js	26 Oct 2010 15:36:32 -0000
@@ -4,15 +4,15 @@
  * @file
  * Test JavaScript file for Libraries loading.
  *
- * Insert a 'libraries-test' div and some text below the page title. See
- * example_installed.txt for more information.
+ * Replace the text in the 'libraries-test-javascript' div. See README.txt for
+ * more information.
  */
 
 (function ($) {
 
 Drupal.behaviors.librariesTest = {
   attach: function(context, settings) {
-    $('h1#page-title').after('<div id="libraries-test">If this text shows up, the JavaScript file was loaded successfully. If this text is orange, the CSS file was loaded successfully.</div>')
+    $('.libraries-test-javascript').text('If this text shows up, example_3.js was loaded successfully')
   }
 };
 
Index: tests/example/example_4.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/example/example_4.css,v
retrieving revision 1.1
diff -u -p -r1.1 example_4.css
--- tests/example/example_4.css	22 Sep 2010 17:30:38 -0000	1.1
+++ tests/example/example_4.css	26 Oct 2010 15:36:32 -0000
@@ -4,10 +4,9 @@
  * @file
  * Test CSS file for Libraries loading.
  *
- * Color the 'libraries-test' div blue. See example_installed.txt for more
- * information.
+ * Color the 'libraries-test-css' div blue. See README.txt for more information.
  */
 
-div#libraries-test {
+.libraries-test-css {
   color: blue;
 }
Index: tests/example/example_4.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/example/example_4.js,v
retrieving revision 1.1
diff -u -p -r1.1 example_4.js
--- tests/example/example_4.js	22 Sep 2010 17:30:38 -0000	1.1
+++ tests/example/example_4.js	26 Oct 2010 15:36:32 -0000
@@ -4,15 +4,15 @@
  * @file
  * Test JavaScript file for Libraries loading.
  *
- * Insert a 'libraries-test' div and some text below the page title. See
- * example_installed.txt for more information.
+ * Replace the text in the 'libraries-test-javascript' div. See README.txt for
+ * more information.
  */
 
 (function ($) {
 
 Drupal.behaviors.librariesTest = {
   attach: function(context, settings) {
-    $('h1#page-title').after('<div id="libraries-test">If this text shows up, the JavaScript file was loaded successfully. If this text is blue, the CSS file was loaded successfully.</div>')
+    $('.libraries-test-javascript').text('If this text shows up, example_4.js was loaded successfully')
   }
 };
 
Index: tests/libraries_info_example/libraries_info_example.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/libraries/tests/libraries_info_example/libraries_info_example.info,v
retrieving revision 1.1
diff -u -p -r1.1 libraries_info_example.info
--- tests/libraries_info_example/libraries_info_example.info	15 Oct 2010 16:11:08 -0000	1.1
+++ tests/libraries_info_example/libraries_info_example.info	26 Oct 2010 15:36:32 -0000
@@ -2,7 +2,7 @@
 
 ; This is an example info file of a library used for testing purposes.
 ; Do not declare name manually. It is set automatically.
-name = example_info_file
+name = libraries_info_example
 title = Example info file
 
 ; Because Drupal thinks this is a module's .info file, it is in the 'libraries'
