drupal_add_js includes settings twice

walkah - January 10, 2008 - 22:43
Project:Drupal
Version:7.x-dev
Component:javascript
Category:bug report
Priority:normal
Assigned:Unassigned
Status:needs work
Description

When creating a CCK formatter with token.module installed the formatter is included twice, thus causing 2 calls to drupal_add_js() with a 'setting' parameter. This creates the setting to be included twice which can break certain javascript files.

#1

Jody Lynn - January 10, 2008 - 23:19
Status:active» needs review

_drupal_add_js does not do anything to prevent settings or inline types of js from being called more than once. Patch is an idea to do so for the settings.

AttachmentSizeStatusTest resultOperations
js_settings.patch761 bytesIgnoredNoneNone

#2

DougKress - January 12, 2008 - 00:57

Here is a slightly different approach than Lynn's.

AttachmentSizeStatusTest resultOperations
_drupal_add_js_settings.patch781 bytesIgnoredNoneNone

#3

dvessel - January 21, 2008 - 23:10
Status:needs review» needs work

Shouldn't this be applied to both settings and inline? And which is faster? The hash or array_search?

#4

Jody Lynn - January 22, 2008 - 22:19
Version:5.5» 5.x-dev
Status:needs work» needs review

I agree it should be applied to inline js as well. I think Doug's hash method is a little better and more analogous to what is already done for external js files.

AttachmentSizeStatusTest resultOperations
js-once.patch959 bytesIgnoredNoneNone

#5

drumm - February 11, 2008 - 06:26
Version:5.x-dev» 7.x-dev
Status:needs review» needs work

I would like to see this committed to the development version first, and then backported as needed. The code looks okay on reading, except the first added lines are indented with tabs instead of spaces. The patch does apply to HEAD.

#6

DougKress - May 27, 2008 - 23:18

Here's the HEAD patch with the suggested adjustments.

Thanks,

Doug

AttachmentSizeStatusTest resultOperations
drupal_add_js-HEAD.patch938 bytesIdleFailed: Failed to apply patch.View details | Re-test

#7

DougKress - June 10, 2008 - 17:48
Status:needs work» needs review

Forgot to change the status with my last post.

#8

Dries - June 12, 2008 - 19:37

Mmm, I'm not sure about this solution. Why are we adding it with a different key? It's still in the array twice.

#9

mfer - June 14, 2008 - 16:33

This is strange. I think the root of the problem is in drupal_get_js where we have:

<?php
 
switch ($type) {
      case
'setting':
       
$output .= '<script type="text/javascript">jQuery.extend(Drupal.settings, ' . drupal_to_js(call_user_func_array('array_merge_recursive', $data)) . ");</script>\n";
        break;
?>

It looks like the intent of call_user_func_array('array_merge_recursive', $data) is to roll-up the arrays and their settings so this condition doesn't happen. This might be a good place to start looking. Why isn't this eliminating the duplicates?

#10

mfer - June 14, 2008 - 17:15

The method used in #6 will only work if it's the exact same setting call.

For example, it would catch this:

<?php
  drupal_add_js
(array('myvar' => 'value'), 'setting');
 
drupal_add_js(array('myvar' => 'value'), 'setting');
?>

But not this:

<?php
  drupal_add_js
(array('myvar' => 'value'), 'setting');
 
drupal_add_js(array('myvar' => array('value', 'value 2')), 'setting');
?>

The hashes would be different so 'value' would still be added twice.

We almost need an array_merge_recursive_unique function that knows the difference between numbers and associative keys and only does performs unique calls on numbers.

#11

Dries - June 15, 2008 - 21:30

@#10, mfer, the expected behavior would be for drupal_add_js(array('myvar' => array('value', 'value 2')), 'setting'); to overwrite the previous value set by drupal_add_js(array('myvar' => 'value'), 'setting');, not?

It looks like the problem is easily solved by changing the way $data is stored internally. In case of settings, we need to extract keys and values, and overwrite duplicate keys. It looks to me like drupal_add_js() is doing too much at once. This problem is easily solved by breaking up the function and defining a better API like drupal_add_js_setting($key, $value, $defer = FALSE, ...). Such function could easily solve duplicate keys, and is also easier to grok.

#12

mfer - June 16, 2008 - 00:19

@dries - the actual behavior of:

<?php
  drupal_add_js
(array('myvar' => 'value'), 'setting');
 
drupal_add_js(array('myvar' => array('value', 'value 2')), 'setting');
?>

is an array that looks like:

<?php
 
array( 'value', 'value', 'value2' );
?>

This is when it gets transfered to drupal_to_js. Both setting calls store the settings separately and they are recursively combined before being sent to drupal_to_js. The problem is when they are recursively combined it isn't checking for uniqueness. And, the array_unique php function looks as unique values and not key value pairs.

I like your idea for a drupal_add_js_setting function. Currently, the value can be arrays with unlimited nesting that can be added to with multiple drupal_add_js calls. I would like to see this ability remain.

#13

lilou - October 26, 2008 - 14:45
Status:needs review» needs work

#14

p.brouwers - September 11, 2009 - 14:08

Had the same issue. Calling drupal_add_js twice for a setting changes it's datatype from your setting to an array containing both settings. (Thus not overridding the previous setting)

Read http://nl.php.net/manual/en/function.array-merge-recursive.php#92195 about the problem. His function 'array_merge_recursive_distinct' could solve this problem.

#15

donquixote - October 5, 2009 - 21:43

I did not follow the complete discussion (coming from uc_option_image), but here is an example for how PHP's array_merge_recursive sucks badly with numeric keys.

<?php
<?php
$x
= array('str' => array(4 => 'x'));
$y = array('str' => array(8 => 'y'));

print_r(array_merge_recursive($x, $y));
?>

The result:
Array
(
    [str] => Array
        (
            [4] => x
            [5] => y
        )

)

As you can see, the 8 key becomes a 5, while the 4 key is not changed. Feels quite inconsistent.

Solution for us: Either use something different from array_merge_recursive, or use string keys instead of numeric ones.

#16

p.brouwers - October 13, 2009 - 14:24

Yes, array_merge_recursive() is buggy.

When using array_merge_recursive_distinct() the result is correct:

Array
(
    [str] => Array
        (
            [4] => x
            [8] => y
        )

)

added a patch file for this.

AttachmentSizeStatusTest resultOperations
common.inc_.patch2.42 KBIdleFailed: Failed to apply patch.View details | Re-test

#17

Rob Loach - October 26, 2009 - 20:27

This has got me a couple times.

#18

p.brouwers - November 12, 2009 - 12:22
Status:needs work» needs review

ok, this patch needs a review.

AttachmentSizeStatusTest resultOperations
common.inc_.patch2.42 KBIdleFailed: Failed to apply patch.View details | Re-test

#19

System Message - November 12, 2009 - 12:35
Status:needs review» needs work

The last submitted patch failed testing.

#21

p.brouwers - November 12, 2009 - 16:06
Status:needs work» needs review

whoops, wrong patch

AttachmentSizeStatusTest resultOperations
common.inc_.patch3.46 KBIdleFailed: Failed to install HEAD.View details | Re-test

#22

System Message - November 12, 2009 - 16:15
Status:needs review» needs work

The last submitted patch failed testing.

 
 

Drupal is a registered trademark of Dries Buytaert.