### Eclipse Workspace Patch 1.0 #P drupal-cvs Index: misc/progress.js =================================================================== RCS file: /cvs/drupal/drupal/misc/progress.js,v retrieving revision 1.15 diff -u -r1.15 progress.js --- misc/progress.js 4 May 2007 09:41:36 -0000 1.15 +++ misc/progress.js 24 May 2007 21:16:25 -0000 @@ -23,7 +23,7 @@ $(this.element).html('
'+ '
'+ '
 
'); -} +}; /** * Set the percentage and status message for the progressbar. @@ -37,7 +37,7 @@ if (this.updateCallback) { this.updateCallback(percentage, message, this); } -} +}; /** * Start monitoring progress via Ajax. @@ -46,7 +46,7 @@ this.delay = delay; this.uri = uri; this.sendPing(); -} +}; /** * Stop monitoring progress via Ajax. @@ -55,7 +55,7 @@ clearTimeout(this.timer); // This allows monitoring to be stopped from within the callback this.uri = null; -} +}; /** * Request progress data from server. @@ -90,7 +90,7 @@ } }); } -} +}; /** * Display errors on the page. @@ -105,4 +105,4 @@ if (this.errorCallback) { this.errorCallback(this); } -} +}; Index: misc/autocomplete.js =================================================================== RCS file: /cvs/drupal/drupal/misc/autocomplete.js,v retrieving revision 1.17 diff -u -r1.17 autocomplete.js --- misc/autocomplete.js 9 Jan 2007 07:31:04 -0000 1.17 +++ misc/autocomplete.js 24 May 2007 21:16:25 -0000 @@ -15,7 +15,7 @@ $(input.form).submit(Drupal.autocompleteSubmit); new Drupal.jsAC(input, acdb[uri]); }); -} +}; /** * Prevents the form from submitting if the suggestions popup is open @@ -25,7 +25,7 @@ return $('#autocomplete').each(function () { this.owner.hidePopup(); }).size() == 0; -} +}; /** * An AutoComplete object @@ -59,7 +59,7 @@ default: // all other keys return true; } -} +}; /** * Handler for the "keyup" event @@ -96,14 +96,14 @@ this.hidePopup(e.keyCode); return true; } -} +}; /** * Puts the currently highlighted suggestion into the autocomplete field */ Drupal.jsAC.prototype.select = function (node) { this.input.value = node.autocompleteValue; -} +}; /** * Highlights the next suggestion @@ -118,7 +118,7 @@ this.highlight(lis.get(0)); } } -} +}; /** * Highlights the previous suggestion @@ -127,7 +127,7 @@ if (this.selected && this.selected.previousSibling) { this.highlight(this.selected.previousSibling); } -} +}; /** * Highlights a suggestion @@ -138,7 +138,7 @@ } $(node).addClass('selected'); this.selected = node; -} +}; /** * Unhighlights a suggestion @@ -146,7 +146,7 @@ Drupal.jsAC.prototype.unhighlight = function (node) { $(node).removeClass('selected'); this.selected = false; -} +}; /** * Hides the autocomplete suggestions @@ -163,7 +163,7 @@ $(popup).fadeOut('fast', function() { $(popup).remove(); }); } this.selected = false; -} +}; /** * Positions the suggestions popup and starts a search @@ -187,7 +187,7 @@ // Do search this.db.owner = this; this.db.search(this.input.value); -} +}; /** * Fills the suggestion popup with any matches received @@ -222,7 +222,7 @@ this.hidePopup(); } } -} +}; Drupal.jsAC.prototype.setStatus = function (status) { switch (status) { @@ -235,7 +235,7 @@ $(this.input).removeClass('throbbing'); break; } -} +}; /** * An AutoComplete DataBase object @@ -244,7 +244,7 @@ this.uri = uri; this.delay = 300; this.cache = {}; -} +}; /** * Performs a cached and delayed search @@ -286,7 +286,7 @@ } }); }, this.delay); -} +}; /** * Cancels the current autocomplete request @@ -295,7 +295,7 @@ if (this.owner) this.owner.setStatus('cancel'); if (this.timer) clearTimeout(this.timer); this.searchString = ''; -} +}; // Global Killswitch if (Drupal.jsEnabled) { Index: misc/tableheader.js =================================================================== RCS file: /cvs/drupal/drupal/misc/tableheader.js,v retrieving revision 1.2 diff -u -r1.2 tableheader.js --- misc/tableheader.js 7 Apr 2007 03:03:40 -0000 1.2 +++ misc/tableheader.js 24 May 2007 21:16:25 -0000 @@ -86,7 +86,7 @@ // Precalculate table heights $('table.sticky-table').each(function () { this.height = $(this).height(); - }) + }); $(cells).each(function () { // Get position. Index: misc/textarea.js =================================================================== RCS file: /cvs/drupal/drupal/misc/textarea.js,v retrieving revision 1.14 diff -u -r1.14 textarea.js --- misc/textarea.js 10 Apr 2007 11:24:16 -0000 1.14 +++ misc/textarea.js 24 May 2007 21:16:25 -0000 @@ -36,7 +36,7 @@ textarea.css('opacity', 1); } }); -} +}; if (Drupal.jsEnabled) { $(document).ready(Drupal.textareaAttach); Index: misc/drupal.js =================================================================== RCS file: /cvs/drupal/drupal/misc/drupal.js,v retrieving revision 1.31 diff -u -r1.31 drupal.js --- misc/drupal.js 4 May 2007 09:41:36 -0000 1.31 +++ misc/drupal.js 24 May 2007 21:16:25 -0000 @@ -50,7 +50,7 @@ // Restore form submission button.form.action = action; button.form.target = target; - + // Get response from iframe body try { response = (iframe.contentWindow || iframe.contentDocument || iframe).document.body.innerHTML; @@ -64,7 +64,7 @@ catch (e) { response = null; } - + response = Drupal.parseJson(response); // Check response code if (response.status == 0) { @@ -74,14 +74,14 @@ handler.oncomplete(response.data); return true; - } + }; return true; - } - } + }; + }; button.onmouseout = button.onblur = function() { button.onclick = null; - } + }; }; /** @@ -218,7 +218,7 @@ return { 'start': start, 'end': end }; } return { 'start': element.selectionStart, 'end': element.selectionEnd }; -} +}; // Global Killswitch on the element if (Drupal.jsEnabled) { Index: misc/teaser.js =================================================================== RCS file: /cvs/drupal/drupal/misc/teaser.js,v retrieving revision 1.3 diff -u -r1.3 teaser.js --- misc/teaser.js 9 Apr 2007 13:58:02 -0000 1.3 +++ misc/teaser.js 24 May 2007 21:16:25 -0000 @@ -73,7 +73,7 @@ } }); -} +}; if (Drupal.jsEnabled) { $(document).ready(Drupal.teaserAttach); Index: misc/batch.js =================================================================== RCS file: /cvs/drupal/drupal/misc/batch.js,v retrieving revision 1.1 diff -u -r1.1 batch.js --- misc/batch.js 4 May 2007 09:41:36 -0000 1.1 +++ misc/batch.js 24 May 2007 21:16:25 -0000 @@ -12,7 +12,7 @@ pb.stopMonitoring(); window.location = uri+'&op=finished'; } - } + }; var errorCallback = function (pb) { var div = document.createElement('p'); @@ -20,7 +20,7 @@ $(div).html(errorMessage); $(holder).prepend(div); $('#wait').hide(); - } + }; var progress = new Drupal.progressBar('updateprogress', updateCallback, "POST", errorCallback); progress.setProgress(-1, initMessage); Index: misc/collapse.js =================================================================== RCS file: /cvs/drupal/drupal/misc/collapse.js,v retrieving revision 1.11 diff -u -r1.11 collapse.js --- misc/collapse.js 28 Feb 2007 20:29:38 -0000 1.11 +++ misc/collapse.js 24 May 2007 21:16:25 -0000 @@ -14,7 +14,7 @@ Drupal.collapseScrollIntoView(this.parentNode); this.parentNode.animating = false; }); - if (typeof Drupal.textareaAttach != 'undefined') { + if (typeof(Drupal.textareaAttach) != 'undefined') { // Initialize resizable textareas that are now revealed Drupal.textareaAttach(null, fieldset); } @@ -25,7 +25,7 @@ this.parentNode.animating = false; }); } -} +}; /** * Scroll a given fieldset into view as much as possible. @@ -42,7 +42,7 @@ window.scrollTo(0, pos.y + node.offsetHeight - h + fudge); } } -} +}; // Global Killswitch if (Drupal.jsEnabled) { Index: misc/upload.js =================================================================== RCS file: /cvs/drupal/drupal/misc/upload.js,v retrieving revision 1.11 diff -u -r1.11 upload.js --- misc/upload.js 31 Aug 2006 23:31:25 -0000 1.11 +++ misc/upload.js 24 May 2007 21:16:25 -0000 @@ -13,7 +13,7 @@ var hide = base + '-hide'; var upload = new Drupal.jsUpload(uri, button, wrapper, hide); }); -} +}; /** * JS upload object. @@ -25,7 +25,7 @@ this.wrapper = '#'+ wrapper; this.hide = '#'+ hide; Drupal.redirectFormButton(uri, $(this.button).get(0), this); -} +}; /** * Handler for the form redirection submission. @@ -49,7 +49,7 @@ $(hide).after(el); $(el).fadeIn('slow'); $(hide).fadeOut('slow'); -} +}; /** * Handler for the form redirection completion. @@ -92,7 +92,7 @@ Drupal.uploadAutoAttach(); } Drupal.unfreezeHeight(); -} +}; /** * Handler for the form redirection error. @@ -107,7 +107,7 @@ position: 'static', left: '0px' }); -} +}; // Global killswitch Index: misc/tableselect.js =================================================================== RCS file: /cvs/drupal/drupal/misc/tableselect.js,v retrieving revision 1.1 diff -u -r1.1 tableselect.js --- misc/tableselect.js 21 Nov 2006 08:16:39 -0000 1.1 +++ misc/tableselect.js 24 May 2007 21:16:25 -0000 @@ -40,7 +40,7 @@ // Keep track of the last checked checkbox. lastChecked = e.target; }); -} +}; Drupal.tableSelectRange = function(from, to, state) { // We determine the looping mode based on the the order of from and to. @@ -65,7 +65,7 @@ else if (jQuery.filter(to, [i]).r.length) break; } -} +}; // Global Killswitch if (Drupal.jsEnabled) { Index: modules/system/system.js =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.js,v retrieving revision 1.5 diff -u -r1.5 system.js --- modules/system/system.js 14 May 2007 16:22:26 -0000 1.5 +++ modules/system/system.js 24 May 2007 21:16:25 -0000 @@ -23,7 +23,7 @@ $("#clean-url .description span").append('
'+ Drupal.settings.cleanURL.failure +"
"); } }}); -} +}; /** * Internal function to check using Ajax if clean URLs can be enabled on the @@ -50,12 +50,12 @@ $("#clean-url .description span").append('
'+ Drupal.settings.cleanURL.failure +"
"); } }}); -} +}; Drupal.installDefaultTimezone = function() { var offset = new Date().getTimezoneOffset() * -60; $("#edit-date-default-timezone").val(offset); -} +}; /** * Show/hide custom format sections on the date-time settings page. Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.481 diff -u -r1.481 system.module --- modules/system/system.module 23 May 2007 08:00:46 -0000 1.481 +++ modules/system/system.module 24 May 2007 21:16:27 -0000 @@ -258,7 +258,7 @@ ); $items['admin/settings/performance'] = array( 'title' => 'Performance', - 'description' => 'Enable or disable page caching for anonymous users, and enable or disable CSS preprocessor.', + 'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwith optimization options.', 'page callback' => 'drupal_get_form', 'page arguments' => array('system_performance_settings'), ); @@ -700,13 +700,23 @@ $form['bandwidth_optimizations']['preprocess_css'] = array( '#type' => 'radios', '#title' => t('Aggregate and compress CSS files'), - '#default_value' => variable_get('preprocess_css', FALSE) && $is_writable, + '#default_value' => variable_get('preprocess_css', 0) && $is_writable, '#disabled' => !$is_writable, '#options' => array(t('Disabled'), t('Enabled')), '#description' => t("Some Drupal modules include their own CSS files. When these modules are enabled, each module's CSS file adds an additional HTTP request to the page, which can increase the load time of each page. These HTTP requests can also slightly increase server load. It is recommended to only turn this option on when your site is in production, as it can interfere with theme development. This option is disabled if you have not set up your files directory, or if your download method is set to private."), - ); + ); + + $form['bandwidth_optimizations']['preprocess_js'] = array( + '#type' => 'radios', + '#title' => t('Aggregate JS files'), + '#default_value' => variable_get('preprocess_js', 0) && $is_writable, + '#disabled' => !$is_writable, + '#options' => array(t('Disabled'), t('Enabled')), + '#description' => t("Some Drupal modules include their own JS files. When these modules are enabled, each module's JS file adds an additional HTTP request to the page, which can increase the load time of each page. These HTTP requests can also slightly increase server load. It is recommended to only turn this option on when your site is in production, as it can interfere with module development. This option is disabled if you have not set up your files directory, or if your download method is set to private."), + ); $form['#submit'][] = 'drupal_clear_css_cache'; + $form['#submit'][] = 'drupal_clear_js_cache'; return system_settings_form($form); } @@ -1716,6 +1726,7 @@ } drupal_clear_css_cache(); + drupal_clear_js_cache(); $form_state['redirect'] = 'admin/build/modules'; Index: misc/farbtastic/farbtastic.js =================================================================== RCS file: /cvs/drupal/drupal/misc/farbtastic/farbtastic.js,v retrieving revision 1.3 diff -u -r1.3 farbtastic.js --- misc/farbtastic/farbtastic.js 10 Jan 2007 07:03:48 -0000 1.3 +++ misc/farbtastic/farbtastic.js 24 May 2007 21:16:25 -0000 @@ -9,7 +9,7 @@ jQuery.farbtastic = function (container, callback) { var container = $(container).get(0); return container.farbtastic || (container.farbtastic = new jQuery._farbtastic(container, callback)); -} +}; jQuery._farbtastic = function (container, callback) { // Store farbtastic object @@ -62,12 +62,12 @@ } } return this; - } + }; fb.updateValue = function (event) { if (this.value && this.value != fb.color) { fb.setColor(this.value); } - } + }; /** * Change color with HTML syntax #123456 @@ -81,7 +81,7 @@ fb.updateDisplay(); } return this; - } + }; /** * Change color with HSL triplet [0..1, 0..1, 0..1] @@ -92,7 +92,7 @@ fb.color = fb.pack(fb.rgb); fb.updateDisplay(); return this; - } + }; ///////////////////////////////////////////////////// @@ -121,7 +121,7 @@ // Look for the coordinates starting from the wheel widget. var e = reference; - var offset = { x: 0, y: 0 } + var offset = { x: 0, y: 0 }; while (e) { if (typeof e.mouseX != 'undefined') { x = e.mouseX - offset.x; @@ -149,7 +149,7 @@ } // Subtract distance to middle return { x: x - fb.width / 2, y: y - fb.width / 2 }; - } + }; /** * Mousedown handler @@ -168,7 +168,7 @@ // Process fb.mousemove(event); return false; - } + }; /** * Mousemove handler @@ -189,7 +189,7 @@ fb.setHSL([fb.hsl[0], sat, lum]); } return false; - } + }; /** * Mouseup handler @@ -199,7 +199,7 @@ $(document).unbind('mousemove', fb.mousemove); $(document).unbind('mouseup', fb.mouseup); document.dragging = false; - } + }; /** * Update the markers and styles @@ -238,7 +238,7 @@ else if (typeof fb.callback == 'function') { fb.callback.call(fb, fb.color); } - } + }; /** * Get absolute position of element @@ -262,7 +262,7 @@ return '#' + (r < 16 ? '0' : '') + r.toString(16) + (g < 16 ? '0' : '') + g.toString(16) + (b < 16 ? '0' : '') + b.toString(16); - } + }; fb.unpack = function (color) { if (color.length == 7) { @@ -275,7 +275,7 @@ parseInt('0x' + color.substring(2, 3)) / 15, parseInt('0x' + color.substring(3, 4)) / 15]; } - } + }; fb.HSLToRGB = function (hsl) { var m1, m2, r, g, b; @@ -285,7 +285,7 @@ return [this.hueToRGB(m1, m2, h+0.33333), this.hueToRGB(m1, m2, h), this.hueToRGB(m1, m2, h-0.33333)]; - } + }; fb.hueToRGB = function (m1, m2, h) { h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h); @@ -293,7 +293,7 @@ if (h * 2 < 1) return m2; if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6; return m1; - } + }; fb.RGBToHSL = function (rgb) { var min, max, delta, h, s, l; @@ -314,7 +314,7 @@ h /= 6; } return [h, s, l]; - } + }; // Install mousedown handler (the others are set on the document on-demand) $('*', e).mousedown(fb.mousedown); @@ -326,4 +326,4 @@ if (callback) { fb.linkTo(callback); } -} \ No newline at end of file +}; \ No newline at end of file Index: update.php =================================================================== RCS file: /cvs/drupal/drupal/update.php,v retrieving revision 1.222 diff -u -r1.222 update.php --- update.php 16 May 2007 07:56:19 -0000 1.222 +++ update.php 24 May 2007 21:16:23 -0000 @@ -406,6 +406,7 @@ cache_clear_all('*', 'cache_page', TRUE); cache_clear_all('*', 'cache_filter', TRUE); drupal_clear_css_cache(); + drupal_clear_js_cache(); $_SESSION['update_results'] = $results; $_SESSION['update_success'] = $success; Index: includes/batch.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/batch.inc,v retrieving revision 1.3 diff -u -r1.3 batch.inc --- includes/batch.inc 16 May 2007 07:56:19 -0000 1.3 +++ includes/batch.inc 24 May 2007 21:16:23 -0000 @@ -66,7 +66,7 @@ $current_set = _batch_current_set(); drupal_set_title($current_set['title']); - drupal_add_js('misc/progress.js', 'core', 'header'); + drupal_add_js('misc/progress.js', 'core', 'header', FALSE, FALSE); $url = url($batch['url'], array('query' => array('id' => $batch['id']))); $js_setting = array( @@ -77,7 +77,7 @@ ), ); drupal_add_js($js_setting, 'setting'); - drupal_add_js('misc/batch.js', 'core', 'header', FALSE, TRUE); + drupal_add_js('misc/batch.js', 'core', 'header', FALSE, FALSE); $output = '
'; return $output; Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.641 diff -u -r1.641 common.inc --- includes/common.inc 15 May 2007 20:19:47 -0000 1.641 +++ includes/common.inc 24 May 2007 21:16:24 -0000 @@ -1660,24 +1660,26 @@ * (optional) If set to FALSE, the JavaScript file is loaded anew on every page * call, that means, it is not cached. Defaults to TRUE. Used only when $type * references a JavaScript file. + * @param $preprocess + * (optional) Should this JS file be aggregated if this + * feature has been turned on under the performance section? * @return * If the first parameter is NULL, the JavaScript array that has been built so * far for $scope is returned. */ -function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE) { - if (!is_null($data)) { - _drupal_add_js('misc/jquery.js', 'core', 'header', FALSE, $cache); - _drupal_add_js('misc/drupal.js', 'core', 'header', FALSE, $cache); - } - return _drupal_add_js($data, $type, $scope, $defer, $cache); -} - -/** - * Helper function for drupal_add_js(). - */ -function _drupal_add_js($data, $type, $scope, $defer, $cache) { +function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE, $preprocess = TRUE) { static $javascript = array(); + // Add jquery.js and drupal.js the first time a Javascript file is added. + if ($data && empty($javascript)) { + $javascript['header'] = array( + 'core' => array( + 'misc/jquery.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE), + 'misc/drupal.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE), + ), + 'module' => array(), 'theme' => array(), 'setting' => array(), 'inline' => array(), + ); + } if (!isset($javascript[$scope])) { $javascript[$scope] = array('core' => array(), 'module' => array(), 'theme' => array(), 'setting' => array(), 'inline' => array()); } @@ -1686,7 +1688,7 @@ $javascript[$scope][$type] = array(); } - if (!is_null($data)) { + if (isset($data)) { switch ($type) { case 'setting': $javascript[$scope][$type][] = $data; @@ -1695,7 +1697,8 @@ $javascript[$scope][$type][] = array('code' => $data, 'defer' => $defer); break; default: - $javascript[$scope][$type][$data] = array('cache' => $cache, 'defer' => $defer); + // If cache is FALSE, don't preprocess the JS file. + $javascript[$scope][$type][$data] = array('cache' => $cache, 'defer' => $defer, 'preprocess' => (!$cache ? FALSE : $preprocess)); } } @@ -1718,13 +1721,25 @@ * @return * All JavaScript code segments and includes for the scope as HTML tags. */ -function drupal_get_js($scope = 'header', $javascript = NULL) { - $output = ''; - if (is_null($javascript)) { +function drupal_get_js($scope = 'header', $javascript = NULL) { + if (!isset($javascript)) { $javascript = drupal_add_js(NULL, NULL, $scope); } + if (count($javascript) < 1) { + return ''; + } + + $output = ''; + $preprocessed = ''; + $no_preprocess = array('core' => '', 'module' => '', 'theme' => ''); + $files = array(); + $preprocess_js = variable_get('preprocess_js', FALSE); + $directory = file_directory_path(); + $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC); + foreach ($javascript as $type => $data) { + if (!$data) continue; switch ($type) { @@ -1737,16 +1752,301 @@ } 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) { - $output .= '\n"; + if (!$info['preprocess'] || !$is_writable || !$preprocess_js) { + $no_preprocess[$type] .= '\n"; + } + else { + $files[$path] = $info; + } } } + } + + // Aggregate any remaining JS files that haven't already been output. + if ($is_writable && $preprocess_js && count($files) > 0) { + $filename = md5(serialize($files)) .'.js'; + $preprocess_file = drupal_build_js_cache($files, $filename); + $preprocessed .= ''. "\n"; } + // 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; + return $output; } /** + * Aggregate JS files, putting them in the files directory. + * + * @param $files + * An array of JS files to aggregate and compress into one file. + * @param $filename + * The name of the aggregate JS file. + * @return + * The name of the JS file. + */ +function drupal_build_js_cache($files, $filename) { + $contents = ''; + + // Create the js/ within the files folder. + $jspath = file_create_path('js'); + file_check_directory($jspath, FILE_CREATE_DIRECTORY); + + if (!file_exists($jspath .'/'. $filename)) { + // Build aggregate JS file. + foreach ($files as $path => $info) { + if ($info['preprocess']) { + // Append a ';' after each JS file to prevent them from running together. + $contents .= _drupal_compress_js(file_get_contents($path). ';'); + } + } + + // Create the JS file. + file_save_data($contents, $jspath .'/'. $filename, FILE_EXISTS_REPLACE); + } + + return $jspath .'/'. $filename; +} + +/** + * Perform basic code compression for JavaScript. + * + * Helper function for drupal_pack_js(). + */ +function _drupal_compress_js($script) { + $regexps = array( + // Protect strings. + array('/\'[^\'\\n\\r]*\'/', '$0'), + array('/"[^"\\n\\r]*"/', '$0'), + // Remove comments. + array('/\\/\\/[^\\n\\r]*[\\n\\r]/', ''), + array('/\\/\\*[^*]*\\*+((?:[^\\/][^*]*\\*+)*)\\//', ''), + // Protect regular expressions + array('/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$1'), + array('/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', '$0'), + // Remove redundant semi-colons + array('/\\(;;\\)/', '$0'), // Protect for (;;) loops + array('/;+\\s*([};])/', '$1'), + // Protect spaces between keywords and variables + array('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$1 $2'), + array('/([+\\-])\\s+([+\\-])/', '$1 $2'), + // Remove all other white-space + array('/\\s+/', ''), + ); + $script = _packer_apply($script, $regexps, TRUE); + + return $script; +} + +/** + * Multi-regexp replacements. + * + * Allows you to perform multiple regular expression replacements at once, + * without overlapping matches. + * + * @param $script + * The text to modify. + * @param $regexps + * An array of replacement instructions, each being a tuple with values: + * - A stand-alone regular expression without modifiers (slash-delimited) + * - A replacement expression, which may include placeholders. + * @param $escape + * Whether to ignore slash-escaped characters for matching. This allows you + * to match e.g. quote-delimited strings with /'[^']+'/ without having to + * worry about \'. Otherwise, you'd have to mess with look-aheads and + * look-behinds to match these. + */ +function _packer_apply($script, $regexps, $escape = FALSE) { + + $_regexps = array(); + // Process all regexps + foreach ($regexps as $regexp) { + list($expression, $replacement) = $regexp; + + // Count the number of matching groups (including the whole). + $length = 1 + preg_match_all('/(? 'backreferences', + 'data' => array( + 'replacement' => $replacement, + 'length' => $length, + ) + ); + } + } + } + // Store the modified expression. + if (!empty($expression)) { + $_regexps[] = array($expression, $replacement, $length); + } + else { + $_regexps[] = array('/^$/', $replacement, $length); + } + } + + // Execute the global replacement + + // Build one mega-regexp out of the smaller ones. + $regexp = '/'; + foreach ($_regexps as $_regexp) { + list($expression) = $_regexp; + $regexp .= '(' . substr($expression, 1, -1) . ')|'; + } + $regexp = substr($regexp, 0, -1) . '/'; + + // In order to simplify the regexps that look e.g. for quoted strings, we + // remove all escaped characters (such as \' or \") from the data. Then, we + // put them back as they were. + + if ($escape) { + // Remove escaped characters + $script = preg_replace_callback( + '/\\\\(.)' .'/', + '_packer_escape_char', + $script + ); + $escaped = _packer_escape_char(NULL, TRUE); + } + + _packer_replacement(NULL, $_regexps, $escape); + $script = preg_replace_callback( + $regexp, + '_packer_replacement', + $script + ); + + if ($escape) { + // Restore escaped characters + _packer_unescape_char(NULL, $escaped); + $script = preg_replace_callback( + '/\\\\' .'/', + '_packer_unescape_char', + $script + ); + + // We only delete portions of data afterwards to ensure the escaped character + // replacements don't go out of sync. We mark all sections to delete with + // ASCII 01 bytes. + $script = preg_replace('/\\x01[^\\x01]*\\x01/', '', $script); + } + + return $script; +} + +/** + * Helper function for _packer_apply(). + */ +function _packer_escape_char($match, $return = FALSE) { + // Build array of escaped characters that were removed. + static $_escaped = array(); + if ($return) { + $escaped = $_escaped; + $_escaped = array(); + return $escaped; + } + else { + $_escaped[] = $match[1]; + return '\\'; + } +} + +/** + * Helper function for _packer_apply(). + * + * Performs replacements for the multi-regexp. + */ +function _packer_replacement($arguments, $regexps = NULL, $escape = NULL) { + // Cache regexps + static $_regexps, $_escape; + if (isset($regexps)) { + $_regexps = $regexps; + } + if (isset($escape)) { + $_escape = $escape; + } + + if (empty($arguments)) { + return ''; + } + + $i = 1; $j = 0; + // Loop through the regexps + while (isset($_regexps[$j])) { + list($expression, $replacement, $length) = $_regexps[$j++];; + + // Do we have a result? + if (isset($arguments[$i]) && ($arguments[$i] != '')) { + if (is_array($replacement) && isset($replacement['fn'])) { + return call_user_func('_packer_'. $replacement['fn'], $arguments, $i, $replacement['data']); + } + elseif (is_int($replacement)) { + return $arguments[$replacement + $i]; + } + else { + $delete = !$escape || strpos($arguments[$i], '\\') === FALSE + ? '' : "\x01" . $arguments[$i] . "\x01"; + return $delete . $replacement; + } + // skip over references to sub-expressions + } + else { + $i += $length; + } + } +} + +/** + * Helper function for _packer_apply(). + */ +function _packer_unescape_char($match, $escaped = NULL) { + // Store array of escaped characters to insert back. + static $_escaped, $i; + if ($escaped) { + $_escaped = $escaped; + $i = 0; + } + else { + return '\\'. array_shift($_escaped); + } +} + +/** + * Helper function for _packer_replacement(). + */ +function _packer_backreferences($match, $offset, $data) { + $replacement = $data['replacement']; + $i = $data['length']; + while ($i) { + $replacement = str_replace('$'.$i--, $match[$offset + $i], $replacement); + } + return $replacement; +} + +/** + * Delete all cached JS files. + */ +function drupal_clear_js_cache() { + file_scan_directory(file_create_path('js'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE); +} + +/** * Converts a PHP variable into its Javascript equivalent. * * We use HTML-safe strings, i.e. with <, > and & escaped. Index: modules/color/color.js =================================================================== RCS file: /cvs/drupal/drupal/modules/color/color.js,v retrieving revision 1.2 diff -u -r1.2 color.js --- modules/color/color.js 13 Apr 2007 07:33:23 -0000 1.2 +++ modules/color/color.js 24 May 2007 21:16:25 -0000 @@ -141,12 +141,12 @@ // Update locked values if (propagate) { var i = input.i; - for (j = i + 1; ; ++j) { + for (j = i + 1; true; ++j) { if (!locks[j - 1] || $(locks[j - 1]).is('.unlocked')) break; var matched = shift_color(color, reference[input.key], reference[inputs[j].key]); callback(inputs[j], matched, false); } - for (j = i - 1; ; --j) { + for (j = i - 1; true; --j) { if (!locks[j] || $(locks[j]).is('.unlocked')) break; var matched = shift_color(color, reference[input.key], reference[inputs[j].key]); callback(inputs[j], matched, false); @@ -223,7 +223,7 @@ ); $(this).after(lock); locks.push(lock); - } + }; // Add hook var hook = $('
'); @@ -236,7 +236,7 @@ }) .focus(focus); - $('#palette label', form) + $('#palette label', form); // Focus first color focus.call(inputs[0]);