diff -rupN misc/tableheader.js misc/tableheader.js --- misc/tableheader.js 2009-09-22 13:48:29.000000000 +0200 +++ misc/tableheader.js 2009-09-22 13:54:18.000000000 +0200 @@ -18,8 +18,9 @@ Drupal.behaviors.tableHeader = { var headers = []; $('table.sticky-enabled thead', context).once('tableheader', function () { - // Clone thead so it inherits original jQuery properties. - var headerClone = $(this).clone(true).insertBefore(this.parentNode).wrap('').parent().css({ + // Clone thead so it inherits original jQuery properties. Hide the table + // to avoid a flash of the header clone upon page load. + var headerClone = $(this).clone(true).hide().insertBefore(this.parentNode).wrap('').parent().css({ position: 'fixed', top: '0px' }); @@ -32,6 +33,9 @@ Drupal.behaviors.tableHeader = { headerClone.table = table; // Finish initializing header positioning. tracker(headerClone); + // We hid the header to avoid it showing up erroneously on page load; + // we need to unhide it now so that it will show up when expected. + $(headerClone).children('thead').show(); $(table).addClass('sticky-table'); }); diff -rupN modules/locale/locale.test modules/locale/locale.test --- modules/locale/locale.test 2009-09-22 13:48:29.000000000 +0200 +++ modules/locale/locale.test 2009-09-22 13:54:18.000000000 +0200 @@ -236,7 +236,7 @@ class LocaleTranslationFunctionalTest ex $this->clickLink(t('edit')); // We save the lid from the path. $matches = array(); - preg_match('!admin/config/regional/translate/edit/(\d)+!', $this->getUrl(), $matches); + preg_match('!admin/config/regional/translate/edit/(\d+)!', $this->getUrl(), $matches); $lid = $matches[1]; // No t() here, it's surely not translated yet. $this->assertText($name, t('name found on edit screen.')); diff -rupN modules/node/node.admin.inc modules/node/node.admin.inc --- modules/node/node.admin.inc 2009-09-22 13:48:29.000000000 +0200 +++ modules/node/node.admin.inc 2009-09-22 13:54:18.000000000 +0200 @@ -447,6 +447,9 @@ function node_admin_nodes() { foreach ($result as $node) { $nodes[$node->nid] = ''; $options = empty($node->language) ? array() : array('language' => $languages[$node->language]); + // Set a class to flag to the overlay, if present, not to open the link in + // the overlay. + $options['attributes']['class'] = 'overlay-escape'; $form['title'][$node->nid] = array('#markup' => l($node->title, 'node/' . $node->nid, $options) . ' ' . theme('mark', node_mark($node->nid, $node->changed))); $form['name'][$node->nid] = array('#markup' => check_plain(node_type_get_name($node))); $form['username'][$node->nid] = array('#markup' => theme('username', $node)); diff -rupN modules/overlay/overlay.api.php modules/overlay/overlay.api.php --- modules/overlay/overlay.api.php 1970-01-01 01:00:00.000000000 +0100 +++ modules/overlay/overlay.api.php 2009-09-22 13:54:18.000000000 +0200 @@ -0,0 +1,50 @@ + -1 ? '&' : '?') + 'render=overlay' + fragment; + $(this).attr('href', href); + } + else { + $(this).attr('target', '_new'); + } + }); + $('form:not(.overlay-processed)', context).addClass('overlay-processed').each(function() { + // Obtain the action attribute of the form. + var action = $(this).attr('action'); + if (action.indexOf('http') != 0 && action.indexOf('https') != 0) { + // Keep internal forms in the overlay. + action += (action.indexOf('?') > -1 ? '&' : '?') + 'render=overlay'; + $(this).attr('action', action); + } + else { + $(this).attr('target', '_new'); + } + }); +}; + +})(jQuery); diff -rupN modules/overlay/overlay.info modules/overlay/overlay.info --- modules/overlay/overlay.info 2009-09-22 13:48:29.000000000 +0200 +++ modules/overlay/overlay.info 2009-09-22 13:54:18.000000000 +0200 @@ -0,0 +1,7 @@ +; $Id$ +name = Overlay +description = Displays the Drupal administration interface in an overlay. +package = Core +version = VERSION +core = 7.x +files[] = overlay.module diff -rupN modules/overlay/overlay.module modules/overlay/overlay.module --- modules/overlay/overlay.module 1970-01-01 01:00:00.000000000 +0100 +++ modules/overlay/overlay.module 2009-09-22 14:35:52.000000000 +0200 @@ -0,0 +1,285 @@ + 'overlay'); + } + } +} + +/** + * Implement hook_block_info_alter(). + */ +function overlay_block_info_alter(&$blocks) { + global $custom_theme; + + if (overlay_mode() == 'child') { + $themes = list_themes(); + $theme = $themes[$custom_theme]; + if (!empty($theme->info['overlay_regions'])) { + // Don't show any blocks except the main page content and the help text if + // we're in the overlay. + foreach ($blocks as $bid => $block) { + if (!in_array($block->region, $theme->info['overlay_regions'])) { + unset($blocks[$bid]); + } + } + } + } +} + +/** + * Implement hook_system_info_alter(). + * + * Add default regions for overlay. + */ +function overlay_system_info_alter(&$info, $file) { + $info['overlay_regions'][] = 'content'; + $info['overlay_regions'][] = 'help'; +} + +/** + * Preprocess template variables for html.tpl.php. + */ +function overlay_preprocess_html(&$variables) { + if (overlay_mode() == 'child') { + // Add overlay class, so themes can react to being displayed in the overlay. + $variables['classes_array'][] = 'overlay'; + // Do not include site name or slogan in the overlay title. + $variables['head_title'] = drupal_get_title(); + } +} + +/** + * Preprocess template variables for page.tpl.php. + */ +function overlay_preprocess_page(&$variables) { + if (overlay_mode() == 'child') { + // Remove 'Home' from the breadcrumbs. + $overlay_breadcrumb = drupal_get_breadcrumb(); + array_shift($overlay_breadcrumb); + $variables['breadcrumb'] = theme('breadcrumb', $overlay_breadcrumb); + } +} + +/** + * Form after build callback. + * + * After all ook_form_alter() implementations have been processed, we look at + * the list of submit handler and add our own at the end, so we can affect the + * redirection done at the end of the form processing if we are in the overlay + * children. + * + * @see _form_builder_handle_input_element() + * @see _form_builder_ie_cleanup() + * @see form_execute_handlers() + * @see form_builder() + * + * @ingroup forms + */ +function overlay_form_after_build($form, &$form_state) { + if (isset($_GET['render']) && $_GET['render'] == 'overlay') { + // Form API may have already captured submit handlers from the submitted + // button before after_build callback is invoked. This may have been done + // by _form_builder_handle_input_element(). + // If so, the list of submit handlers is stored in the $form_state array + // which is something we can also alter from here, luckily. + // Rememeber: our goal here is set $form_state['redirect'] is set to FALSE + // if the API overlay_close_dialog() has been invoked. That's because we + // want to tell the parent window to close the overlay. + if (!empty($form_state['submit_handlers']) && !in_array('overlay_form_submit', $form_state['submit_handlers'])) { + $form_state['submit_handlers'][] = 'overlay_form_submit'; + } + // If this element has submit handlers, then append our own. + if (isset($form['#submit'])) { + $form['#submit'][] = 'overlay_form_submit'; + } + } + return $form; +} + +/** + * Generic form submit handler. + * + * When we are requested to close an overlay, we don't want Form API to + * perform any redirection once the submitted form has been processed. + * + * When $form_state['redirect'] is set to FALSE, then Form API will simply + * re-render the form with the values still in its fields. And this is all + * we need to output the JavaScript that will tell the parent window to close + * the child dialog. + * + * @ingroup forms + */ +function overlay_form_submit($form, &$form_state) { + $settings = &drupal_static(__FUNCTION__); + + // Check if we have a request to close the overlay. + $args = overlay_close_dialog(); + + // Make sure the overlay is closed when a node edit form has been submitted + // in child mode. + if ($args === FALSE && overlay_mode() == 'child' && !empty($form['#node_edit_form'])) { + $args = overlay_close_dialog(TRUE); + } + + // Close the overlay only if specifically requested. + if ($args !== FALSE) { + if (!isset($settings)) { + $settings = array( + 'overlayChild' => array( + 'closeOverlay' => TRUE, + 'statusMessages' => theme('status_messages'), + 'args' => $args, + ), + ); + // Tell the child window to perform the redirection when requested to. + if (!empty($form_state['redirect'])) { + $settings['overlayChild']['redirect'] = url($form_state['redirect']); + } + drupal_add_js($settings, array('type' => 'setting')); + } + // Tell FAPI to redraw the form without redirection after all submit + // callbacks have been processed. + $form_state['redirect'] = FALSE; + } +} + +/** + * Set overlay mode and add proper JavaScript and styles to the page. + * + * @param $mode + * To set the mode, pass in either 'parent' or 'child'. 'parent' is used in + * the context of a parent overlay window, where the overlay may appear, and + * JavaScript is added accordingly to present an overlay dialog. 'child' is + * used in the context of the child overlay window, the one actually appearing + * within the overlay, and certain JavaScript and CSS is added so that Drupal + * behaves nicely from within the overlay. This parameter is optional, and if + * omitted, the current mode will be returned with no action taken. + * @return + * The current mode, if any has been set, or NULL if no mode has been set. + * @ingroup overlay_api + */ +function overlay_mode($mode = NULL) { + global $base_path; + $overlay_mode = &drupal_static(__FUNCTION__); + + // Make sure external resources are not included more than once. Also return + // current mode, if no mode was specified. + if (isset($overlay_mode) || !isset($mode)) { + return $overlay_mode; + } + $overlay_mode = $mode; + $module_path = drupal_get_path('module', 'overlay'); + + switch ($mode) { + case 'parent': + // Add required jQuery UI elements. Note that we don't use + // drupal_add_library() here, since we have no use for the CSS files added + // by the library. + drupal_add_js('misc/ui/ui.core.js', array('weight' => JS_LIBRARY + 5)); + drupal_add_js('misc/ui/ui.dialog.js', array('weight' => JS_LIBRARY + 6)); + + drupal_add_css($module_path . '/overlay-parent.css'); + drupal_add_js($module_path . '/overlay-parent.js'); + + // Allow modules to act upon overlay events. + module_invoke_all('overlay_parent_initialize'); + break; + + case 'child': + // Disable admin toolbar, which is something child windows don't need and + // shouldn't have. + if (module_exists('toolbar')) { + toolbar_enabled(FALSE); + } + + // This is required to get access to jQuery UI extensions to jQuery itself, + // such as the ':focusable' and ':tabbable' selectors. No need for the whole + // library, so not using drupal_add_library(). + drupal_add_js('misc/ui/ui.core.js', array('weight' => JS_LIBRARY + 5)); + + // Add JavaScript to the child page. + drupal_add_js($module_path . '/overlay-child.js'); + + // Allow modules to act upon overlay events. + module_invoke_all('overlay_child_initialize'); + break; + } + return $overlay_mode; +} + +/** + * Callback to close the overlay dialog. + * + * @param $set + * If set, will set the current close dialog mode to the given state. + * Use FALSE to disable close dialog mode. Otherwise, the argument will + * be forwarded to the onOverlayClose callback of the overlay. + * @return + * The current overlay close dialog mode, TRUE if the overlay should close or + * FALSE if it should not (default). + */ +function overlay_close_dialog($set = NULL) { + $close = &drupal_static(__FUNCTION__, FALSE); + if (isset($set)) { + $close = $set; + } + return $close; +} diff -rupN modules/overlay/overlay-parent.css modules/overlay/overlay-parent.css --- modules/overlay/overlay-parent.css 1970-01-01 01:00:00.000000000 +0100 +++ modules/overlay/overlay-parent.css 2009-09-22 13:54:18.000000000 +0200 @@ -0,0 +1,138 @@ +/* $Id$ */ + +/** + * jQuery UI Dialog classes. + */ +.overlay { + color: #000; + padding-right: 42px; +} + +.overlay .ui-dialog-titlebar { + position: relative; + height: 35px; + white-space: nowrap; + z-index: 2; + padding: 0 20px; +} + +.overlay .ui-dialog-title { + display: block; + font-family: Verdana,sans-serif; + position: absolute; + top: 0; + left: 20px; + margin: 0.3em 0; + padding: 0; + color: #fff; + font-size: 20px; +} + +.overlay .ui-dialog-title:active, +.overlay .ui-dialog-title:focus { + outline: 0; +} + +.overlay .ui-dialog-titlebar-close { + display: block; + position: absolute; + right: -31px; + top: 35px; + margin: 0; + padding: 0; + width: 32px; + height: 36px; + background: transparent url(images/close.png) no-repeat; +} + +.overlay .ui-dialog-titlebar-close span { + display: none; +} + +.overlay .ui-dialog-content { + color: #292929; + background-color: #f8f8f8; +} + +/** + * ui-dialog overlay. + */ +.ui-widget-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #000; + opacity: 0.7; + filter: alpha(opacity=80); +} + +/** + * Overlay content and shadows. + */ +#overlay-container { + margin: 0; + padding: 0; + background: #fff url(images/loading.gif) no-repeat 50% 50%; + -webkit-box-shadow: 8px 8px 8px rgba(0,0,0,.5); + -moz-box-shadow: 8px 8px 8px rgba(0,0,0,.5); + box-shadow: 8px 8px 8px rgba(0,0,0,.5); +} + +#overlay-element { + margin: 0; + padding: 0; + border: none; + overflow: hidden; +} + +/** + * Tabs on the overlay. + */ +.ui-dialog-titlebar ul { + float: right; + margin: 0 0 -10px 0; + padding: 3px 0 0 8px; + line-height: 40px; + text-transform: uppercase; + font-size: 12px; +} + +.ui-dialog-titlebar ul li { + display: inline; + list-style: none; + margin: 0 0 0 -3px; + padding-bottom: 0; +} + +.ui-dialog-titlebar ul li a, +.ui-dialog-titlebar ul li a:active, +.ui-dialog-titlebar ul li a:visited, +.ui-dialog-titlebar ul li a:hover { + background-color: #a6a7a2; + -moz-border-radius: 8px 8px 0 0; + -webkit-border-top-left-radius: 8px; + -webkit-border-top-right-radius: 8px; + border-radius: 8px 8px 0 0; + color: #000; + font-weight: bold; + padding: 8px 20px 4px 20px; + text-decoration: none; +} + +.ui-dialog-titlebar ul li.active a, +.ui-dialog-titlebar ul li.active a.active, +.ui-dialog-titlebar ul li.active a:active, +.ui-dialog-titlebar ul li.active a:visited { + background-color: #fff; + padding: 8px 20px 5px 20px; +} + +.ui-dialog-titlebar ul li a:hover { + color: #fff; +} + +.ui-dialog-titlebar ul li.active a:hover { + color: #000; +} diff -rupN modules/overlay/overlay-parent.js modules/overlay/overlay-parent.js --- modules/overlay/overlay-parent.js 1970-01-01 01:00:00.000000000 +0100 +++ modules/overlay/overlay-parent.js 2009-09-22 13:57:30.000000000 +0200 @@ -0,0 +1,645 @@ +// $Id: parent.js,v 1.1.4.4 2009/06/19 15:32:57 markuspetrux Exp $ + +(function ($) { + +/** + * Open or modify overlay based on clicks of links marked with .to-overlay. + */ +Drupal.behaviors.overlayParent = { + attach: function(context, settings) { + + // Attach on the .to-overlay class. + $('a.to-overlay:not(.overlay-exclude)').once('overlay').click(function() { + + // Remove the active class from where it was, and add the active class to + // this link, so the button keeps highlighting where we are. Only + // highlight active items in the shortcuts bar. + $('#toolbar a').each(function() { + $(this).removeClass('active'); + }); + if ($(this).parents('div.toolbar-shortcuts').length) { + $(this).addClass('active'); + } + + // Append render variable, so the server side can choose the right + // rendering and add child modal frame code to the page if needed. + var linkURL = $(this).attr('href'); + linkURL += (linkURL.indexOf('?') > -1 ? '&' : '?') + 'render=overlay'; + + // If the modal frame is already open, replace the loaded document with + // this new one. Keeps browser history. + if (Drupal.overlay.isOpen) { + Drupal.overlay.load(linkURL); + return false; + } + + // There is overlay opened yet, we should open a new one. + var toolbarHeight = $('#toolbar').height(); + var overlayOptions = { + url: linkURL, + width: $(window).width() - 40, + height: $(window).height() - 40 - toolbarHeight, + // Remove active class from all header buttons. + onOverlayClose: function() { $('#toolbar a').each(function() { $(this).removeClass('active'); }); }, + draggable: false + }; + Drupal.overlay.open(overlayOptions); + + // Set position and styling to let the admin toolbar work. + $('.overlay').css('top', toolbarHeight + 20); + $('#toolbar').css('z-index', 2000); + + // Prevent default action of the link click event. + return false; + }); + } +}; + +/** + * Overlay object for parent windows. + */ +Drupal.overlay = Drupal.overlay || { + options: {}, + iframe: { $container: null, $element: null }, + isOpen: false +}; + +/** + * Open an overlay. + * + * Ensure that only one overlay is opened ever. Use Drupal.overlay.load() if + * the overlay is already open but a new page needs to be opened. + * + * @param options + * Properties of the overlay to open: + * - url: the URL of the page to open in the overlay. + * - width: width of the overlay in pixels. + * - height: height of the overlay in pixels. + * - autoFit: boolean indicating whether the overlay should be resized to + * fit the contents of the document loaded. + * - onOverlayOpen: callback to invoke when the overlay is opened. + * - onOverlayCanClose: callback to allow external scripts decide if the + * overlay can be closed. + * - onOverlayClose: callback to invoke when the overlay is closed. + * - customDialogOptions: an object with custom jQuery UI Dialog options. + * @return + * If the overlay was opened true, otherwise false. + */ +Drupal.overlay.open = function(options) { + var self = this; + + // Just one overlay is allowed. + if (self.isOpen || $('#overlay-container').size()) { + return false; + } + + var defaultOptions = { + url: options.url, + width: options.width, + height: options.height, + autoFit: (options.autoFit == undefined || options.autoFit), + onOverlayOpen: options.onOverlayOpen, + onOverlayCanClose: options.onOverlayCanClose, + onOverlayClose: options.onOverlayClose, + customDialogOptions: options.customDialogOptions || {} + } + + self.options = $.extend(defaultOptions, options); + + // Create the dialog and related DOM elements. + self.create(); + + // Open the dialog offscreen where we can set its size, etc. + self.iframe.$container.dialog('option', { position: ['-999em', '-999em'] }).dialog('open'); + + return true; +}; + +/** + * Create the underlying markup and behaviors for the overlay. + * + * Reuses jQuery UI's dialog component to construct the overlay markup and + * behaviors, sanitizing the options previously set in self.options. + */ +Drupal.overlay.create = function() { + var self = this; + + // Note: We use scrolling="yes" for IE as a workaround to yet another IE bug + // where the horizontal scrollbar is always rendered no matter how wide the + // iframe element is defined. + self.iframe.$element = $('