Index: misc/tableheader.js =================================================================== RCS file: /cvs/drupal/drupal/misc/tableheader.js,v retrieving revision 1.31 diff -u -r1.31 tableheader.js --- misc/tableheader.js 23 May 2010 18:23:32 -0000 1.31 +++ misc/tableheader.js 24 May 2010 11:09:25 -0000 @@ -42,7 +42,7 @@ // Track positioning and visibility. function tracker(e) { // Reset top position of sticky table headers to the current top offset. - var topOffset = Drupal.settings.tableHeaderOffset ? eval(Drupal.settings.tableHeaderOffset + '()') : 0; + var topOffset = Drupal.displace ? Drupal.displace.getDisplacement('top') : 0; $('.sticky-header').css('top', topOffset + 'px'); // Save positioning data. var viewHeight = document.documentElement.scrollHeight || document.body.scrollHeight; Index: misc/drupal.js =================================================================== RCS file: /cvs/drupal/drupal/misc/drupal.js,v retrieving revision 1.68 diff -u -r1.68 drupal.js --- misc/drupal.js 24 May 2010 07:22:12 -0000 1.68 +++ misc/drupal.js 24 May 2010 11:09:25 -0000 @@ -331,7 +331,21 @@ // 'js enabled' cookie. document.cookie = 'has_js=1; path=/'; -// Attach all behaviors. +/** + * Additions to jQuery.support. + */ +$(function () { + /** + * Boolean indicating whether or not position:fixed is supported. + */ + if (jQuery.support.positionFixed === undefined) { + var el = $('
').appendTo(document.body); + jQuery.support.positionFixed = el[0].offsetTop === 10; + el.remove(); + } +}); + +//Attach all behaviors. $(function () { Drupal.attachBehaviors(document, Drupal.settings); }); Index: modules/toolbar/toolbar.tpl.php =================================================================== RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.tpl.php,v retrieving revision 1.11 diff -u -r1.11 toolbar.tpl.php --- modules/toolbar/toolbar.tpl.php 23 May 2010 18:23:32 -0000 1.11 +++ modules/toolbar/toolbar.tpl.php 24 May 2010 11:09:26 -0000 @@ -22,7 +22,7 @@ * @see template_preprocess_toolbar() */ ?> -
+
Index: modules/toolbar/toolbar.js =================================================================== RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.js,v retrieving revision 1.18 diff -u -r1.18 toolbar.js --- modules/toolbar/toolbar.js 23 May 2010 18:23:32 -0000 1.18 +++ modules/toolbar/toolbar.js 24 May 2010 11:09:26 -0000 @@ -15,9 +15,8 @@ // Toggling toolbar drawer. $('#toolbar a.toggle', context).once('toolbar-toggle').click(function(e) { Drupal.toolbar.toggle(); - // As the toolbar is an overlay displaced region, overlay should be - // notified of it's height change to adapt its position. - $(window).triggerHandler('resize.overlay-event'); + // Allow resize event handlers to recalculate sizes/positions. + $(window).triggerHandler('resize'); return false; }); } @@ -49,7 +48,6 @@ .removeClass('toggle-active') .attr('title', toggle_text) .html(toggle_text); - $('body').removeClass('toolbar-drawer').css('paddingTop', Drupal.toolbar.height()); $.cookie( 'Drupal.toolbar.collapsed', 1, @@ -71,7 +69,6 @@ .addClass('toggle-active') .attr('title', toggle_text) .html(toggle_text); - $('body').addClass('toolbar-drawer').css('paddingTop', Drupal.toolbar.height()); $.cookie( 'Drupal.toolbar.collapsed', 0, @@ -95,14 +92,4 @@ } }; -Drupal.toolbar.height = function() { - var height = $('#toolbar').outerHeight(); - // In IE, Shadow filter adds some extra height, so we need to remove it from - // the returned height. - if ($('#toolbar').css('filter').match(/DXImageTransform\.Microsoft\.Shadow/)) { - height -= $('#toolbar').get(0).filters.item("DXImageTransform.Microsoft.Shadow").strength; - } - return height; -}; - })(jQuery); Index: modules/toolbar/toolbar.module =================================================================== RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.module,v retrieving revision 1.40 diff -u -r1.40 toolbar.module --- modules/toolbar/toolbar.module 23 May 2010 18:23:32 -0000 1.40 +++ modules/toolbar/toolbar.module 24 May 2010 11:09:26 -0000 @@ -177,12 +177,9 @@ '#theme' => 'toolbar', '#attached'=> array( 'js' => array( - $module_path . '/toolbar.js', + array('data' => 'misc/displace.js', 'weight' => JS_LIBRARY - 1), array('data' => 'misc/jquery.cookie.js', 'weight' => JS_LIBRARY + 2), - array( - 'data' => array('tableHeaderOffset' => 'Drupal.toolbar.height'), - 'type' => 'setting' - ), + $module_path . '/toolbar.js', ), 'css' => array( $module_path . '/toolbar.css', Index: modules/toolbar/toolbar.css =================================================================== RCS file: /cvs/drupal/drupal/modules/toolbar/toolbar.css,v retrieving revision 1.22 diff -u -r1.22 toolbar.css --- modules/toolbar/toolbar.css 23 May 2010 18:23:32 -0000 1.22 +++ modules/toolbar/toolbar.css 24 May 2010 11:09:25 -0000 @@ -1,13 +1,5 @@ /* $Id: toolbar.css,v 1.22 2010/05/23 18:23:32 dries Exp $ */ -body.toolbar { - padding-top: 2.2em; -} - -body.toolbar-drawer { - padding-top: 5.3em; -} - /** * Aggressive resets so we can achieve a consistent look in hostile CSS * environments. @@ -32,10 +24,8 @@ font: normal 0.9em "Lucida Grande", Verdana, sans-serif; background: #666; color: #ccc; - position: fixed; - top: 0; - left: 0; - right: 0; +} +.displace-processed #toolbar { margin: 0 -20px; padding: 0 20px; z-index: 600; @@ -45,6 +35,13 @@ filter: progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10'); -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(color=#000000, direction='180', strength='10')"; } +.displace-unsupported #toolbar { + margin: 0; + padding-right: 0; + left: -20px; + right: 0; + width: 100%; +} #toolbar div.collapsed { display: none; @@ -132,18 +129,3 @@ position: relative; padding: 0 10px; } - -/** - * IE 6 Fix. - * - * IE 6 shows elements with position:fixed as position:static so we replace - * it with position:absolute; toolbar needs it's z-index to stay above overlay. - */ -* html #toolbar { - position: absolute; - margin: 0; - padding-right: 0; - left: -20px; - right: 0; - width: 100%; -} Index: modules/overlay/overlay.module =================================================================== RCS file: /cvs/drupal/drupal/modules/overlay/overlay.module,v retrieving revision 1.19 diff -u -r1.19 overlay.module --- modules/overlay/overlay.module 23 May 2010 18:23:32 -0000 1.19 +++ modules/overlay/overlay.module 24 May 2010 11:09:25 -0000 @@ -300,16 +300,6 @@ } /** - * Preprocess template variables for toolbar.tpl.php. - * - * Adding the 'overlay-displace-top' class to the toolbar pushes the overlay - * down, so it appears below the toolbar. - */ -function overlay_preprocess_toolbar(&$variables) { - $variables['classes_array'][] = "overlay-displace-top"; -} - -/** * Form after_build callback. * * After all hook_form_alter() implementations have been processed, we look at Index: modules/overlay/overlay-parent.js =================================================================== RCS file: /cvs/drupal/drupal/modules/overlay/overlay-parent.js,v retrieving revision 1.42 diff -u -r1.42 overlay-parent.js --- modules/overlay/overlay-parent.js 23 May 2010 18:23:32 -0000 1.42 +++ modules/overlay/overlay-parent.js 24 May 2010 11:09:25 -0000 @@ -650,15 +650,7 @@ return; } - // Consider any region that should be visible above the overlay (such as - // an admin toolbar). - var $displaceTop = $('.overlay-displace-top'); - var displaceTopHeight = 0; - $displaceTop.each(function () { - displaceTopHeight += $(this).height(); - }); - - self.$wrapper.css('top', displaceTopHeight); + var displaceTop = Drupal.displace ? Drupal.displace.getDisplacement('top') : 0; // When the overlay has no height yet, make it fit exactly in the window, // or the configured height when autoFit is disabled. @@ -666,7 +658,7 @@ var titleBarHeight = self.$dialogTitlebar.outerHeight(true); if (self.options.autoFit || self.options.height == undefined ||!isNan(self.options.height)) { - self.lastHeight = parseInt($(window).height() - displaceTopHeight - titleBarHeight - 45); + self.lastHeight = parseInt($(window).height() - displaceTop - titleBarHeight - 45); } else { self.lastHeight = self.options.height; @@ -703,11 +695,11 @@ var $target = $(event.target); - if (self.isOpen && $target.closest('.overlay-displace-top, .overlay-displace-bottom').length) { + if (self.isOpen && $target.closest('.displace-top, .displace-bottom').length) { // Click events in displaced regions could potentionally change the size of // that region (e.g. the toggle button of the toolbar module). Trigger the // resize event to force a recalculation of overlay's size/position. - $(window).triggerHandler('resize.overlay-event'); + $(window).triggerHandler('resize'); } // Only continue by left-click or right-click. @@ -929,7 +921,7 @@ var self = this; var windowDomain = window.location.protocol + window.location.hostname; - $('.overlay-displace-top, .overlay-displace-bottom') + $('.displace-top, .displace-bottom') .find('a[href]') // Remove active class from all links in displaced regions. .removeClass('active') Index: modules/system/system.css =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.css,v retrieving revision 1.77 diff -u -r1.77 system.css --- modules/system/system.css 23 May 2010 18:23:32 -0000 1.77 +++ modules/system/system.css 24 May 2010 11:09:25 -0000 @@ -257,41 +257,31 @@ /* ** To be used with displace.js */ -.displace-top, -.displace-bottom { +.displace-processed body { position: relative; - width: 100%; -} -.displace-processed .displace-top, -.displace-processed .displace-bottom { - position: fixed; - width: auto; - left: 0; - right: 0; } -.displace-unsupported .displace-top, -.displace-unsupported .displace-bottom { - position: absolute; -} - -/* -** To be used with displace.js -*/ .displace-top, .displace-bottom { position: relative; width: 100%; } .displace-processed .displace-top, -.displace-processed .displace-bottom { +.displace-processed .displace-bottom, +.displace-processed .displace-absolute.displace-absolute-fixed { position: fixed; + z-index: 600; width: auto; left: 0; right: 0; } +.displace-processed .displace-absolute { + position: absolute; + z-index: 500; +} .displace-unsupported .displace-top, .displace-unsupported .displace-bottom { position: absolute; + width: 100%; } /* Index: misc/displace.js =================================================================== RCS file: misc/displace.js diff -N misc/displace.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ misc/displace.js 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,316 @@ +// $Id$ +(function ($) { + +/** + * Provides a generic method to position elements fixed to the viewport. + * + * Fixed positioning (CSS declaration position:fixed) is done relative to the + * viewport. This makes it hard to position multiple fixed positioned element + * relative to each other (e.g. multiple toolbars should come after each other, + * not on top of each other). + * + * To position an element fixed at the top of the viewport add the class + * "displace-top" to that element, and to position it to the bottom of the view- + * port add the class "displace-bottom". + * + * To position an element absolute at the top of the viewport also add the + * class "displace-absolute". + * + * When a displacement region contains both absolute and fixed positioned + * elements, the absolute positioned elements will be positioned fixed too, but + * will only be visible when they would be visible if they were positioned + * absolute. This is done to improve the look & feel. + * + * When a browser does not support position:fixed (like IE6) the displaced + * elements automaticly receive the class "displace-unsupported" and are + * positioned absolute. + */ + +Drupal.behaviors.displace = { + /** + * Attaches the displace behavior. + */ + attach: function (context, settings) { + // Test for position:fixed support. + if (!$.support.positionFixed) { + $(document.documentElement).addClass('displace-unsupported'); + } + + Drupal.displace.clearCache(true); + + if (Drupal.displace.getDisplacedElements('top') || Drupal.displace.getDisplacedElements('bottom')) { + var events = 'resize.drupal-displace'; + // If there are both absolute and fixed displaced elements also add + // a scroll event handler. + if (Drupal.displace.mixedDisplacement('top') || Drupal.displace.mixedDisplacement('bottom')) { + events += ' scroll.drupal-displace'; + } + $(window) + .bind(events, Drupal.displace.eventhandlerDisplaceDocument) + .bind('drupalDisplaceAnchor.drupal-displace', Drupal.displace.eventhandlerDisplaceAnchor) + .bind('drupalDisplaceFocus.drupal-displace', Drupal.displace.eventhandlerDisplaceFocus); + } + // Unbind any previously bound event handlers as there are no displaced + // elements to be taken into account. + else { + $(window).unbind('.drupal-displace'); + } + + $(document.documentElement).once('displace', function () { + // Detect when an anchor is being scrolled into view. + $(window).bind('scroll.drupal-displace-anchor', Drupal.displace.eventhandlerDetectAnchorScrolling); + // Detect when the focus is being set to an element. + $(document).bind('focusin.drupal-displace-focus', Drupal.displace.eventhandlerDetectFocusMovement); + }); + + $(window).triggerHandler('resize.drupal-displace'); + $(window).triggerHandler('scroll.drupal-displace-anchor'); + }, + + /** + * Detaches the displace behavior. + */ + detach: function (context, settings, trigger) { + if (trigger == 'unload' || trigger == 'move') { + Drupal.displace.clearCache(true); + } + } +}; + +/** + * The displace object. + */ +Drupal.displace = Drupal.displace || {}; +Drupal.displace.prototype = {}; + +/** + * Get all displaced elements of given region. + * + * @param region + * Region name. Either "top" or "bottom". + * + * @return + * jQuery object containing all displaced elements of given region. + */ +Drupal.displace.getDisplacedElements = function (region) { + if (!this._elements[region]) { + this._elements[region] = $('.displace-' + region); + } + return this._elements[region]; +}; + +/** + * Checks whether there are both absolute and fixed displaced elements. + * + * @param region + * Region name. Either "top" or "bottom". + * + * @return + * TRUE if there are both absolute and fixed displaced elements in given + * region, FALSE otherwise. + */ +Drupal.displace.mixedDisplacement = function (region) { + if (!this._mixedDisplacement[region]) { + this._mixedDisplacement[region] = Drupal.displace.getDisplacedElements(region).is('.displace-absolute') && Drupal.displace.getDisplacedElements(region).is(':not(.displace-absolute)'); + } + return this._mixedDisplacement[region]; +}; + +/** + * Get the total displacement of given region. + * + * @param region + * Region name. Either "top" or "bottom". + * + * @return + * The total displacement of given region in pixels. + */ +Drupal.displace.getDisplacement = function (region) { + if (!this._displacement[region]) { + var offset = 0; + var height = 0; + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + this.getDisplacedElements(region).each(function () { + offset = offset + height; + height = $(this).css(region, offset).outerHeight(); + + // Make sure absolute and fixed displaced elements work together. + if (Drupal.displace.mixedDisplacement(region) && $(this).hasClass('displace-absolute')) { + if ((region == 'top' && scrollTop < offset + height) || (region == 'bottom' && scrollTop + $(window).height() > document.body.scrollHeight - offset + height)) { + $(this).addClass('displace-absolute-fixed'); + } + // Skip this absolute displaced element as it is outside the viewport. + else { + $(this).removeClass('displace-absolute-fixed'); + height = 0; + } + } + + // In IE, Shadow filter adds some extra height, so we need to remove it + // from the returned height. + if (this.filters && this.filters.length && this.filters.item('DXImageTransform.Microsoft.Shadow')) { + height -= this.filters.item('DXImageTransform.Microsoft.Shadow').strength; + height = Math.max(0, height); + } + }); + + // Use offset of latest displaced element as the total displacement. + this._displacement[region] = offset + height; + } + + return this._displacement[region]; +}; + +/** + * Clear cache. + * + * @param selectorCache + * Boolean whether to also clear the selector cache. + */ +Drupal.displace.clearCache = function (selectorCache) { + if (selectorCache) { + this._elements = []; + this._mixedDisplacement = []; + } + this._displacement = []; +}; + +/** + * Creates an event object containing position data. + * + * @param type + * Describes the nature of the event. + * @param target + * The DOM element that initiated the event. + */ +Drupal.displace.createEvent = function (type, target) { + var event = jQuery.Event(type); + var $target = $(target); + var targetOffset = $target.offset(); + + event.target = target; + event.$target = $target; + // The amount of pixels the entire document has been scrolled. + event.pageXOffset = $(window).scrollLeft(); + event.pageYOffset = $(window).scrollTop(); + // The position of the target element relative to the document. + event.pageX = Math.round(targetOffset.left); + event.pageY = Math.round(targetOffset.top); + // The position of the target element relative to the viewport. + event.clientX = event.pageX - event.pageXOffset; + event.clientY = event.pageY - event.pageYOffset; + + return event; +}; + +/** + * Resize/scroll event handler: makes sure there is enough space for displaced + * elements at the top and bottom of the document. + */ +Drupal.displace.eventhandlerDisplaceDocument = function (event) { + Drupal.displace.clearCache(); + + // Adjust paddingTop and paddingBottom of document root element to make sure + // nothing is hidden beneath any displaced elements. + var adjustment = { + paddingTop: Drupal.displace.getDisplacement('top') || null, + paddingBottom: Drupal.displace.getDisplacement('bottom') || null + }; + if (Drupal.displace.rootHasBackground === undefined || Drupal.displace.bodyHasBackground === undefined) { + Drupal.displace.rootHasBackground = $(document.documentElement).css('background-image') != 'none' && $(document.documentElement).css('background-color') != 'transparent'; + Drupal.displace.bodyHasBackground = $(document.body).css('background-image') != 'none'; + } + // To move the background-image of the BODY element along with the padding + // to be added to the HTML element, the HTML element needs to have an + // background-image or -color; if it does not, the BODY element is being + // treated as being the root element and will cover the entire window + // (http://www.w3.org/TR/CSS21/colors.html#background). If the HTML element + // already has a background set don't adjust anything else, as the BODY + // is already expected not to cover the entire window. + // Note that marginTop works for all browsers except for IE7/IE6, but + // marginBottom also won't work for IE8 and FF (at least until 3.6). + if (Drupal.displace.bodyHasBackground && !Drupal.displace.rootHasBackground && (Drupal.displace.rootBackgroundChanged || adjustment.paddingTop || adjustment.paddingBottom)) { + // Add a background-color to the HTML element and make sure BODY still + // covers the entire window by giving it a minimum height. + if (adjustment.paddingTop || adjustment.paddingBottom) { + adjustment.backgroundColor = '#fff'; + $(document.body).css('minHeight', $(window).height() - adjustment.paddingTop - adjustment.paddingBottom); + } + else { + adjustment.backgroundColor = null; + $(document.body).css('minHeight', null); + } + Drupal.displace.rootBackgroundChanged = true; + } + + // If position:fixed is not supported, displaced elements are positioned + // absolute; their positions are relative to their offset parent (BODY) and + // will therefor be moved along with the padding; move them back to the top + // or bottom of the window. + if (!$.support.positionFixed && (adjustment.paddingTop || adjustment.paddingBottom)) { + var regions = [ 'top', 'bottom' ]; + for (var i in regions) { + Drupal.displace.getDisplacedElements(regions[i]).each(function () { + $(this).css(regions[i], (parseInt($(this).css(regions[i])) - Drupal.displace.getDisplacement(regions[i]))); + }); + } + } + + // Apply adjustments to the document. + $(document.documentElement).css(adjustment); +}; + +/** + * Scroll event handler: detects when an anchor is being scrolled into view. + */ +Drupal.displace.eventhandlerDetectAnchorScrolling = function (event) { + if (location.hash && location.hash != '#') { + // Delay the timeout as long as events are triggered. + clearTimeout(Drupal.displace.displaceAnchorTimeout); + Drupal.displace.displaceAnchorTimeout = setTimeout(function () { + var $anchor = $(location.hash); + if ($anchor.length && Math.round($anchor.offset().top) == $(window).scrollTop()) { + $(window).trigger(Drupal.displace.createEvent('drupalDisplaceAnchor', $anchor[0])); + } + }, 100); + } +}; + +/** + * Focusin event handler: detects when the focus is being set to an element. + */ +Drupal.displace.eventhandlerDetectFocusMovement = function (event) { + if (event.target.nodeType === 1) { + // Make sure target element is not fixed positioned. + var offsetParent = $(event.target).offsetParent(); + while (offsetParent.length && offsetParent[0] !== document.body) { + if (offsetParent.css('position') == 'fixed') { + return; + } + offsetParent = $(offsetParent[0]).offsetParent(); + } + + $(window).trigger(Drupal.displace.createEvent('drupalDisplaceFocus', event.target)); + } +}; + +/** + * DrupalDisplaceAnchor event handler: makes sure the anchor being scrolled into + * view is not hidden beneath any displaced elements. Adjusts the scrollTop if + * it does. + */ +Drupal.displace.eventhandlerDisplaceAnchor = function (event) { + window.scrollBy(0, -Drupal.displace.getDisplacement('top')); +}; + +/** + * DrupalDisplaceFocus event handler: makes sure the element being focused is + * not hidden beneath any displaced elements. Adjusts the scrollTop if it does. + */ +Drupal.displace.eventhandlerDisplaceFocus = function (event) { + if (event.clientY < Drupal.displace.getDisplacement('top')) { + window.scrollBy(0, -(Drupal.displace.getDisplacement('top') - event.clientY)); + } +}; + +})(jQuery);