You are here

jqp.module in jQuery Plugin Handler (JQP) 6.2

Same filename and directory in other branches
  1. 6 jqp.module

Used to register and load javascript libraries and plugins from a cetral point

File

jqp.module
View source
<?php

/**
 * @file
 * Used to register and load javascript libraries and plugins from a cetral point
 */

/**
 * Implementation of hook_help().
 */
function jqp_help($path, $arg) {
  switch ($path) {
    case 'admin/help#jqp':
      $output[] = '<p>' . t('Javascript libraries can either be built up from an single javascript file, a javascript plugin or a set of javascript files, sometimes with stylesheets included. These libraries can be used to extend the look and feel of modules.') . '</p>';
      $output[] = '<p>' . t('Libraries can be registered in several ways:') . '</p>';
      $output[] = '<ul>';
      $output[] = '  <li>';
      $output[] = '<h4>' . t('.info files') . '</h4>';
      $output[] = '<p>' . t('This is the preferred way of registering a library into javascript libraries\' cache. During installation, jQp module will check for the presence of a js_libraries folder in one of the following directories:') . '</p>';
      $output[] = '<ul>';
      $output[] = '  <li>js_libraries</li>';
      $output[] = '  <li>sites/all/js_libraries</li>';
      $output[] = '  <li>$profile/install_profile/js_libraries</li>';
      $output[] = '  <li>$conf_path/js_libraries</li>';
      $output[] = '  <li>$file_directory_path/js_libraries</li>';
      $output[] = '</ul>';
      $output[] = '<p>' . t('If this directory exists, this should be the place to place all libraries as separate folders, containing all required files for the library. If a library folder contains a .info file, this will be used to register the library. Here\'s an example of how a .info file should be written:', $replacements) . '</p>';
      $output[] = '<pre>';
      $output[] = '; $Id$';
      $output[] = 'name = Library Name';
      $output[] = 'description = Description of the library';
      $output[] = 'project_url = http://www.library_project_page.org';
      $output[] = 'stylesheets[][] = style.css';
      $output[] = 'scripts[][] = jquery.test.min.js';
      $output[] = 'scripts[1.4.4][] = jquery.test[1.4.4].min.js';
      $output[] = 'scripts[1.4.4][] = drupal.js';
      $output[] = '</pre>';
      $output[] = '<p>' . t('This .info file will register two versions of this library (default and 1.4.4). style.css is added as a first element of the stylesheets array, and will therefore will have 0 as key. All elements of both the stylesheets and scripts array with a 0 key will be treated as the default version. Files with other keys, such as 1.4.4, will be handled as separate versions. If a specific version of a library is loaded, and there\'s no file type which overrides the default one, the default one is included automatically. Note that versions can include several files!') . '</p>';
      $output[] = '</li>';
      $output[] = '<li>';
      $output[] = '<h4>' . t('hook_jqp()') . '</h4>';
      $output[] = '<p>' . t('Modules can also register their own libraries using the provided hook function.') . '</p>';
      $output[] = '<p><strong>' . t('Definition') . ':</strong></p>';
      $output[] = '<p><code>hook_jqp(&amp;$js_libraries)</code></p>';
      $output[] = '<p><strong>' . t('Description') . ':</strong></p>';
      $output[] = '<p>' . t('Provide other modules a hook to add custom libraries or alter already registered libraries.') . '</p>';
      $output[] = '<p><strong>' . t('Parameters') . ':</strong></p>';
      $output[] = '<p><code>$js_libraries</code>: ' . t('An associative array in which the library register is built up.') . '</p>';
      $output[] = '<p><strong>' . t('Code') . ':</strong></p>';
      $output[] = '<pre class="php">';
      $output[] = '/**';
      $output[] = ' * Implemantation of hook_jqp().';
      $output[] = ' */';
      $output[] = 'function hook_jqp(&amp;$js_libraries) {';
      $output[] = '  $js_libraries[\'plugin1\'] = array(';
      $output[] = '    \'name\' => \'Plugin 1\',';
      $output[] = '    \'description\' => \'This plugin is registered by a module.\',';
      $output[] = '    \'project_url\' => \'http://www.library_project_page.org\',';
      $output[] = '    \'scripts\' => array(';
      $output[] = '      array(\'jquery.hoi.min.js\'), // This will have key 0, so it is the default version';
      $output[] = '      \'1.4.3-BETA-3\' => array(\'jquery.hoi[2].min.js\'),';
      $output[] = '    ),';
      $output[] = '    \'stylesheets\' => array(';
      $output[] = '      array(\'plugin1.css\'),';
      $output[] = '      \'1.4.3-BETA-3\' => array(';
      $output[] = '        \'plugin1[1.4.3-BETA-3].css\'';
      $output[] = '      ),';
      $output[] = '    ),';
      $output[] = '  );';
      $output[] = '}';
      $output[] = '</pre>';
      $output[] = '</li>';
      $output[] = '<li>';
      $output[] = '<h4>' . t('Loading a javascript library') . '</h4>';
      $output[] = '<p>' . t('Loading a javascript library or plugin can be done using drupal_add_js_library().') . '</p>';
      $output[] = '<p><strong>' . t('Definition') . ':</strong></p>';
      $output[] = '<p><code>drupal_add_js_library($name, $version = 0, $options = array())</code></p>';
      $output[] = '<p><strong>' . t('Description') . ':</strong></p>';
      $output[] = '<p>' . t('Load a shared library from the javascript library directory.') . '</p>';
      $output[] = '<p><strong>' . t('Parameters') . ':</strong></p>';
      $output[] = '<p><code>$name</code>: ' . t('(required)') . "&nbsp;" . t('the name of the library or plugin to load.') . '</p>';
      $output[] = '<p><code>$version</code>: ' . t('(optional)') . "&nbsp;" . t('the version of the library to load. If omitted the default version is loaded.') . '</p>';
      $output[] = '<p><code>$options</code>: ' . t('(optional)') . "&nbsp;" . t('Array containing additional options to pass to drupal_add_js or drupal_add_css.') . '</p>';
      $output[] = '<pre class="php">';
      $output[] = '     array(';
      $output[] = '       \'css\' => array($type = \'module\', $media = \'all\', $preprocess = TRUE),';
      $output[] = '       \'js\' => array($type = \'module\', $scope = \'header\', $defer = FALSE, $cache = TRUE, $preprocess = TRUE),';
      $output[] = '     );';
      $output[] = '</pre>';
      $output[] = '<p><strong>' . t('Example') . ':</strong></p>';
      $output[] = '<p><code>drupal_add_js_library(\'beautytips\', \'0.9\', array(\'css\' => array(\'theme\', \'all\', FALSE)));</code></p>';
      $output[] = '</li>';
      $output[] = '<li>';
      $output[] = '<h4>' . t('Administration pages') . '</h4>';
      $output[] = '<p>' . t('After registering a library, the paths of the attached files can be changed at the !admin_pages. It is also possible to attach an detach files per version.', array(
        '!admin_pages' => l('administration pages', 'admin/build/jqp'),
      )) . '</p>';
      $output[] = '</li>';
      $output[] = '</ul>';
      return join("\n", $output);
    case 'admin/build/jqp':
      $output[] = '<p>' . t('Javascript libraries can either be built up from an single javascript file, a javascript plugin or a set of javascript files, sometimes with stylesheets included. These libraries can be used to extend the look and feel of modules.') . '</p>';
      $output[] = '<p>' . t('After registering a library, the paths of the attached files can be changed here. It is also possible to attach and detach files per version by clicking on the configure link.') . '</p>';
      $output[] = '<p>' . t('You can completely rebuild the cache by clicking !rebuild link.', array(
        '!rebuild' => l(t('this'), 'admin/build/jqp/rebuild_confirm'),
      )) . '</p>';
      return join("\n", $output);
  }
}

