Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.424 diff -u -r1.424 theme.inc --- includes/theme.inc 26 May 2008 17:12:54 -0000 1.424 +++ includes/theme.inc 4 Jun 2008 05:09:43 -0000 @@ -158,7 +158,7 @@ // Add scripts used by this theme. foreach ($final_scripts as $script) { - drupal_add_js($script, 'theme'); + drupal_add_js($script, array('weight' => 20)); } $theme_engine = NULL; @@ -1284,7 +1284,7 @@ // Add sticky headers, if applicable. if (count($header)) { - drupal_add_js('misc/tableheader.js'); + drupal_add_js('misc/tableheader.js', array('weight' => -20)); // Add 'sticky-enabled' class to the table to identify it for JS. // This is needed to target tables constructed by this function. $attributes['class'] = empty($attributes['class']) ? 'sticky-enabled' : ($attributes['class'] . ' sticky-enabled'); @@ -1401,7 +1401,7 @@ * Returns a header cell for tables that have a select all functionality. */ function theme_table_select_header_cell() { - drupal_add_js('misc/tableselect.js'); + drupal_add_js('misc/tableselect.js', array('weight' => -20)); return array('class' => 'select-all'); } Index: includes/batch.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/batch.inc,v retrieving revision 1.15 diff -u -r1.15 batch.inc --- includes/batch.inc 14 Apr 2008 17:48:33 -0000 1.15 +++ includes/batch.inc 4 Jun 2008 05:09:41 -0000 @@ -73,7 +73,7 @@ // and the initialization and error messages. $current_set = _batch_current_set(); drupal_set_title($current_set['title']); - drupal_add_js('misc/progress.js', 'core', 'header', FALSE, FALSE); + drupal_add_js('misc/progress.js', array('cache' => FALSE, 'weight' => -20)); $url = url($batch['url'], array('query' => array('id' => $batch['id']))); $js_setting = array( @@ -84,7 +84,7 @@ ), ); drupal_add_js($js_setting, 'setting'); - drupal_add_js('misc/batch.js', 'core', 'header', FALSE, FALSE); + drupal_add_js('misc/batch.js', array('cache' => FALSE, 'weight' => -20)); $output = '
'; return $output; Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.272 diff -u -r1.272 form.inc --- includes/form.inc 6 May 2008 12:18:45 -0000 1.272 +++ includes/form.inc 4 Jun 2008 05:09:42 -0000 @@ -1488,7 +1488,7 @@ */ function theme_fieldset($element) { if ($element['#collapsible']) { - drupal_add_js('misc/collapse.js'); + drupal_add_js('misc/collapse.js', array('weight' => -20)); if (!isset($element['#attributes']['class'])) { $element['#attributes']['class'] = ''; @@ -1786,8 +1786,8 @@ // Adding the same javascript settings twice will cause a recursion error, // we avoid the problem by checking if the javascript has already been added. if (isset($element['#ahah']['path']) && isset($element['#ahah']['event']) && !isset($js_added[$element['#id']])) { - drupal_add_js('misc/jquery.form.js'); - drupal_add_js('misc/ahah.js'); + drupal_add_js('misc/jquery.form.js', array('weight' => -20)); + drupal_add_js('misc/ahah.js', array('weight' => -20)); $ahah_binding = array( 'url' => url($element['#ahah']['path']), @@ -1812,7 +1812,7 @@ // Add progress.js if we're doing a bar display. if ($ahah_binding['progress']['type'] == 'bar') { - drupal_add_js('misc/progress.js'); + drupal_add_js('misc/progress.js', array('weight' => -20)); } drupal_add_js(array('ahah' => array($element['#id'] => $ahah_binding)), 'setting'); @@ -2000,7 +2000,7 @@ $output = ''; if ($element['#autocomplete_path']) { - drupal_add_js('misc/autocomplete.js'); + drupal_add_js('misc/autocomplete.js', array('weight' => -20)); $class[] = 'form-autocomplete'; $extra = ''; } @@ -2052,16 +2052,14 @@ // Add teaser behavior (must come before resizable) if (!empty($element['#teaser'])) { - drupal_add_js('misc/teaser.js'); - // Note: arrays are merged in drupal_get_js(). - drupal_add_js(array('teaserCheckbox' => array($element['#id'] => $element['#teaser_checkbox'])), 'setting'); - drupal_add_js(array('teaser' => array($element['#id'] => $element['#teaser'])), 'setting'); + drupal_add_js('misc/teaser.js', array('weight' => -20)); + drupal_add_js(array('teaserCheckbox' => array($element['#id'] => $element['#teaser_checkbox']), 'teaser' => array($element['#id'] => $element['#teaser'])), 'setting'); $class[] = 'teaser'; } // Add resizable behavior if ($element['#resizable'] !== FALSE) { - drupal_add_js('misc/textarea.js'); + drupal_add_js('misc/textarea.js', array('weight' => -20)); $class[] = 'resizable'; } Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.770 diff -u -r1.770 common.inc --- includes/common.inc 30 May 2008 17:41:51 -0000 1.770 +++ includes/common.inc 4 Jun 2008 05:09:41 -0000 @@ -1894,7 +1894,7 @@ * reference to an existing file or as inline code. The following actions can be * performed using this function: * - * - Add a file ('core', 'module' and 'theme'): + * - Add a file ('file'): * Adds a reference to a JavaScript file to the page. JavaScript files * are placed in a certain order, from 'core' first, to 'module' and finally * 'theme' so that files, that are added later, can override previously added @@ -1911,92 +1911,151 @@ * will be accessible at Drupal.settings. * * @param $data - * (optional) If given, the value depends on the $type parameter: - * - 'core', 'module' or 'theme': Path to the file relative to base_path(). + * (optional) If given, the value depends on the $options parameter: + * - 'file': Path to the file relative to base_path(). * - 'inline': The JavaScript code that should be placed in the given scope. * - 'setting': An array with configuration options as associative array. The * array is directly placed in Drupal.settings. You might want to wrap your * actual configuration settings in another variable to prevent the pollution * of the Drupal.settings namespace. - * @param $type - * (optional) The type of JavaScript that should be added to the page. Allowed - * values are 'core', 'module', 'theme', 'inline' and 'setting'. You - * can, however, specify any value. It is treated as a reference to a JavaScript - * file. Defaults to 'module'. - * @param $scope - * (optional) The location in which you want to place the script. Possible - * values are 'header' and 'footer' by default. If your theme implements - * different locations, however, you can also use these. - * @param $defer - * (optional) If set to TRUE, the defer attribute is set on the \n"; + $renderable_js[] = array( + 'type' => 'setting', + 'data' => $data, + ); break; case 'inline': foreach ($data as $info) { - $output .= '\n"; + $renderable_js[] = array( + 'type' => 'inline', + 'data' => $info['data'], + 'attributes' => $info['attributes'], + ); } break; default: - // If JS preprocessing is off, we still need to output the scripts. - // Additionally, go through any remaining scripts if JS preprocessing is on and output the non-cached ones. foreach ($data as $path => $info) { - if (!$info['preprocess'] || !$is_writable || !$preprocess_js) { - $no_preprocess[$type] .= '\n"; - } - else { - $files[$path] = $info; - } + $info['attributes']['src'] = base_path() . $path . ($info['cache'] ? $query_string : '?' . time()); + $renderable_js[] = array( + 'type' => 'file', + 'attributes' => $info['attributes'], + 'path' => $path, + ); } } } + return theme('scripts', $renderable_js, $scope); +} - // Aggregate any remaining JS files that haven't already been output. - if ($is_writable && $preprocess_js && count($files) > 0) { - $filename = md5(serialize($files) . $query_string) . '.js'; - $preprocess_file = drupal_build_js_cache($files, $filename); - $preprocessed .= '' . "\n"; - } +/** + * Themes a list of scripts, which are then rendered to the page. + * + * @param $scripts + * An array of scripts to be output. + * @param $scope + * The scope of the JavaScript: where it's going to be located + * on the page. + * @return + * HTML containing the script tags. + */ +function theme_scripts($scripts, $scope) { + $output = ''; - // Keep the order of JS files consistent as some are preprocessed and others are not. - // Make sure any inline or JS setting variables appear last after libraries have loaded. - $output = $preprocessed . implode('', $no_preprocess) . $output; + foreach ($scripts as $script) { + switch ($script['type']) { + case 'setting': + $output .= '\n"; + break; + case 'inline': + $output .= '' . $script['data'] . "\n"; + break; + case 'file': + $output .= '\n"; + } + } return $output; } @@ -2196,7 +2319,7 @@ function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgroup = NULL, $source = NULL, $hidden = TRUE, $limit = 0) { static $js_added = FALSE; if (!$js_added) { - drupal_add_js('misc/tabledrag.js', 'core'); + drupal_add_js('misc/tabledrag.js', array('weight' => -20)); $js_added = TRUE; } @@ -2256,9 +2379,14 @@ } /** - * Converts a PHP variable into its Javascript equivalent. - * - * We use HTML-safe strings, i.e. with <, > and & escaped. + * Converts a PHP variable into its Javascript equivalent. We use + * HTML-safe strings, i.e. with <, > and & escaped. + * + * @param $var + * The variable to encode + * @return + * A string, which is a JavaScript-encoded version of $var. + * @see drupal_json */ function drupal_to_js($var) { // json_encode() does not escape <, > and &, so we do it with str_replace() @@ -2273,6 +2401,7 @@ * * @param $var * (optional) If set, the variable will be converted to JSON and output. + * @see drupal_to_js */ function drupal_json($var = NULL) { // We are returning JavaScript, so tell the browser. @@ -3015,6 +3144,10 @@ 'form_element' => array( 'arguments' => array('element' => NULL, 'value' => NULL), ), + // from form.inc + 'scripts' => array( + 'arguments' => array('scripts' => NULL, 'scope' => NULL), + ), ); } Index: modules/user/user.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v retrieving revision 1.22 diff -u -r1.22 user.admin.inc --- modules/user/user.admin.inc 7 May 2008 19:34:24 -0000 1.22 +++ modules/user/user.admin.inc 4 Jun 2008 05:09:46 -0000 @@ -83,7 +83,7 @@ ); } - drupal_add_js('misc/form.js', 'core'); + drupal_add_js('misc/form.js', array('weight' => -20)); return $form; } Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.909 diff -u -r1.909 user.module --- modules/user/user.module 26 May 2008 17:12:55 -0000 1.909 +++ modules/user/user.module 4 Jun 2008 05:09:46 -0000 @@ -2111,7 +2111,7 @@ global $user; // Only need to do once per page. if (!$complete) { - drupal_add_js(drupal_get_path('module', 'user') . '/user.js', 'module'); + drupal_add_js(drupal_get_path('module', 'user') . '/user.js'); drupal_add_js(array( 'password' => array( Index: modules/system/system.test =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.test,v retrieving revision 1.5 diff -u -r1.5 system.test --- modules/system/system.test 2 Jun 2008 17:32:19 -0000 1.5 +++ modules/system/system.test 4 Jun 2008 05:09:46 -0000 @@ -295,3 +295,162 @@ } } } + +class CommonIncTest extends DrupalWebTestCase { + /** + * Implementation of getInfo(). + */ + function getInfo() { + return array( + 'name' => t('Common.inc tests'), + 'description' => t('Tests for common.inc'), + 'group' => t('Core'), + ); + } + + function test_drupal_add_js() { + $original_js = drupal_add_js(); + // Reset the JavaScript. + drupal_add_js(NULL, array('reset' => TRUE)); + + // Default JavaScript array. + $default_js = array( + 'files' => array( + 'misc/jquery.js' => array( + 'weight' => -1000, + 'scope' => 'header', + 'cache' => TRUE, + 'preprocess' => TRUE, + 'attributes' => array('type' => 'text/javascript'), + 'data' => 'misc/jquery.js', + ), + 'misc/drupal.js' => array( + 'weight' => -999, + 'scope' => 'header', + 'cache' => TRUE, + 'preprocess' => TRUE, + 'attributes' => array('type' => 'text/javascript'), + 'data' => 'misc/drupal.js', + ), + ), + 'setting' => array( + 'header' => array( + 'basePath' => base_path(), + ), + ), + 'inline' => array(), + ); + + // Second time - make sure it retains the defaults. + $this->assertIdentical(drupal_add_js(), $default_js, t('Test drupal_add_js without parameters.')); + + // Try adding a duplicate file. + drupal_add_js('misc/jquery.js'); + $this->assertIdentical(drupal_add_js(), $default_js, t('The duplicate file was only added once.')); + + // Try the reset parameter. + drupal_add_js('misc/'. $this->randomName(8) .'.js'); + drupal_add_js(NULL, array('reset' => TRUE)); + $this->assertIdentical(drupal_add_js(), $default_js, t('reset actually reset the javascript.')); + + // Test the weight system with files. + $weights = array(1, 10, -10); + $files = array(); + $sort = array(); + foreach ($weights as $weight) { + $js_file = 'misc/' . $this->randomName(8) . '.js'; + $files[$js_file] = array( + 'weight' => $weight, + 'scope' => 'header', + 'cache' => TRUE, + 'preprocess' => TRUE, + 'delete' => FALSE, + 'data' => $js_file, + 'attributes' => array('type' => 'text/javascript'), + ); + $sort[$js_file] = $weight; + } + + $weight = 1; + foreach ($files as $file) { + drupal_add_js($file['data'], array('weight' => $file['weight'])); + } + array_multisort($sort, SORT_NUMERIC, $files); + + // Make a copy of the default JS. + $new_js = $default_js; + $new_js['files'] += $files; + $this->assertIdentical(drupal_add_js(), $new_js, t('The weights system works with files.')); + + // Test deleting with $options['delete'] = TRUE. + $file = array_shift($files); + $new_js = $default_js; + $new_js['files'] += $files; + drupal_add_js($file['data'], array('delete' => TRUE)); + $this->assertIdentical(drupal_add_js(), $new_js, t('The JavaScript was deleted from the array with delete => TRUE.')); + + // Test deleting with $type = 'delete'. + $file = array_shift($files); + $new_js = $default_js; + $new_js['files'] += $files; + drupal_add_js($file['data'], 'delete'); + $this->assertIdentical(drupal_add_js(), $new_js, t('The JavaScript was deleted from the array with $type = \'delete\'.')); + + // Test resetting. Although I can't triger the if (empty($javascript)), I can trigger the $options['reset']. + drupal_add_js(NULL, array('reset' => TRUE)); + $this->assertIdentical(drupal_add_js(), $default_js, t('The JavaScript was reset.')); + + drupal_add_js($new_js, array('reset' => TRUE)); + $this->assertIdentical(drupal_add_js(), $new_js, t('The JavaScript was reset to an array.')); + + // Test deleting without adding first. + drupal_add_js($this->randomName(), array('delete' => TRUE)); + $this->assertIdentical(drupal_add_js(), $new_js, t('A file that has not been added, but deleted, doesn\'t show up.')); + + // Test settings. + $settings = array(); + $settings[$this->randomName()] = $this->randomName(); + $settings[$this->randomName()] = array($this->randomName(), $this->randomName()); + $settings[$this->randomName()] = array('test' => $this->randomName(), 'test2' => $this->randomName()); + $new_js['setting']['header'] = $settings + $new_js['setting']['header']; + drupal_add_js($settings, 'setting'); + $this->assertIdentical(drupal_add_js(), $new_js, t('The settings were added.')); + + drupal_add_js($settings, 'setting'); + $this->assertIdentical(drupal_add_js(), $new_js, t('Duplicate settings had no effect.')); + + // Test adding settings with type => 'setting'. + $settings = array(); + $settings[$this->randomName()] = $this->randomName(); + $settings[$this->randomName()] = array($this->randomName(), $this->randomName()); + $settings[$this->randomName()] = array('test' => $this->randomName(), 'test2' => $this->randomName()); + $new_js['setting']['header'] = $settings + $new_js['setting']['header']; + drupal_add_js($settings, array('type' => 'setting')); + $this->assertIdentical(drupal_add_js(), $new_js, t('Added settings with type => \'setting\'.')); + + // Test inline. + drupal_add_js("$(function() { $('test').hide(); });", 'inline'); + $new_js['inline']["$(function() { $('test').hide(); });"] = array( + 'weight' => 0, + 'scope' => 'header', + 'cache' => TRUE, + 'preprocess' => TRUE, + 'delete' => FALSE, + 'data' => "$(function() { $('test').hide(); });", + 'attributes' => array('type' => 'text/javascript'), + ); + print_r($new_js); + print_r(drupal_add_js()); + $this->assertIdentical(drupal_add_js(), $new_js, t('Added inline JavaScript.')); + + // Test caching. + $file = $this->randomName(); + drupal_add_js($file, array('cache' => FALSE)); + $new_js = drupal_add_js(); + $this->assertFalse($new_js['files'][$file]['cache'], t('The setting to turn caching off went through.')); + $this->assertFalse($new_js['files'][$file]['preprocess'], t('The setting to turn caching off, in turn turning preprocess off, went through.')); + + // Reset the JavaScript. + drupal_add_js($original_js, array('reset' => TRUE)); + } +} Index: modules/system/system.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v retrieving revision 1.76 diff -u -r1.76 system.admin.inc --- modules/system/system.admin.inc 10 May 2008 07:32:02 -0000 1.76 +++ modules/system/system.admin.inc 4 Jun 2008 05:09:46 -0000 @@ -1522,7 +1522,7 @@ * @see system_date_time_settings_submit() */ function system_date_time_settings() { - drupal_add_js(drupal_get_path('module', 'system') . '/system.js', 'module'); + drupal_add_js(drupal_get_path('module', 'system') . '/system.js'); drupal_add_js(array('dateTime' => array('lookup' => url('admin/settings/date-time/lookup'))), 'setting'); // Date settings: @@ -1730,7 +1730,7 @@ if (!variable_get('clean_url', 0)) { if (strpos(request_uri(), '?q=') !== FALSE) { - drupal_add_js(drupal_get_path('module', 'system') . '/system.js', 'module'); + drupal_add_js(drupal_get_path('module', 'system') . '/system.js'); $form['clean_url']['#description'] .= ' ' . t('Before enabling clean URLs, you must perform a test to determine if your server is properly configured. If you are able to see this page again after clicking the "Run the clean URL test" link, the test has succeeded and the radio buttons above will be available. If instead you are directed to a "Page not found" error, you will need to change the configuration of your server. The handbook page on Clean URLs has additional troubleshooting information.', array('@handbook' => 'http://drupal.org/node/15365')) . ''; Index: modules/simpletest/simpletest.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.module,v retrieving revision 1.3 diff -u -r1.3 simpletest.module --- modules/simpletest/simpletest.module 10 May 2008 07:46:22 -0000 1.3 +++ modules/simpletest/simpletest.module 4 Jun 2008 05:09:45 -0000 @@ -81,7 +81,7 @@ function simpletest_entrypoint() { simpletest_load(); drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css', 'module'); - drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js', 'module'); + drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js'); $output = drupal_get_form('simpletest_overview_form'); if (simpletest_running_output()) { Index: modules/locale/locale.module =================================================================== RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v retrieving revision 1.217 diff -u -r1.217 locale.module --- modules/locale/locale.module 23 Apr 2008 20:01:52 -0000 1.217 +++ modules/locale/locale.module 4 Jun 2008 05:09:44 -0000 @@ -507,24 +507,18 @@ // The first three parameters are NULL in order to get an array with all // scopes. This is necessary to prevent recreation of JS translation files // when new files are added for example in the footer. - $javascript = drupal_add_js(NULL, NULL, NULL); + $javascript = drupal_add_js(); $files = $new_files = FALSE; - foreach ($javascript as $scope) { - foreach ($scope as $type => $data) { - if ($type != 'setting' && $type != 'inline') { - foreach ($data as $filepath => $info) { - $files = TRUE; - if (!in_array($filepath, $parsed)) { - // Don't parse our own translations files. - if (substr($filepath, 0, strlen($dir)) != $dir) { - locale_inc_callback('_locale_parse_js_file', $filepath); - watchdog('locale', 'Parsed JavaScript file %file.', array('%file' => $filepath)); - $parsed[] = $filepath; - $new_files = TRUE; - } - } - } + foreach ($javascript['files'] as $filepath => $info) { + $files = TRUE; + if (!in_array($filepath, $parsed)) { + // Don't parse our own translations files. + if (substr($filepath, 0, strlen($dir)) != $dir) { + locale_inc_callback('_locale_parse_js_file', $filepath); + watchdog('locale', 'Parsed JavaScript file %file.', array('%file' => $filepath)); + $parsed[] = $filepath; + $new_files = TRUE; } } } @@ -554,7 +548,7 @@ // Add the translation JavaScript file to the page. if ($files && !empty($language->javascript)) { - drupal_add_js($dir . '/' . $language->language . '_' . $language->javascript . '.js', 'core'); + drupal_add_js($dir . '/' . $language->language . '_' . $language->javascript . '.js'); } } Index: modules/color/color.module =================================================================== RCS file: /cvs/drupal/drupal/modules/color/color.module,v retrieving revision 1.41 diff -u -r1.41 color.module --- modules/color/color.module 19 May 2008 19:36:41 -0000 1.41 +++ modules/color/color.module 4 Jun 2008 05:09:44 -0000 @@ -154,7 +154,7 @@ // Add Farbtastic color picker. drupal_add_css('misc/farbtastic/farbtastic.css', 'module', 'all', FALSE); - drupal_add_js('misc/farbtastic/farbtastic.js'); + drupal_add_js('misc/farbtastic/farbtastic.js', array('weight' => -20)); // Add custom CSS and JS. drupal_add_css($base . '/color.css', 'module', 'all', FALSE); Index: modules/block/block-admin-display-form.tpl.php =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block-admin-display-form.tpl.php,v retrieving revision 1.6 diff -u -r1.6 block-admin-display-form.tpl.php --- modules/block/block-admin-display-form.tpl.php 15 May 2008 21:30:02 -0000 1.6 +++ modules/block/block-admin-display-form.tpl.php 4 Jun 2008 05:09:43 -0000 @@ -26,7 +26,7 @@ ?> -20)); drupal_add_js(drupal_get_path('module', 'block') . '/block.js'); foreach ($block_regions as $region => $title) { drupal_add_tabledrag('blocks', 'match', 'sibling', 'block-region-select', 'block-region-' . $region, NULL, FALSE); Index: modules/node/node.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.admin.inc,v retrieving revision 1.22 diff -u -r1.22 node.admin.inc --- modules/node/node.admin.inc 14 Apr 2008 17:48:38 -0000 1.22 +++ modules/node/node.admin.inc 4 Jun 2008 05:09:45 -0000 @@ -259,7 +259,7 @@ $form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset')); } - drupal_add_js('misc/form.js', 'core'); + drupal_add_js('misc/form.js', array('weight' => -20)); return $form; } Index: install.php =================================================================== RCS file: /cvs/drupal/drupal/install.php,v retrieving revision 1.119 diff -u -r1.119 install.php --- install.php 26 May 2008 17:12:54 -0000 1.119 +++ install.php 4 Jun 2008 05:09:41 -0000 @@ -711,7 +711,7 @@ // Add JavaScript validation. _user_password_dynamic_validation(); - drupal_add_js(drupal_get_path('module', 'system') . '/system.js', 'module'); + drupal_add_js(drupal_get_path('module', 'system') . '/system.js'); // We add these strings as settings because JavaScript translation does not // work on install time. drupal_add_js(array('copyFieldValue' => array('edit-site-mail' => array('edit-account-mail')), 'cleanURL' => array('success' => st('Your server has been successfully tested to support this feature.'), 'failure' => st('Your system configuration does not currently support this feature. The handbook page on Clean URLs has additional troubleshooting information.'), 'testing' => st('Testing clean URLs...'))), 'setting');