0

I was using jQuery Update module, but it conflicts with Views and feature requests for library version per page or per theme are consistently killed. On the other side, Views maintainers seems to assume that if UI works with distribution's jQuery, it isn't broken. Long story short, I was forced to forget about jQuery Update the way we know it.

Solution I found is jQuery Multi module, as it allows load updated jQuery only, if there is a library that requires it in use on the page. And for things like Chosen (when jQuery Update is hacked out of requirements) it works great. Now, I have problem with javascripts directly attached to forms:

$form['#attached']['js'] = array(
  drupal_get_path('module', 'aat_pc_product') . '/js/tag.js',
  drupal_get_path('module', 'aat_pc_product') . '/js/tabs.js',
  drupal_get_path('module', 'aat_pc_product') . '/js/categories.js',
);

jQuery Multi claims it can be used on any js file, but it's documentation says:

C) Put the JS anywhere else, like in a custom module, and use hook_jqmulti_files() to load it. No need to use the alias. 38

D) Put the JS anywhere, use the provided alias, and don't use the jqmulti hooks or UI. Simply load with drupal_add_js() or something similar. If you're only using this method, make sure to select the 'Load Always' checkbox in the jQuery Multi config.

Now, I can't do C) as I'm using Form API and I can't do D) as it breaks Views.

Form API for #attached says:

Description: Allows loading of CSS, Javascript, libraries,

Converting my scripts to a libraries would help, as A) and B) options for jQuery Multi are for libraries, but only examples provided by Form API are for JS and CSS files.

There is one use-case for libraries provided:

$form['#attached']['library'][] = array('system', 'jquery.cookie');

but it's there without description.

I can make my scripts library:

function amymodule_library() {
  $module_path = drupal_get_path('module', 'mymodule');
  $items = array();
  $items['mymodule_form'] = array(
    'title' => t('My Module Form'),
    'version' => '1.0.0',
    'js' => array(
        $module_path . '/js/tag.js' => array(),
        $module_path . '/js/tabs.js' => array(),
        $module_path . '/js/categories.js' => array(),
    ),
    'css' => array(
      $module_path . '/css/mymodule_form.css' => array(),
    ),
  );
  // dpr($items);
  // die();
  return $items;
}

And attach it as a library to a form:

$form['#attached']['library'] = array(
  array('mymodule', 'mymodule_form'),
);

Sadly this does not make it appear on the configuration screen:

jQuery Multi Settings

I know it's due to the first 2 methods described in documentation:

A) Put the JS in sites/all/libraries/ and use the UI at admin/config/system/jqmulti to select your library. All the library's JS files will be loaded with the jqmulti jQuery version. There is no need to use the alias in this case. This is ideal if you're using a prepackaged jQuery library or plugin.

B) Put the JS in sites/all/libraries/ and use hook_jqmulti_libraries() to include the library. No need to use the alias. This is ideal if you're using a prepackaged jQuery library or plugin, and is equivalent to method A.

Of course I cannot put my scripts there, they are built in part of my module.

Now, I'm perfectly open to module hacking, if that's what needed, but if there is a way to "pretend" that my module's library is in the folder jQuery Multi wants it to be, I would be grateful to know it.

  • When I was doing my best to ask a wise question, I made this work. So answer below. But I hope to see other answers, if there is something fundamental I'm missing. – Mołot Oct 7 '13 at 14:40
1

Solution seems to be to hack jQuery Multi module.

First, we want to add module-provided libraries to the list in configuration options. Function responsible for it is jqmulti_get_available_libraries():

/**
 * Returns available libraries to be used in the admin form.
 */
function jqmulti_get_available_libraries() {
  $libs = libraries_get_libraries();

  unset($libs['jquery']);
  foreach ($libs as $key => $value) {
    $libs[$key] = $key;
  }
  return $libs;
}

To display module-provided libraries, we need to add following line:

$libs += module_invoke_all('library');

Of course now it looks ugly:

Screenshot

List is long and hard to understand, because these are machine names only. Red colour used to distinguish element I added by hand after taking screenshot, it's all black folks.

Form element is defined in jqmulti.admin.inc, but it's the only place that ever uses jqmulti_get_available_libraries(), so we may stay inside it instead, to limit changes to narrow area. If we change this:

  foreach ($libs as $key => $value) {
    $libs[$key] = $key;
  }

to this:

foreach ($libs as $key => $value) {
  if(isset($value['title'])) {
    $libs[$key] = '<strong>' . $value['title'] . '</strong> ' . @$value[version] . ' (' . $key . ')';
  } else {
    $libs[$key] = '<strong>' . $key . '</strong> (' . $value . ')';
  }
}

output will get a lot more readable:

Screenshot after changes

Again, blanked a project-related row but it is there all right.

But now the fun part begins. I can save, but logic is all wrong:

  • Warning: scandir(sites/all/libraries/product_form): failed to open dir: No such file or directory in jqmulti_find_all_js_files() (line 180 of /var/www/sites/all/modules/jqmulti/jqmulti.module).
  • Warning: scandir(): (errno 0): Success in jqmulti_find_all_js_files() (line 180 of /var/www/sites/all/modules/jqmulti/jqmulti.module).
  • Warning: Invalid argument supplied for foreach() in jqmulti_find_all_js_files() (line 181 of /var/www/sites/all/modules/jqmulti/jqmulti.module).

Error is located in function jqmulti_find_all_js_files($dir) function. Obviously module still tries to use old way of scanning /sites/all/libraries even when I clicked a library that is not there. Function is called from jqmulti_get_library_files($library, $reset = FALSE) that blindly assume all files are in 'sites/all/libraries'. First, it's a bug in it's own right, second it's not sufficient. We need to change:

$base_path = 'sites/all/libraries';

to something smarter. For example:

if(isset($libraries_libraries[$library])) { // Give precedent to old way
  $path = $libraries_libraries[$library];
} elseif(isset($module_libraries[$library])) {
  $path = NULL;
  $files = array_keys($module_libraries[$library]['js']);
} else { // Fallback to the old way, for what it's worth
   $path = $base_path . '/' . $library;
}

As you can see, if module gave us files already, there is no need to play with $path, we may use it directly. So code below needs to be wrapped in if.

That's all, now it works. Without conflict with Views, but with all jQuery Multi's own issues, some especially urgent for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.