/**
 * Implementation of hook_perm().
 */
function jqp_perm() {
  return array(
    'administer javascript libraries',
  );
}

/**
 * Implementation of hook_menu().
 */
function jqp_menu() {
  $items['admin/build/jqp'] = array(
    'title' => 'Javascript libraries',
    'description' => "Administer javascript libraries.",
    'page callback' => 'jqp_status_page',
    'access arguments' => array(
      'administer javascript libraries',
    ),
    'file' => 'jqp.admin.inc',
  );
  $items['admin/build/jqp/rebuild_confirm'] = array(
    'title' => 'Reset all libraries',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'jqp_confirm_form',
    ),
    'access arguments' => array(
      'administer javascript libraries',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'jqp.admin.inc',
  );
  $items['admin/build/jqp/%js_library/%'] = array(
    'title callback' => 'jqp_overview_title',
    'title arguments' => array(
      3,
      4,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'js_library_edit',
      3,
      4,
    ),
    'access arguments' => array(
      'administer javascript libraries',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'jqp.admin.inc',
  );
  $items['admin/build/jqp/%js_library/%/list'] = array(
    'title' => 'List files',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
    'file' => 'jqp.admin.inc',
  );
  $items['admin/build/jqp/%js_library/%/reset'] = array(
    'title' => 'Reset library',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'jqp_confirm_form',
      3,
      4,
    ),
    'access arguments' => array(
      'administer javascript libraries',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'jqp.admin.inc',
  );
  $items['admin/build/jqp/%js_library/%/remove_file'] = array(
    'title' => 'Reset library',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'jqp_confirm_form',
      3,
      4,
    ),
    'access arguments' => array(
      'administer javascript libraries',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'jqp.admin.inc',
  );
  $items['admin/build/jqp/%js_library/%/add'] = array(
    'title' => 'Add file',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'js_library_edit',
      3,
      4,
      5,
    ),
    'access arguments' => array(
      'administer javascript libraries',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'jqp.admin.inc',
  );
  $items['jqp_ajax_check_file'] = array(
    'page callback' => '_jqp_ajax_check_file',
    'access arguments' => array(
      'administer javascript libraries',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'jqp.admin.inc',
  );
  $items['jqp_autocomplete'] = array(
    'page callback' => '_jqp_autocomplete',
    'access arguments' => array(
      'administer javascript libraries',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'jqp.admin.inc',
  );
  return $items;
}

/**
 * Implementation of hook_theme().
 */
function jqp_theme() {
  return array(
    'js_library_edit' => array(
      'arguments' => array(
        'form' => array(),
        'js_library' => array(),
        'version' => 0,
      ),
    ),
  );
}

/**
 * Title callback.
 */
function jqp_overview_title($js_library, $version = NULL) {
  return $js_library->info['name'] . ($version ? " v.{$version}" : "");
}

/**
 * Add a shared library from the javascript library directory
 *
 * @param $name
 *   the name of the library to add
 *
 * @param $version
 *   the version of the library to add
 *
 * @param $options
 *   Array containing additional options to pass to drupal_add_js or drupal_add_css.
 *     array(
 *       'css' => array($path = NULL, $type = 'module', $media = 'all', $preprocess = TRUE),
 *       'js' => array($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE, $preprocess = TRUE),
 *     );
 *
 * @see drupal_add_js()
 * @see drupal_add_css()
 */
function drupal_add_js_library($name, $version = 0, $options = array()) {

  // Get the library
  $js_library = js_library_load($name);
  $types = array(
    'js' => 'scripts',
    'css' => 'stylesheets',
  );
  $files = array();

  // Loop trough the file types
  foreach ($types as $key => $type) {

    // Add the files of the default version, if exist
    if ($version !== 0 && is_array($js_library->info[$type][0])) {
      $files[$key] = $js_library->info[$type][0];
    }
    if (is_array($js_library->info[$type][$version])) {
      $files[$key] = $js_library->info[$type][$version];
    }
  }

  // Load all files using drupal_add_js and drupal_add_css
  foreach ($types as $key => $type) {
    if (!$files[$key]) {
      continue;
    }
    foreach ($files[$key] as $path) {

      //trace("$path $key");
      $args[0] = $path;
      $args = array_merge($args, $options);
      call_user_func_array("drupal_add_{$key}", $args);
    }
  }
}

/**
 * Scan the 'js_libraries', 'modules' and 'themes' directories for .js and .css files
 *
 * @return an array where key is file name and value is file path
 *
 *   Example:
 *     array('sites/all/modules/cck/content.js' => (object)array(
 *       'filename' => 'sites/all/modules/cck/content.js',
 *       'basename' => 'content.js',
 *       'name' => 'content',
 *     );
 *
 *  LIKE MODULES, javascript libraries should have unique file names
 *
 * @see _jqp_autocomplete()
 */
function jqp_scan_dir() {
  static $files;
  if (!isset($files)) {
    if ($cache = cache_get('jqp')) {
      $files = $cache->data;
    }
    else {
      $profile = variable_get('install_profile', 'default');
      $config = conf_path();
      $file_dir = file_directory_path();
      $files = array();
      foreach (array(
        'modules',
        'js_libraries',
        'themes',
      ) as $directory) {
        $searchdir[] = "{$directory}";
        $searchdir[] = "sites/all/{$directory}";
        $searchdir[] = "profiles/{$profile}/{$directory}";
        $searchdir[] = "{$config}/{$directory}";
        $searchdir[] = "{$file_dir}/{$directory}";
      }
      foreach ($searchdir as $dir) {
        if (file_exists($dir)) {
          $files = array_merge($files, file_scan_directory($dir, '(\\.js$|\\.css$)', array(
            '.',
            '..',
            'CVS',
          ), 0, TRUE, 'filename', 0));
        }
      }
      cache_set('jqp', $files);
    }
  }
  return $files;
}

/**
 * Implementation of hook_requirements().
 *
 * Checks if a js_libraries directory is present at install
 */
function jqp_requirements($phase) {

  // Ensure translations don't break at install time
  $t = get_t();

  // Report Drupal version
  if ($phase == 'runtime') {
    $library_exists = _check_js_library_directory('js_libraries');
    $requirements['jqp'] = array(
      'title' => $t('Javascript libraries directory'),
      'value' => $library_exists ? $t('Exists') : $t('Not present'),
      'severity' => $library_exists ? REQUIREMENT_OK : REQUIREMENT_WARNING,
    );
    if (!$library_exists) {
      $requirements['jqp']['description'] = $t('There\'s no javascript libraries folder present yet! Please create a directory called "js_libraries" at sites/all/js_libraries or sites/[example.com]/js_libraries or similar.');
    }
  }
  return $requirements;
}

/**
 * Searches for a js_libraries directory
 */
function _check_js_library_directory($directory) {
  global $profile;
  $config = conf_path();
  $searchdir = array(
    "{$directory}",
    "sites/all/{$directory}",
    "profiles/{$profile}/{$directory}",
    "{$config}/{$directory}",
  );
  foreach ($searchdir as $dir) {
    if (file_exists($dir)) {
      variable_set('jqp_directory', $dir);
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Rebuilds the javascript libraries cache.
 *
 * It registers both all libraries defined in library info files,
 * as well as libraries defined in hook_jqp.
 *
 * @param $reset
 *   If true, all javascript libraries are completely reindexed.
 *   Otherwise only libraries which havent been costumized by administrators are reindexed.
 */
function jqp_rebuild_cache($reset = FALSE) {
  cache_clear_all('jqp', 'cache');

  // Set defaults for library info
  $defaults = array(
    'description' => '',
    'version' => NULL,
  );

  // The following code is merely copied from drupal_system_listing.
  // Added the file_directory to be a possible location for the library directory.
  $profile = variable_get('install_profile', 'default');
  $directory = 'js_libraries';
  $config = conf_path();
  $file_dir = file_directory_path();
  $js_libraries = array();
  $searchdir = array(
    "{$directory}",
    "sites/all/{$directory}",
    "profiles/{$profile}/{$directory}",
    "{$config}/{$directory}",
    "{$file_dir}/{$directory}",
  );

  // Get current list of libraries registerd by dot-info files
  foreach ($searchdir as $dir) {
    if (file_exists($dir)) {
      $js_libraries = array_merge($js_libraries, file_scan_directory($dir, '\\.info$', array(
        '.',
        '..',
        'CVS',
      ), 0, TRUE, 'name', 0));
    }
  }

  // Get all libraries registerd by modules
  $module_libraries = module_invoke_all('jqp');
  if (is_array($module_libraries)) {
    foreach ($module_libraries as $module_library_name => $module_library) {
      $js_libraries[$module_library_name] = (object) ($module_library + array(
        'type' => 'javascript library',
      ));
    }
  }

  // Extract current files from database.
  system_get_files_database($js_libraries, 'javascript library');
  ksort($js_libraries);
  foreach ($js_libraries as $name => $library) {
    $versions_changed = array();

    // Look for the info file.
    $library->info = drupal_parse_info_file(dirname($library->filename) . '/' . $library->name . '.info');

    // If the library doesn't provide info, we're dealing with an module registered library.
    // We'll create the info manually.
    if (empty($library->info)) {
      $info = clone $library;
      unset($info->type, $info->info, $js_libraries[$name]->scripts, $js_libraries[$name]->styles);
      $js_libraries[$name]->info = (array) $info;
      $js_libraries[$name]->name = $name;
      $js_libraries[$name]->filename = "{$name}-module-jqp";
    }

    // Merge in defaults and save.
    $js_libraries[$name]->info = $library->info + $defaults;

    // Used to merge saved data with loaded data
    $old_library = js_library_load($name);

    // Loop trough each file type
    foreach (array(
      'stylesheets',
      'scripts',
    ) as $type) {

      // Continue if no data is found for this file type
      if (empty($js_libraries[$name]->info[$type])) {
        continue;
      }
      $files = array();

      // Get the data of each file type per version
      foreach ($js_libraries[$name]->info[$type] as $version => $data) {

        // Check whether this version have been madified and saved before
        if (isset($old_library->info[$type][$version]['changed']) && $reset === FALSE) {
          $versions_changed[] = $version;
        }
        foreach ($data as $file_name) {
          $dirname = file_exists($js_libraries[$name]->filename) ? dirname($js_libraries[$name]->filename) : '';
          $files[$version][$file_name] = $dirname ? "{$dirname}/{$file_name}" : $file_name;

          // Let's store the basepath of the dot-info file
          $library->info['base'] = $dirname ? $dirname : NULL;
        }
      }
      $js_libraries[$name]->info[$type] = $files;
    }
    if (!empty($versions_changed)) {
      foreach ($versions_changed as $version_changed) {
        if (isset($js_libraries[$name]->info['scripts'][$version_changed])) {
          $js_libraries[$name]->info['scripts'][$version_changed] = $old_library->info['scripts'][$version_changed];
        }
        if (isset($js_libraries[$name]->info['stylesheets'][$version_changed])) {
          $js_libraries[$name]->info['stylesheets'][$version_changed] = $old_library->info['stylesheets'][$version_changed];
        }
      }
    }

    // Invoke hook_system_info_alter() to give installed modules a chance to
    // modify the data in the .info files if necessary.
    drupal_alter('system_info', $js_libraries[$name]->info, $js_libraries[$name]);

    // Update the contents of the system table:
    if (isset($library->status) || isset($library->old_filename) && $library->old_filename != $library->filename) {
      db_query("UPDATE {system} SET info = '%s', name = '%s', filename = '%s', bootstrap = %d WHERE filename = '%s'", serialize($js_libraries[$name]->info), $library->name, $library->filename, $bootstrap, $library->old_filename);
    }
    else {

      // This is a new library.
      db_query("INSERT INTO {system} (name, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)", $name, serialize($js_libraries[$name]->info), 'javascript library', $js_libraries[$name]->filename, 0, 0, $bootstrap);
    }
  }
  return $js_libraries;
}

/**
 * Fetches all cached libraries from the system table
 */
function jqp_list($refresh = FALSE) {
  static $list;
  if ($refresh) {
    $list = array();
  }
  if (empty($list)) {
    $list = array();
    $js_libraries = array();
    $result = db_query("SELECT * FROM {system} WHERE type = '%s'", 'javascript library');
    while ($library = db_fetch_object($result)) {
      $library->info = unserialize($library->info);
      $js_libraries[] = $library;
      foreach (array(
        'stylesheets',
        'scripts',
      ) as $type) {
        if (empty($library->info[$type])) {
          continue;
        }
        foreach ($library->info[$type] as $version => $data) {
          foreach ($data as $file_name => $path) {
            if (file_exists($path)) {
              $library->{$type}[$version][$file_name] = $path;
            }
          }
        }
      }
      $list[$library->name] = $library;
    }
  }
  return $list;
}

/**
 * Loads a single library
 *
 * @param $name
 *   the name of the library to add
 */
function js_library_load($name) {
  static $js_libraries;
  if (empty($js_libraries)) {
    $js_libraries = jqp_list();
  }
  return $js_libraries[$name];
}

/**
 * Implementation of hook_jqp().
 *
 * ONLY FOR TESTING PURPOSES!!!
 */
function jqp_jqp() {
  $js_libraries['dummy_plugin1'] = array(
    'name' => 'Dummy plugin 1',
    'description' => 'This plugin is registered by the jqp module.',
    'project_url' => 'http://www.library_project_page.org',
    'scripts' => array(
      array(
        'jquery.dummy.min.js',
      ),
      '1.4.3-BETA-3' => array(
        'jquery.dummy[1.4.3-BETA-3].min.js',
      ),
    ),
    'stylesheets' => array(
      array(
        'styles.css',
      ),
      '1.4.3-BETA-3' => array(
        'styles[1.4.3-BETA-3].css',
      ),
    ),
  );
  $js_libraries['dummy_plugin2'] = array(
    'name' => 'Dummy plugin 2',
    'description' => 'This plugin is also registered by the jqp module.',
    'project_url' => 'http://www.library_project_page.org',
    'scripts' => array(
      array(
        'jquery.dummy2.min.js',
      ),
      '2' => array(
        'jquery.dummy2[2].min.js',
      ),
    ),
  );
  return $js_libraries;
}

Functions

Namesort descending Description
drupal_add_js_library Add a shared library from the javascript library directory
jqp_help Implementation of hook_help().
jqp_jqp Implementation of hook_jqp().
jqp_list Fetches all cached libraries from the system table
jqp_menu Implementation of hook_menu().
jqp_overview_title Title callback.
jqp_perm Implementation of hook_perm().
jqp_rebuild_cache Rebuilds the javascript libraries cache.
jqp_requirements Implementation of hook_requirements().
jqp_scan_dir Scan the 'js_libraries', 'modules' and 'themes' directories for .js and .css files
jqp_theme Implementation of hook_theme().
js_library_load Loads a single library
_check_js_library_directory Searches for a js_libraries directory