I am the maintainer of the Masonry module and am creating a new version of it that puts the Javascript in a separate .js file (as opposed to using inline js). I am then sending the Masonry options to the JS file as settings using drupal_add_js().
The problem I'm having is that some of the options can be functions or objects, but as users are entering them in a form, they're being sent as a string to the JS file and aren't working...
I've done some research and discovered that I could possibly use eval() (or one of its aliases), but apparently eval is evil.
My question is therefore: how do I make it possible for users to enter a custom function or object into a form and then pass that form value (string) to Javascript?
Here is an example of the code I have currently (just showing the relevant parts).
Form:
$form['masonry_width_custom'] = array(
'#type' => 'textarea',
'#title' => t('Column width function'),
'#description' => t("A javascript function that returns a numerical value to use as the column width (in pixels). See <a href='http://masonry.desandro.com/docs/options.html#columnwidth'>http://masonry.desandro.com/docs/options.html#columnwidth</a> for more information."),
'#default_value' => $default_values['masonry_width_custom'],
'#rows' => 3,
);
$form['masonry_animated_custom'] = array(
'#type' => 'textfield',
'#title' => t('Custom animation options'),
'#description' => t("A javascript object of options to use for the animation. See <a href='http://masonry.desandro.com/docs/options.html#animationoptions'>http://masonry.desandro.com/docs/options.html#animationoptions</a> for more information."),
'#default_value' => $default_values['masonry_animated_custom'],
'#maxlength' => 255,
);
JS Settings:
$masonry = array(
'masonry' => array(
$container => array(
'columnWidth' => $settings['masonry_width_custom'],
'animationOptions' => $settings['masonry_animated_custom'],
),
),
);
drupal_add_js($masonry, 'setting');
drupal_add_js(drupal_get_path('module', 'masonry') . '/masonry.js');
JS File:
$.each(Drupal.settings.masonry, function (container, settings) {
var $options = new Object();
if (settings.columnWidth) {
$options.columnWidth = settings.columnWidth;
}
if (settings.animationOptions) {
$options.animationOptions = settings.animationOptions;
}
$(container).masonry($options);
});
Comments
Are you talking about module
Are you talking about module developers passing in functions? Opening up your JavaScript to functions entered in forms is dangerous, as it opens up big security holes. There is a reason eval() is evil, and that's because it's basically opening up a door in your system and leaving it open.
That said, if you are trying to create a method to allow a select group of users to add their own javascript, like the site developer, you can do the following:
1) Create your settings page so that your code is saved to a drupal variable using variable_set(). For example
variable_set('some_setting', 'alert(Drupal.t("Inline js executed"));');2) On the page where the code is to be output, you can use one of the two following methods (#1 preferred):
Method 1 - using the #attached element of any render array:
This will fire off the code set on your settings page in step 1. All render arrays in D7 support the #attached element, allowing you to ensure that your JS is attached specifically to the element it belongs to, allowing it to be altered by any _alter functions along the way.
Method 2 - using drupal_add_js()
This will add the inline js, however it does not allow for this to be altered along the way on the object the JS belongs to, it can only be overridden in hook_js_alter(), which makes for more work.
Contact me to contract me for D7 -> D10/11 migrations.