').parent().css({
+ // Clone the table header 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 --git modules/dashboard/dashboard.css modules/dashboard/dashboard.css
index 05980a8..69d38f8 100644
--- modules/dashboard/dashboard.css
+++ modules/dashboard/dashboard.css
@@ -64,9 +64,11 @@
border: 0;
}
-#dashboard .canvas-content input {
+#dashboard .canvas-content a.button {
float: right;
margin: 0 0 0 10px;
+ color: #5a5a5a;
+ text-decoration: none;
}
#dashboard .region {
diff --git modules/dashboard/dashboard.js modules/dashboard/dashboard.js
index fedb542..2af23ad 100644
--- modules/dashboard/dashboard.js
+++ modules/dashboard/dashboard.js
@@ -65,7 +65,7 @@ Drupal.behaviors.dashboard = {
* Helper for enterCustomizeMode; sets up drag-and-drop and close button.
*/
setupDrawer: function () {
- $('div.customize .canvas-content').prepend('');
+ $('div.customize .canvas-content').prepend('' + Drupal.t('Done') + '');
$('div.customize .canvas-content input').click(Drupal.behaviors.dashboard.exitCustomizeMode);
// Initialize drag-and-drop.
diff --git modules/locale/locale.test modules/locale/locale.test
index ba92516..5760f44 100644
--- modules/locale/locale.test
+++ modules/locale/locale.test
@@ -237,7 +237,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
$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 --git modules/overlay/overlay-child.js modules/overlay/overlay-child.js
new file mode 100644
index 0000000..b27d775
--- /dev/null
+++ modules/overlay/overlay-child.js
@@ -0,0 +1,138 @@
+// $Id: child.js,v 1.1.4.3 2009/06/17 15:16:26 markuspetrux Exp $
+
+(function ($) {
+
+/**
+ * Overlay object for child windows.
+ */
+Drupal.overlayChild = Drupal.overlayChild || { processed: false, behaviors: {} };
+
+/**
+ * Attach the child dialog behavior to new content.
+ */
+Drupal.behaviors.overlayChild = {
+ attach: function (context, settings) {
+ var self = Drupal.overlayChild;
+ var settings = settings.overlayChild || {};
+
+ // Make sure this behavior is not processed more than once.
+ if (self.processed) {
+ return;
+ }
+ self.processed = true;
+
+ // If we cannot reach the parent window, then we have nothing else to do
+ // here.
+ if (!Drupal.isObject(parent.Drupal) || !Drupal.isObject(parent.Drupal.overlay)) {
+ return;
+ }
+
+ // If a form has been submitted successfully, then the server side script
+ // may have decided to tell us the parent window to close the popup dialog.
+ if (settings.closeOverlay) {
+ parent.Drupal.overlay.bindChild(window, true);
+ // Close the child window from a separate thread because the current
+ // one is busy processing Drupal behaviors.
+ setTimeout(function () {
+ // We need to store the parent variable locally because it will
+ // disappear as soon as we close the iframe.
+ var p = parent;
+ p.Drupal.overlay.close(settings.args, settings.statusMessages);
+ if (typeof settings.redirect == 'string') {
+ p.Drupal.overlay.redirect(settings.redirect);
+ }
+ }, 1);
+ return;
+ }
+
+ // Ok, now we can tell the parent window we're ready.
+ parent.Drupal.overlay.bindChild(window);
+
+ // Install onBeforeUnload callback, if module is present.
+ if (Drupal.isObject(Drupal.onBeforeUnload) && !Drupal.onBeforeUnload.callbackExists('overlayChild')) {
+ Drupal.onBeforeUnload.addCallback('overlayChild', function () {
+ // Tell the parent window we're unloading.
+ parent.Drupal.overlay.unbindChild(window);
+ });
+ }
+
+ // Attach child related behaviors to the iframe document.
+ self.attachBehaviors(context, settings);
+ }
+};
+
+/**
+ * Attach child related behaviors to the iframe document.
+ */
+Drupal.overlayChild.attachBehaviors = function (context, settings) {
+ $.each(this.behaviors, function () {
+ this(context, settings);
+ });
+};
+
+/**
+ * Scroll to the top of the page.
+ *
+ * This makes the overlay visible to users even if it is not as tall as the
+ * previously shown overlay was.
+ */
+Drupal.overlayChild.behaviors.scrollToTop = function (context, settings) {
+ window.scrollTo(0, 0);
+};
+
+/**
+ * Modify links and forms depending on their relation to the overlay.
+ *
+ * By default, forms and links are assumed to keep the flow in the overlay.
+ * Thus their action and href attributes respectively get a ?render=overlay
+ * suffix. Non-administrative links should however close the overlay and
+ * redirect the parent page to the given link. This would include links in a
+ * content listing, where administration options are mixed with links to the
+ * actual content to be shown on the site out of the overlay.
+ *
+ * @see Drupal.overlay.isAdminLink()
+ */
+Drupal.overlayChild.behaviors.parseLinks = function (context, settings) {
+ $('a:not(.overlay-exclude)', context).once('overlay').each(function () {
+ // Non-admin links should close the overlay and open in the main window.
+ if (!parent.Drupal.overlay.isAdminLink(this.href)) {
+ $(this).click(function () {
+ // We need to store the parent variable locally because it will
+ // disappear as soon as we close the iframe.
+ var parentWindow = parent;
+ if (parentWindow.Drupal.overlay.close(false)) {
+ parentWindow.Drupal.overlay.redirect($(this).attr('href'));
+ }
+ return false;
+ });
+ return;
+ }
+ else {
+ var href = $(this).attr('href');
+ if (href.indexOf('http') > 0 || href.indexOf('https') > 0) {
+ $(this).attr('target', '_new');
+ }
+ else {
+ $(this).click(function () {
+ var linkURL = parent.Drupal.overlay.addOverlayParam($(this).attr('href'));
+ parent.Drupal.overlay.load(linkURL);
+ return false;
+ });
+ }
+ }
+ });
+ $('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 --git modules/overlay/overlay-parent.css modules/overlay/overlay-parent.css
new file mode 100644
index 0000000..448c5bc
--- /dev/null
+++ modules/overlay/overlay-parent.css
@@ -0,0 +1,123 @@
+/* $Id$ */
+
+/**
+ * ui-dialog overlay.
+ */
+.ui-widget-overlay {
+ background-color: #000;
+ opacity: 0.7;
+ filter: alpha(opacity=80);
+ background-image: none;
+}
+
+/**
+ * jQuery UI Dialog classes.
+ */
+.overlay {
+ padding-right: 26px;
+}
+
+.overlay.ui-widget-content, .overlay .ui-widget-header {
+ background: none;
+ border: none;
+}
+
+.overlay .ui-dialog-titlebar {
+ white-space: nowrap;
+ padding: 0 20px;
+}
+
+.overlay .ui-dialog-title {
+ font-family: Verdana,sans-serif;
+ margin: 0;
+ padding: 0.3em 0;
+ color: #fff;
+ font-size: 20px;
+}
+.overlay .ui-dialog-title:active,
+.overlay .ui-dialog-title:focus {
+ outline: 0;
+}
+.overlay .ui-dialog-titlebar-close,
+.overlay .ui-dialog-titlebar-close:hover {
+ display: block;
+ right: -25px;
+ top: 100%;
+ margin: 0;
+ border: none;
+ padding: 0;
+ width: 26px;
+ height: 36px;
+ background: transparent url(images/close.png) no-repeat;
+ -moz-border-radius-topleft: 0;
+ -webkit-border-top-left-radius: 0;
+}
+.overlay .ui-dialog-titlebar-close span {
+ display: none;
+}
+.overlay .ui-dialog-content {
+ color: #292929;
+ background-color: #f8f8f8;
+}
+
+/**
+ * Overlay content and shadows.
+ */
+.overlay #overlay-container {
+ margin: 0;
+ padding: 0;
+ overflow: visible;
+ 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 #overlay-element {
+ overflow: hidden;
+}
+
+/**
+ * Tabs on the overlay.
+ */
+.overlay .ui-dialog-titlebar ul {
+ position: absolute;
+ right: 20px;
+ bottom: 0;
+ margin: 0;
+ line-height: 27px;
+ text-transform: uppercase;
+}
+.overlay .ui-dialog-titlebar ul li {
+ display: inline-block;
+ list-style: none;
+ margin: 0 0 0 -3px;
+ padding: 0;
+}
+.overlay .ui-dialog-titlebar ul li a,
+.overlay .ui-dialog-titlebar ul li a:active,
+.overlay .ui-dialog-titlebar ul li a:visited,
+.overlay .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: 5px 14px;
+ text-decoration: none;
+ font-size: 11px;
+}
+.overlay .ui-dialog-titlebar ul li.active a,
+.overlay .ui-dialog-titlebar ul li.active a.active,
+.overlay .ui-dialog-titlebar ul li.active a:active,
+.overlay .ui-dialog-titlebar ul li.active a:visited {
+ background-color: #fff;
+ padding-bottom: 7px;
+}
+.overlay .ui-dialog-titlebar ul li a:hover {
+ color: #fff;
+}
+.overlay .ui-dialog-titlebar ul li.active a:hover {
+ color: #000;
+}
diff --git modules/overlay/overlay-parent.js modules/overlay/overlay-parent.js
new file mode 100644
index 0000000..3af30cc
--- /dev/null
+++ modules/overlay/overlay-parent.js
@@ -0,0 +1,764 @@
+// $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 all admin links without the 'overlay-exclude' class.
+ $('a:not(.overlay-exclude)').filter(function () {
+ return Drupal.overlay.isAdminLink(this.href);
+ })
+ // Respond to their click event.
+ .once('overlay').click(function () {
+
+ // 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 = Drupal.overlay.addOverlayParam($(this).attr('href'));
+
+ // 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 not an overlay opened yet, we should open a new one.
+ var overlayOptions = {
+ url: linkURL,
+
+ // Remove active class from all header buttons.
+ onOverlayClose: function () {
+ $('a.to-overlay').each(function () {
+ $(this).removeClass('active');
+ });
+ },
+ draggable: false
+ };
+ Drupal.overlay.open(overlayOptions);
+
+ // Prevent default action of the link click event.
+ return false;
+ });
+
+ // Automatically open an overlay if defined in Drupal.settings.overlay.autoOpen.
+ if (Drupal.settings.overlay.autoOpen) {
+ var linkURL = Drupal.overlay.addOverlayParam(Drupal.settings.overlay.autoOpen);
+
+ // Unset autoOpen to prevent looping.
+ delete Drupal.settings.overlay.autoOpen;
+
+ // 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 not an overlay opened yet, we should open a new one.
+ var overlayOptions = {
+ url: linkURL,
+
+ // Remove active class from all header buttons.
+ onOverlayClose: function () {
+ $('a.to-overlay').each(function () {
+ $(this).removeClass('active');
+ });
+ },
+ draggable: false
+ };
+ Drupal.overlay.open(overlayOptions);
+ }
+ }
+};
+
+/**
+ * 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 = $('');
+ self.iframe.$container = $('').append(self.iframe.$element);
+
+ $('body').append(self.iframe.$container);
+
+ // Open callback for jQuery UI Dialog.
+ var dialogOpen = function () {
+ // Unbind the keypress handler installed by ui.dialog itself.
+ // IE does not fire keypress events for some non-alphanumeric keys
+ // such as the tab character. http://www.quirksmode.org/js/keys.html
+ // Also, this is not necessary here because we need to deal with an
+ // iframe element that contains a separate window.
+ // We'll try to provide our own behavior from bindChild() method.
+ $('.overlay').unbind('keypress.ui-dialog');
+
+ // Adjust close button features.
+ $('.overlay .ui-dialog-titlebar-close:not(.overlay-processed)').addClass('overlay-processed')
+ .attr('href', '#')
+ .attr('title', Drupal.t('Close'))
+ .unbind('click')
+ .bind('click', function () { try { self.close(); } catch(e) {}; return false; });
+
+ // Replace the title span element with an h1 element for accessibility.
+ $('.overlay .ui-dialog-title').replaceWith('
' + $('.overlay .ui-dialog-title').html() + '
');
+
+ // Compute initial dialog size.
+ var dialogSize = self.sanitizeSize({width: self.options.width, height: self.options.height});
+
+ // Compute frame size and dialog position based on dialog size.
+ var frameSize = $.extend({}, dialogSize);
+ frameSize.height -= $('.overlay .ui-dialog-titlebar').outerHeight(true);
+ var dialogPosition = self.computePosition($('.overlay'), dialogSize);
+
+ // Adjust size of the iframe element and container.
+ $('.overlay').width(dialogSize.width).height(dialogSize.height);
+ self.iframe.$container.width(frameSize.width).height(frameSize.height);
+ self.iframe.$element.width(frameSize.width).height(frameSize.height);
+
+ // Update the dialog size so that UI internals are aware of the change.
+ self.iframe.$container.dialog('option', { width: dialogSize.width, height: dialogSize.height });
+
+ // Hide the dialog, position it on the viewport and then fade it in with
+ // the frame hidden until the child document is loaded.
+ self.iframe.$element.hide();
+ $('.overlay').hide().css({top: dialogPosition.top, left: dialogPosition.left});
+ $('.overlay').fadeIn('fast', function () {
+ // Load the document on hidden iframe (see bindChild method).
+ self.load(self.options.url);
+ });
+
+ if ($.isFunction(self.options.onOverlayOpen)) {
+ self.options.onOverlayOpen(self);
+ }
+
+ self.isOpen = true;
+ };
+
+ // Before close callback for jQuery UI Dialog.
+ var dialogBeforeClose = function () {
+ if (self.beforeCloseEnabled) {
+ return true;
+ }
+ if (!self.beforeCloseIsBusy) {
+ self.beforeCloseIsBusy = true;
+ setTimeout(function () { self.close(); }, 1);
+ }
+ return false;
+ };
+
+ // Close callback for jQuery UI Dialog.
+ var dialogClose = function () {
+ $(document).unbind('keydown.overlay-event');
+ $('.overlay .ui-dialog-titlebar-close').unbind('keydown.overlay-event');
+ try {
+ self.iframe.$element.remove();
+ self.iframe.$container.dialog('destroy').remove();
+ } catch(e) {};
+ delete self.iframe.documentSize;
+ delete self.iframe.Drupal;
+ delete self.iframe.$element;
+ delete self.iframe.$container;
+ if (self.beforeCloseEnabled) {
+ delete self.beforeCloseEnabled;
+ }
+ if (self.beforeCloseIsBusy) {
+ delete self.beforeCloseIsBusy;
+ }
+ self.isOpen = false;
+ };
+
+ // Default jQuery UI Dialog options.
+ var dialogOptions = {
+ modal: true,
+ autoOpen: false,
+ closeOnEscape: true,
+ resizable: false,
+ title: Drupal.t('Loading...'),
+ dialogClass: 'overlay',
+ zIndex: 500,
+ open: dialogOpen,
+ beforeclose: dialogBeforeClose,
+ close: dialogClose
+ };
+
+ // Allow external script override default jQuery UI Dialog options.
+ $.extend(dialogOptions, self.options.customDialogOptions);
+
+ // Create the jQuery UI Dialog.
+ self.iframe.$container.dialog(dialogOptions);
+};
+
+/**
+ * Load the given URL into the overlay iframe.
+ *
+ * Use this method to change the URL being loaded in the overlay if it is
+ * already open.
+ */
+Drupal.overlay.load = function (url) {
+ var self = this;
+ var iframe = self.iframe.$element.get(0);
+ // Get the document object of the iframe window.
+ // @see http://xkr.us/articles/dom/iframe-document/
+ var doc = (iframe.contentWindow || iframe.contentDocument);
+ if (doc.document) {
+ doc = doc.document;
+ }
+ doc.location.replace(url);
+};
+
+/**
+ * Check if the dialog can be closed.
+ */
+Drupal.overlay.canClose = function () {
+ var self = this;
+ if (!self.isOpen) {
+ return false;
+ }
+ // Allow external scripts decide if the overlay can be closed.
+ if ($.isFunction(self.options.onOverlayCanClose)) {
+ if (!self.options.onOverlayCanClose(self)) {
+ return false;
+ }
+ }
+ return true;
+};
+
+/**
+ * Close the overlay and remove markup related to it from the document.
+ */
+Drupal.overlay.close = function (args, statusMessages) {
+ var self = this;
+
+ // Offer the user a chance to change their mind if there is a form on the
+ // page, which may have unsaved work on it.
+ var iframeElement = self.iframe.$element.get(0);
+ var iframeDocument = (iframeElement.contentWindow || iframeElement.contentDocument);
+ if (iframeDocument.document) {
+ iframeDocument = iframeDocument.document;
+ }
+
+ // Check if the dialog can be closed.
+ if (!self.canClose()) {
+ delete self.beforeCloseIsBusy;
+ return false;
+ }
+
+ // Hide and destroy the dialog.
+ function closeDialog() {
+ // Prevent double execution when close is requested more than once.
+ if (!Drupal.isObject(self.iframe.$container)) {
+ return;
+ }
+ self.beforeCloseEnabled = true;
+ self.iframe.$container.dialog('close');
+ if ($.isFunction(self.options.onOverlayClose)) {
+ self.options.onOverlayClose(args, statusMessages);
+ }
+ }
+ if (!Drupal.isObject(self.iframe.$element) || !self.iframe.$element.size() || !self.iframe.$element.is(':visible')) {
+ closeDialog();
+ }
+ else {
+ self.iframe.$container.animate({height: 'hide'}, { duration: 'fast', 'queue': false });
+ $('.overlay').animate({opacity: 'hide'}, closeDialog);
+ }
+ return true;
+};
+
+/**
+ * Redirect the overlay parent window to the given URL.
+ *
+ * @param link
+ * Can be an absolute URL or a relative link to the domain root.
+ */
+Drupal.overlay.redirect = function (link) {
+ if (link.indexOf('http') != 0 && link.indexOf('https') != 0) {
+ var absolute = location.href.match(/https?:\/\/[^\/]*/)[0];
+ link = absolute + link;
+ }
+ location.href = link;
+ return true;
+}
+
+/**
+ * Bind the child window.
+ *
+ * Add tabs on the overlay, keyboard actions and display animation.
+ */
+Drupal.overlay.bindChild = function (iFrameWindow, isClosing) {
+ var self = this;
+ var $iFrameWindow = iFrameWindow.jQuery;
+ var $iFrameDocument = $iFrameWindow(iFrameWindow.document);
+ var autoResizing = false;
+ self.iframe.Drupal = iFrameWindow.Drupal;
+
+ // We are done if the child window is closing.
+ if (isClosing) {
+ return;
+ }
+
+ // Update the dialog title with the child window title.
+ $('.overlay .ui-dialog-title').html($iFrameDocument.attr('title')).focus();
+ // Add a title attribute to the iframe for accessibility.
+ self.iframe.$element.attr('title', Drupal.t('@title dialog', { '@title': $iFrameDocument.attr('title') }));
+
+ // Move over shortcuts addition button if exists.
+ var addToShortcuts = $('.add-to-shortcuts', $iFrameDocument);
+ if (addToShortcuts.length) {
+ $('.ui-dialog-titlebar .add-to-shortcuts').remove();
+ $('a', addToShortcuts).attr('target', 'overlay-element');
+ $('.overlay .ui-dialog-title').after('
' + addToShortcuts.html() + '
');
+ $('.add-to-shortcuts', $iFrameDocument).remove();
+ }
+
+ // Remove any existing tabs.
+ $('.overlay .ui-dialog-titlebar ul').remove();
+
+ // Setting tabIndex makes the div focusable.
+ $iFrameDocument.attr('tabindex', -1);
+
+ $('.ui-dialog-titlebar-close-bg').animate({opacity: 0.9999}, 'fast');
+
+ // Perform animation to show the iframe element.
+ self.iframe.$element.fadeIn('fast', function () {
+ // @todo: Watch for experience in the way we compute the size of the
+ // iframed document. There are many ways to do it, and none of them
+ // seem to be perfect. Note though, that the size of the iframe itself
+ // may affect the size of the child document, especially on fluid layouts.
+ self.iframe.documentSize = { width: $iFrameDocument.width(), height: $iFrameWindow('body').height() + 25 };
+
+ // Adjust overlay to fit the iframe content?
+ if (self.options.autoFit) {
+ self.resize(self.iframe.documentSize);
+ }
+
+ // Try to enhance keyboard based navigation of the overlay.
+ // Logic inspired by the open() method in ui.dialog.js, and
+ // http://wiki.codetalks.org/wiki/index.php/Docs/Keyboard_navigable_JS_widgets
+
+ // Get a reference to the close button.
+ var $closeButton = $('.overlay .ui-dialog-titlebar-close');
+
+ // Search tabbable elements on the iframed document to speed up related
+ // keyboard events.
+ // @todo: Do we need to provide a method to update these references when
+ // AJAX requests update the DOM on the child document?
+ var $iFrameTabbables = $iFrameWindow(':tabbable:not(form)');
+ var $firstTabbable = $iFrameTabbables.filter(':first');
+ var $lastTabbable = $iFrameTabbables.filter(':last');
+
+ // Unbind keyboard event handlers that may have been enabled previously.
+ $(document).unbind('keydown.overlay-event');
+ $closeButton.unbind('keydown.overlay-event');
+
+ // When the focus leaves the close button, then we want to jump to the
+ // first/last inner tabbable element of the child window.
+ $closeButton.bind('keydown.overlay-event', function (event) {
+ if (event.keyCode && event.keyCode == $.ui.keyCode.TAB) {
+ var $target = (event.shiftKey ? $lastTabbable : $firstTabbable);
+ if (!$target.size()) {
+ $target = $iFrameDocument;
+ }
+ setTimeout(function () { $target.focus(); }, 10);
+ return false;
+ }
+ });
+
+ // When the focus leaves the child window, then drive the focus to the
+ // close button of the dialog.
+ $iFrameDocument.bind('keydown.overlay-event', function (event) {
+ if (event.keyCode) {
+ if (event.keyCode == $.ui.keyCode.TAB) {
+ if (event.shiftKey && event.target == $firstTabbable.get(0)) {
+ setTimeout(function () { $closeButton.focus(); }, 10);
+ return false;
+ }
+ else if (!event.shiftKey && event.target == $lastTabbable.get(0)) {
+ setTimeout(function () { $closeButton.focus(); }, 10);
+ return false;
+ }
+ }
+ else if (event.keyCode == $.ui.keyCode.ESCAPE) {
+ setTimeout(function () { self.close(); }, 10);
+ return false;
+ }
+ }
+ });
+
+ var autoResize = function () {
+ if (typeof self.iframe.$element == 'undefined') {
+ autoResizing = false;
+ $(window).unbind('resize', windowResize);
+ return;
+ }
+ var iframeElement = self.iframe.$element.get(0);
+ var iframeDocument = (iframeElement.contentWindow || iframeElement.contentDocument);
+ if (iframeDocument.document) {
+ iframeDocument = iframeDocument.document;
+ }
+ // Use outerHeight() because otherwise the calculation will be off
+ // because of padding and/or border added by the theme.
+ var height = $(iframeDocument).find('body').outerHeight() + 25;
+ self.iframe.$element.css('height', height);
+ self.iframe.$container.css('height', height);
+ self.iframe.$container.parent().css('height', height + 45);
+ // Don't allow the shadow background to shrink so it's not enough to hide
+ // the whole page. Take the existing document height (with overlay) and
+ // the body height itself for our base calculation.
+ var docHeight = Math.min($(document).find('body').outerHeight(), $(document).height());
+ $('.ui-widget-overlay').height(Math.max(docHeight, $(window).height(), height + 145));
+ setTimeout(autoResize, 150);
+ };
+
+ var windowResize = function () {
+ var width = $(window).width()
+ var change = lastWidth - width;
+ var currentWidth = self.iframe.$element.width();
+ var newWidth = lastFrameWidth - change;
+ lastWidth = width;
+ lastFrameWidth = newWidth;
+
+ if (newWidth >= 300) {
+ self.iframe.$element.css('width', newWidth);
+ self.iframe.$container.css('width', newWidth);
+ self.iframe.$container.parent().css('width', newWidth);
+ widthBelowMin = false;
+ }
+ else {
+ widthBelowMin = true;
+ }
+ }
+
+ if (!autoResizing) {
+ autoResizing = true;
+ autoResize();
+ var lastFrameWidth = self.iframe.$element.width();
+ var lastWidth = $(window).width();
+ $(window).resize(windowResize);
+ }
+
+ // When the focus is captured by the parent document, then try
+ // to drive the focus back to the first tabbable element, or the
+ // close button of the dialog (default).
+ $(document).bind('keydown.overlay-event', function (event) {
+ if (event.keyCode && event.keyCode == $.ui.keyCode.TAB) {
+ setTimeout(function () {
+ if (!$iFrameWindow(':tabbable:not(form):first').focus().size()) {
+ $closeButton.focus();
+ }
+ }, 10);
+ return false;
+ }
+ });
+
+ // If there are tabs in the page, move them to the titlebar.
+ var tabs = $iFrameDocument.find('ul.primary').get(0);
+
+ // This breaks in anything less than IE 7. Prevent it from running.
+ if (typeof tabs != 'undefined' && (!$.browser.msie || parseInt($.browser.version) >= 7)) {
+ $('.ui-dialog-titlebar').append($(tabs).remove().get(0));
+ if ($(tabs).is('.primary')) {
+ $(tabs).find('a').addClass('to-overlay').removeClass('overlay-processed');
+ Drupal.attachBehaviors($(tabs));
+ }
+ // Remove any classes from the list element to avoid theme styles
+ // clashing with our styling.
+ $(tabs).removeAttr('class');
+ }
+ });
+};
+
+/**
+ * Unbind the child window.
+ *
+ * Remove keyboard event handlers, reset title and hide the iframe.
+ */
+Drupal.overlay.unbindChild = function (iFrameWindow) {
+ var self = this;
+
+ // Prevent memory leaks by explicitly unbinding keyboard event handler
+ // on the child document.
+ iFrameWindow.jQuery(iFrameWindow.document).unbind('keydown.overlay-event');
+
+ // Change the overlay title.
+ $('.overlay .ui-dialog-title').html(Drupal.t('Please, wait...'));
+
+ // Hide the iframe element.
+ self.iframe.$element.fadeOut('fast');
+};
+
+/**
+ * Check if the given link is an admin link and should be opened in the overlay.
+ *
+ * Modules and themes can override the default behavior by adding an array of
+ * links and/or regular expressions to
+ * Drupal.settings.overlay.admin[modulename], for links that should be displayed
+ * inside the overlay, or Drupal.settings.overlay.nonAdmin[modulename], for
+ * links that should be displayed as normal in the parent window.
+ */
+Drupal.overlay.isAdminLink = function (url) {
+ // Create a native Link object, so we can use its object methods.
+ var link = $(url.link(url)).get(0);
+ var path = link.pathname.replace(new RegExp(Drupal.settings.basePath), '');
+ if (path == '') {
+ // If path became empty, it might mean the path is represented in the query
+ // string (clean URLs are not used), so we should look that up.
+ var match = new RegExp("(\\?|&)q=(.+)(&|$)").exec(link.search);
+ if (match && match.length == 4) {
+ path = match[2];
+ }
+ }
+ // Test the link against module/theme-provided non-admin links.
+ for (module in Drupal.settings.overlay.links.nonAdmin) {
+ var list = Drupal.settings.overlay.links.nonAdmin[module];
+ if ((typeof module == 'string') && list.length) {
+ var i;
+ for (i = 0; i < list.length; i++) {
+ var item = list[i];
+ switch (typeof item) {
+ case 'string':
+ if (path.indexOf(item) === 0) {
+ return false;
+ }
+ default:
+ if (path.match(item)) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ if (path.indexOf('admin') === 0) {
+ return true;
+ }
+ if (path.indexOf('node/add') === 0) {
+ return true;
+ }
+ var re = new RegExp("node/[0-9]+/(edit|delete)");
+ if (path.match(re)) {
+ return true;
+ }
+ // Test the link against module/theme-provided admin links.
+ for (module in Drupal.settings.overlay.links.admin) {
+ list = Drupal.settings.overlay.links.admin[module];
+ if ((typeof module == 'string') && list.length) {
+ i;
+ for (i = 0; i < list.length; i++) {
+ item = list[i];
+ switch (typeof item) {
+ case 'string':
+ if (path.indexOf(item) === 0) {
+ return true;
+ }
+ default:
+ if (path.match(item)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * Sanitize dialog size.
+ *
+ * Do not let the overlay go over the 0.78x of the width of the screen and set
+ * minimal height. The height is not limited due to how we rely on the parent
+ * window to provide scrolling instead of scrolling in scrolling with the
+ * overlay.
+ *
+ * @param size
+ * Contains 'width' and 'height' items as numbers.
+ * @return
+ * The same structure with sanitized number values.
+ */
+Drupal.overlay.sanitizeSize = function (size) {
+ var width, height;
+ var $window = $(window);
+
+ // Use 300px as the minimum width but at most expand to 78% of the window.
+ // Ensures that users see that there is an actual website in the background.
+ var minWidth = 300, maxWidth = parseInt($window.width() * .78);
+ if (typeof size.width != 'number') {
+ width = maxWidth;
+ }
+ // Set to at least minWidth but at most maxWidth.
+ else if (size.width < minWidth || size.width > maxWidth) {
+ width = Math.min(maxWidth, Math.max(minWidth, size.width));
+ }
+ else {
+ width = size.width;
+ }
+
+ // Use 100px as the minimum height. Expand to 92% of the window if height
+ // was invalid, to ensure that we have a reasonable chance to show content.
+ var minHeight = 100, maxHeight = parseInt($window.height() * .92);
+ if (typeof size.height != 'number') {
+ height = maxHeight;
+ }
+ else if (size.height < minHeight) {
+ // Do not consider maxHeight as the actual maximum height, since we rely on
+ // the parent window scroll bar to scroll the window. Only set up to be at
+ // least the minimal height.
+ height = Math.max(minHeight, size.height);
+ }
+ else {
+ height = size.height;
+ }
+ return { width: width, height: height };
+};
+
+/**
+ * Compute position to center horizontally and on viewport top vertically.
+ */
+Drupal.overlay.computePosition = function ($element, elementSize) {
+ var $window = $(window);
+ // Consider any region that should be visible above the overlay (such as
+ // an admin toolbar).
+ var $toolbar = $('.overlay-displace-top');
+ var toolbarHeight = 0;
+ $toolbar.each(function () {
+ toolbarHeight += $toolbar.height();
+ });
+ var position = {
+ left: Math.max(0, parseInt(($window.width() - elementSize.width) / 2)),
+ top: toolbarHeight + 20
+ };
+
+ // Reset the scroll to the top of the window so that the overlay is visible again.
+ window.scrollTo(0, 0);
+ return position;
+};
+
+/**
+ * Resize overlay to the given size.
+ *
+ * @param size
+ * Contains 'width' and 'height' items as numbers.
+ */
+Drupal.overlay.resize = function (size) {
+ var self = this;
+
+ // Compute frame and dialog size based on requested document size.
+ var titleBarHeight = $('.overlay .ui-dialog-titlebar').outerHeight(true);
+ var frameSize = self.sanitizeSize(size);
+ var dialogSize = $.extend({}, frameSize);
+ dialogSize.height += titleBarHeight + 15;
+
+ // Compute position on viewport.
+ var dialogPosition = self.computePosition($('.overlay'), dialogSize);
+
+ var animationOptions = $.extend(dialogSize, dialogPosition);
+
+ // Perform the resize animation.
+ $('.overlay').animate(animationOptions, 'fast', function () {
+ // Proceed only if the dialog still exists.
+ if (Drupal.isObject(self.iframe.$element) && Drupal.isObject(self.iframe.$container)) {
+ // Resize the iframe element and container.
+ $('.overlay').width(dialogSize.width).height(dialogSize.height);
+ self.iframe.$container.width(frameSize.width).height(frameSize.height);
+ self.iframe.$element.width(frameSize.width).height(frameSize.height);
+
+ // Update the dialog size so that UI internals are aware of the change.
+ self.iframe.$container.dialog('option', { width: dialogSize.width, height: dialogSize.height });
+
+ // Keep the dim background grow or shrink with the dialog.
+ $('.ui-widget-overlay').height($(document).height());
+
+ // Animate body opacity, so we fade in the page as it loads in.
+ $(self.iframe.$element.get(0)).contents().find('body.overlay').animate({opacity: 0.9999}, 'slow');
+ }
+ });
+};
+
+/**
+ * Add overlay rendering GET parameter to the given href.
+ */
+Drupal.overlay.addOverlayParam = function (href) {
+ // Do not process links with an empty href, or that only have the fragment or
+ // which are external links.
+ if (href.length > 0 && href.charAt(0) != '#' && href.indexOf('http') != 0 && href.indexOf('https') != 0) {
+ var fragmentIndex = href.indexOf('#');
+ var fragment = '';
+ if (fragmentIndex != -1) {
+ fragment = href.substr(fragmentIndex);
+ href = href.substr(0, fragmentIndex);
+ }
+ href += (href.indexOf('?') > -1 ? '&' : '?') + 'render=overlay' + fragment;
+ }
+ return href;
+};
+
+})(jQuery);
diff --git modules/overlay/overlay.api.php modules/overlay/overlay.api.php
new file mode 100644
index 0000000..e4b6868
--- /dev/null
+++ modules/overlay/overlay.api.php
@@ -0,0 +1,52 @@
+ array('links' => array('admin' => NULL, 'nonAdmin' => NULL))), 'setting');
+ if (isset($_GET['render']) && $_GET['render'] == 'overlay') {
+ $admin_theme = variable_get('admin_theme', 0);
+ if ($custom_theme != $admin_theme) {
+ // If system module did not switch the theme yet (i.e. this is not an
+ // admin page, per se), we should switch the theme here.
+ $custom_theme = $admin_theme;
+ drupal_add_css(drupal_get_path('module', 'system') . '/admin.css');
+ }
+ overlay_mode('child');
+ }
+ else {
+ // Otherwise add overlay parent code and our behavior.
+ overlay_mode('parent');
+ // Special case for when overlay.module was just enabled; redirect to
+ // the homepage and reopen the modules page inside the overlay.
+ if (isset($_SESSION['overlay_just_enabled'])) {
+ if ($_SESSION['overlay_just_enabled'] == 'step1' && $_GET['q'] == 'admin/config/modules') {
+ $_SESSION['overlay_just_enabled'] = 'step2';
+ drupal_goto('');
+ }
+ else if ($_SESSION['overlay_just_enabled'] == 'step2' && $_GET['q'] == variable_get('site_frontpage','node')) {
+ unset($_SESSION['overlay_just_enabled']);
+ drupal_add_js(array('overlay' => array('autoOpen' => url('admin/config/modules'))), 'setting');
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implement hook_element_info_alter().
+ */
+function overlay_element_info_alter(&$types) {
+ foreach (array('submit', 'button', 'image_button', 'form') as $type) {
+ $types[$type]['#after_build'][] = 'overlay_form_after_build';
+ }
+}
+
+/**
+ * Implement hook_library().
+ */
+function overlay_library() {
+ $module_path = drupal_get_path('module', 'overlay');
+
+ // Overlay parent.
+ $libraries['parent'] = array(
+ 'title' => 'Overlay: Parent',
+ 'website' => 'http://drupal.org/node/517688',
+ 'version' => '1.0',
+ 'js' => array(
+ $module_path . '/overlay-parent.js' => array(),
+ ),
+ 'css' => array(
+ $module_path . '/overlay-parent.css' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui.dialog'),
+ ),
+ );
+ // Overlay child.
+ $libraries['child'] = array(
+ 'title' => 'Overlay: Child',
+ 'website' => 'http://drupal.org/node/517688',
+ 'version' => '1.0',
+ 'js' => array(
+ $module_path . '/overlay-child.js' => array(),
+ ),
+ 'dependencies' => array(
+ array('system', 'ui'),
+ ),
+ );
+
+ return $libraries;
+}
+
+/**
+ * Implement hook_drupal_goto_alter().
+ *
+ * If the current page request is inside the overlay, add ?render=overlay to
+ * the new path, so that it appears correctly inside the overlay.
+ *
+ * @see overlay_mode()
+ */
+function overlay_drupal_goto_alter(&$path, &$options, &$http_response_code) {
+ if (overlay_mode() == 'child') {
+ if (isset($options['query'])) {
+ $options['query'] += array('render' => 'overlay');
+ }
+ else {
+ $options['query'] = array('render' => 'overlay');
+ }
+ }
+}
+
+/**
+ * Implement hook_batch_alter().
+ *
+ * If the current page request is inside the overlay, add ?render=overlay to
+ * the success callback URL, so that it appears correctly within the overlay.
+ *
+ * @see overlay_mode()
+ */
+function overlay_batch_alter(&$batch) {
+ if (overlay_mode() == 'child') {
+ if (isset($batch['url_options']['query'])) {
+ $batch['url_options']['query']['render'] = 'overlay';
+ }
+ else {
+ $batch['url_options']['query'] = array('render' => 'overlay');
+ }
+ }
+}
+
+/**
+ * Implement hook_block_info_alter().
+ *
+ * If the current page request is inside the overlay, don't display extraneous
+ * blocks.
+ *
+ * @see overlay_mode()
+ */
+function overlay_block_info_alter(&$blocks) {
+ // @todo: custom_theme does not exist anymore.
+ global $custom_theme;
+
+ if (!empty($custom_theme) && 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.
+ *
+ * If the current page request is inside the overlay, add appropriate classes
+ * to the element, and simplify the page title.
+ *
+ * @see overlay_mode()
+ */
+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.
+ *
+ * Display breadcrumbs correctly inside the overlay.
+ *
+ * @see overlay_mode()
+ */
+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', array('breadcrumb' => $overlay_breadcrumb));
+ }
+}
+
+/**
+ * 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
+ * the list of submit handlers and add our own at the end. The added handler
+ * determines whether or not the user is redirected done at the end of form
+ * processing, so that it's possible to close the overlay after submitting
+ * a form.
+ *
+ * @see _form_builder_handle_input_element()
+ * @see _form_builder_ie_cleanup()
+ * @see form_execute_handlers()
+ * @see form_builder()
+ * @see overlay_form_submit()
+ *
+ * @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 to set
+ // $form_state['redirect'] 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.
+ *
+ * @see overlay_mode()
+ * @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 saved
+ // or previewed (but not when delete has been requested) in child mode.
+ // Wait until $form_state['redirect'] has a value (which is not until the
+ // submit button handler has been called), so that we have somewhere
+ // to redirect to.
+ if ($args === FALSE && overlay_mode() == 'child' && !empty($form['#node_edit_form']) && isset($form_state['redirect']) && $form_state['clicked_button']['#value'] !== t('Delete')) {
+ $args = overlay_close_dialog(TRUE);
+ }
+
+ // Make sure the overlay is closed when the node delete form has been
+ // confirmed.
+ if ($args === FALSE && overlay_mode() == 'child' && $form['#form_id'] === 'node_delete_confirm') {
+ $args = overlay_close_dialog(TRUE);
+ }
+
+ // Close the overlay if the overlay module has been disabled
+ if (!module_exists('overlay')) {
+ $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'])) {
+ if (is_array($form_state['redirect'])) {
+ $settings['overlayChild']['redirect'] = url($form_state['redirect'][0], $form_state['redirect'][1]);
+ }
+ else {
+ $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 window (a regular browser window), and JavaScript
+ * is added so that administrative links in the parent window will open in
+ * an overlay. 'child' is used in the context of the child overlay window (the
+ * page actually appearing within the overlay iframe) and JavaScript and CSS
+ * are 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;
+
+ switch ($overlay_mode) {
+ case 'parent':
+ drupal_add_library('overlay', 'parent');
+
+ // Allow modules to act upon overlay events.
+ module_invoke_all('overlay_parent_initialize');
+ break;
+
+ case 'child':
+ drupal_add_library('overlay', 'child');
+
+ // 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 --git modules/shortcut/shortcut.admin.inc modules/shortcut/shortcut.admin.inc
index bb894c0..dcbbdd4 100644
--- modules/shortcut/shortcut.admin.inc
+++ modules/shortcut/shortcut.admin.inc
@@ -519,19 +519,25 @@ function shortcut_link_delete_submit($form, &$form_state) {
* Returned from shortcut_set_load().
*/
function shortcut_link_add_inline($shortcut_set) {
- if (isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'shortcut-add-link') && shortcut_valid_link($_GET['link'])) {
- $link = array(
- 'link_title' => $_GET['name'],
- 'link_path' => $_GET['link'],
- );
- shortcut_admin_add_link($link, $shortcut_set, shortcut_max_slots());
- if (shortcut_set_save($shortcut_set)) {
- drupal_set_message(t('Added a shortcut for %title.', array('%title' => $link['link_title'])));
+ if (isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'shortcut-add-link')) {
+ $link_path = preg_replace('/\?.+/', '', $_GET['link']);
+ if (shortcut_valid_link($link_path)) {
+ $link = array(
+ 'link_title' => $_GET['name'],
+ 'link_path' => $link_path,
+ );
+ shortcut_admin_add_link($link, $shortcut_set, shortcut_max_slots());
+ if (shortcut_set_save($shortcut_set)) {
+ drupal_set_message(t('Added a shortcut for %title.', array('%title' => $link['link_title'])));
+ }
+ else {
+ drupal_set_message(t('Unable to add a shortcut for %title.', array('%title' => $link['link_title'])));
+ }
+ drupal_goto();
}
else {
drupal_set_message(t('Unable to add a shortcut for %title.', array('%title' => $link['link_title'])));
}
- drupal_goto();
}
return drupal_access_denied();
}
diff --git modules/toolbar/toolbar.css modules/toolbar/toolbar.css
index 7aa097d..7d04671 100644
--- modules/toolbar/toolbar.css
+++ modules/toolbar/toolbar.css
@@ -36,7 +36,7 @@ div#toolbar {
left: 0;
right: 0;
top: 0;
- z-index: 100;
+ z-index: 600;
}
div#toolbar .collapsed {
diff --git modules/toolbar/toolbar.js modules/toolbar/toolbar.js
index 49cf158..7feab2c 100644
--- modules/toolbar/toolbar.js
+++ modules/toolbar/toolbar.js
@@ -15,6 +15,16 @@ Drupal.behaviors.admin = {
Drupal.admin.toolbar.toggle();
return false;
});
+
+ // Set the most recently clicked item as active.
+ $('#toolbar a').once().click(function() {
+ $('#toolbar a').each(function() {
+ $(this).removeClass('active');
+ });
+ if ($(this).parents('div.toolbar-shortcuts').length) {
+ $(this).addClass('active');
+ }
+ });
}
};
diff --git modules/toolbar/toolbar.module modules/toolbar/toolbar.module
index 0f1ba5b..26f7b4b 100644
--- modules/toolbar/toolbar.module
+++ modules/toolbar/toolbar.module
@@ -36,13 +36,30 @@ function toolbar_theme($existing, $type, $theme, $path) {
* Add admin toolbar to the page_top region automatically.
*/
function toolbar_page_build(&$page) {
- if (user_access('access toolbar')) {
+ if (user_access('access toolbar') && toolbar_enabled()) {
$page['page_top']['toolbar'] = toolbar_build();
$page['page_top']['toolbar']['toolbar_drawer'] = isset($page['toolbar_drawer']) ? $page['toolbar_drawer'] : array();
}
}
/**
+ * Enable or disable the toolbar, or find out the current status of the toolbar.
+ *
+ * @param $enabled
+ * If given, will change the display mode of the toolbar to the given
+ * boolean.
+ * @return
+ * The current status of the toolbar, TRUE for enabled, FALSE for disabled.
+ */
+function toolbar_enabled($enabled = NULL) {
+ $setting = &drupal_static(__FUNCTION__, TRUE);
+ if (isset($enabled)) {
+ $setting = $enabled;
+ }
+ return $setting;
+}
+
+/**
* Implement hook_preprocess_html().
*
* Add some page classes, so global page theming can adjust to the toolbar.
@@ -53,6 +70,10 @@ function toolbar_preprocess_html(&$vars) {
}
}
+function template_preprocess_toolbar(&$variables) {
+ $variables['classes_array'][] = 'clearfix';
+}
+
/**
* Build the admin menu as a structured array ready for drupal_render().
*/
@@ -180,3 +201,13 @@ function toolbar_in_active_trail($path) {
}
return in_array($path, $active_paths);
}
+
+/**
+ * Implement hook_overlay_child_initialize().
+ */
+function toolbar_overlay_child_initialize() {
+ // Disable admin toolbar, which is something child windows don't need and
+ // shouldn't have.
+ toolbar_enabled(FALSE);
+}
+
diff --git modules/toolbar/toolbar.tpl.php modules/toolbar/toolbar.tpl.php
index 00bd560..8fdff29 100644
--- modules/toolbar/toolbar.tpl.php
+++ modules/toolbar/toolbar.tpl.php
@@ -6,15 +6,17 @@
* Default template for admin toolbar.
*
* Available variables:
+ * - $classes: Classes for the toolbar div element. Can be
+ * manipulated in preprocess functions via $variables['classes_array'].
* - $toolbar['toolbar_user']: User account / logout links.
* - $toolbar['toolbar_menu']: Top level management menu links.
* - $toolbar['toolbar_drawer']: A place for extended toolbar content.
*
* @see template_preprocess()
- * @see template_preprocess_admin_toolbar()
+ * @see template_preprocess_toolbar()
*/
?>
-