Index: modules/system/system.css =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.css,v retrieving revision 1.53 diff -u -r1.53 system.css --- modules/system/system.css 5 Dec 2008 12:50:28 -0000 1.53 +++ modules/system/system.css 15 Feb 2009 06:03:58 -0000 @@ -559,3 +559,55 @@ div.password-confirm { visibility: hidden; } + +/* +** Ajax popup dialog box styles +*/ +#popup-overlay { + position: absolute; + background: black; + z-index: 9; + top: 0; +} +#popup-loading { + z-index: 10; + opacity: 0.75; + position: relative; +} +#popup-loading div { + position: absolute; +} +#popup { + border: 1px solid black; + background: white; + position: absolute; + z-index: 10; + padding: 0.5em; + width: 600px; + overflow: auto; +} +#popup-title { + font-weight: bold; + margin-bottom: 0.25em; +} +#popup-title div.title { + float: left; +} +#popup-title #popup-close { + float: right; +} +#popup-title #popup-close a { + font-weight: normal; +} +/* Allow messages to be used as the title of the popup */ +#popup div.messages { + background: transparent; + border: none; + padding: 0; + margin: 0; +} +a.popup-processed { + padding-right: 12px; + background: url(../../misc/popup.png) no-repeat right; +} + Index: modules/system/system.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v retrieving revision 1.125 diff -u -r1.125 system.admin.inc --- modules/system/system.admin.inc 11 Feb 2009 05:33:18 -0000 1.125 +++ modules/system/system.admin.inc 15 Feb 2009 06:03:58 -0000 @@ -1287,6 +1287,20 @@ return system_settings_form($form); } +function system_popup_settings() { + $form['popups_toggle'] = array ( + '#type' => 'radios', + '#title' => t('Popups'), + '#default_value' => 1, + '#options' => array ( + 0 => t('Off'), + 1 => t('On'), + ), + '#description' => t('Popups can show help pages and confirmation dialogs.'), + ); + return system_settings_form($form); +} + /** * Form builder; Configure site performance settings. * @@ -1771,6 +1785,7 @@ * If true, only returns a boolean whether there are system status errors. */ function system_status($check = FALSE) { + drupal_add_popup(); // Load .install files include_once DRUPAL_ROOT . '/includes/install.inc'; drupal_load_updates(); Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.667 diff -u -r1.667 system.module --- modules/system/system.module 11 Feb 2009 05:33:18 -0000 1.667 +++ modules/system/system.module 15 Feb 2009 06:04:01 -0000 @@ -169,6 +169,9 @@ 'arguments' => array('version' => NULL), ), 'system_compact_link' => array(), + 'popup' => array( + 'template' => 'popup', + ), )); } @@ -649,6 +652,14 @@ 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -1, ); + $items['admin/settings/logging/popups'] = array( + 'title' => 'Popup Behavior', + 'description' => 'Settings for the popup dialog behavior.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('system_popup_settings'), + 'access arguments' => array('administer site configuration'), + 'type' => MENU_LOCAL_TASK, + ); $items['admin/settings/performance'] = array( 'title' => 'Performance', 'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.', @@ -711,7 +722,7 @@ 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); - + // Reports: $items['admin/reports'] = array( 'title' => 'Reports', @@ -1099,6 +1110,7 @@ 'slogan', 'main_menu', 'secondary_menu', +// 'popups', ), 'stylesheets' => array( 'all' => array('style.css') @@ -1459,7 +1471,7 @@ $fragment = isset($path['fragment']) ? $path['fragment'] : NULL; $path = isset($path['path']) ? $path['path'] : NULL; } - $cancel = l($no ? $no : t('Cancel'), $path, array('query' => $query, 'fragment' => $fragment)); + $cancel = l($no ? $no : t('Cancel'), $path, array('query' => $query, 'fragment' => $fragment, 'attributes' => array('class' => 'popup-close'))); drupal_set_title($question, PASS_THROUGH); Index: modules/path/path.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/path/path.admin.inc,v retrieving revision 1.17 diff -u -r1.17 path.admin.inc --- modules/path/path.admin.inc 13 Jan 2009 19:27:21 -0000 1.17 +++ modules/path/path.admin.inc 15 Feb 2009 06:03:56 -0000 @@ -12,6 +12,7 @@ * and return the list of matching URL aliases. */ function path_admin_overview($keys = NULL) { + drupal_add_popup(); // Add the filter form above the overview table. $output = drupal_get_form('path_admin_filter_form', $keys); // Enable language column if locale is enabled or if we have any alias with language @@ -50,7 +51,7 @@ l($data->dst, $data->src), l($data->src, $data->src, array('alias' => TRUE)), l(t('edit'), "admin/build/path/edit/$data->pid", array('query' => $destination)), - l(t('delete'), "admin/build/path/delete/$data->pid", array('query' => $destination)), + l(t('delete'), "admin/build/path/delete/$data->pid", array('query' => $destination, 'attributes' => array('class' => 'popup-confirm'))), ), ); if ($multilanguage) { Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.867 diff -u -r1.867 common.inc --- includes/common.inc 13 Feb 2009 04:43:00 -0000 1.867 +++ includes/common.inc 15 Feb 2009 06:03:51 -0000 @@ -2740,6 +2740,19 @@ drupal_add_js($settings, 'setting'); } + /** + * Attach the popup behavior to the page. + */ +function drupal_add_popup() { + static $added = FALSE; + if (!$added & variable_get('popups_toggle', 1)) { + drupal_add_js('misc/popup.js'); + $settings = array('popup' => array('template' => theme('popup'))); + drupal_add_js($settings, 'setting'); + $added = TRUE; + } +} + /** * Aggregate JS files, putting them in the files directory. * Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.469 diff -u -r1.469 theme.inc --- includes/theme.inc 5 Feb 2009 03:42:56 -0000 1.469 +++ includes/theme.inc 15 Feb 2009 06:03:52 -0000 @@ -1593,7 +1593,8 @@ * Returns code that emits the 'more help'-link. */ function theme_more_help_link($url) { - return ''; + drupal_add_popup(); + return ''; } /** Index: modules/block/block.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.admin.inc,v retrieving revision 1.35 diff -u -r1.35 block.admin.inc --- modules/block/block.admin.inc 11 Feb 2009 03:38:46 -0000 1.35 +++ modules/block/block.admin.inc 15 Feb 2009 06:03:53 -0000 @@ -27,9 +27,10 @@ */ function block_admin_display_form(&$form_state, $blocks, $theme = NULL) { global $theme_key, $custom_theme; - + drupal_add_css(drupal_get_path('module', 'block') . '/block.css', array('preprocess' => FALSE)); - + drupal_add_popup(); + // If non-default theme configuration has been selected, set the custom theme. $custom_theme = isset($theme) ? $theme : variable_get('theme_default', 'garland'); init_theme(); @@ -81,7 +82,8 @@ if ($block['module'] == 'block') { $form[$key]['delete'] = array( '#markup' => l(t('delete'), - 'admin/build/block/delete/' . $block['delta']), + 'admin/build/block/delete/' . $block['delta'], + array('attributes' => array('class' => 'popup-confirm'))), ); } } Index: modules/filter/filter.module =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v retrieving revision 1.240 diff -u -r1.240 filter.module --- modules/filter/filter.module 21 Jan 2009 16:58:42 -0000 1.240 +++ modules/filter/filter.module 15 Feb 2009 06:03:54 -0000 @@ -586,7 +586,8 @@ * @ingroup themeable */ function theme_filter_tips_more_info() { - return '

' . l(t('More information about formatting options'), 'filter/tips') . '

'; + drupal_add_popup(); + return '

' . l(t('More information about formatting options'), 'filter/tips', array('attributes' => array('class' => 'popup'))) . '

'; } /** Index: modules/filter/filter.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.admin.inc,v retrieving revision 1.24 diff -u -r1.24 filter.admin.inc --- modules/filter/filter.admin.inc 5 Feb 2009 19:52:02 -0000 1.24 +++ modules/filter/filter.admin.inc 15 Feb 2009 06:03:53 -0000 @@ -14,7 +14,8 @@ * @see filter_admin_overview_submit() */ function filter_admin_overview() { - + drupal_add_popup(); + // Overview of all formats. $formats = filter_formats(); $error = FALSE; @@ -33,7 +34,7 @@ $form[$id]['name'] = array('#markup' => $format->name); $form[$id]['roles'] = array('#markup' => $default ? t('All roles may use the default format') : ($roles ? implode(', ', $roles) : t('No roles may use this format'))); $form[$id]['configure'] = array('#markup' => l(t('configure'), 'admin/settings/filter/' . $id)); - $form[$id]['delete'] = array('#markup' => $default ? '' : l(t('delete'), 'admin/settings/filter/delete/' . $id)); + $form[$id]['delete'] = array('#markup' => $default ? '' : l(t('delete'), 'admin/settings/filter/delete/' . $id, array('attributes' => array('class' => 'popup-confirm')))); $form[$id]['weight'] = array('#type' => 'weight', '#default_value' => $format->weight); } $form['default'] = array('#type' => 'radios', '#options' => $options, '#default_value' => variable_get('filter_default_format', 1)); Index: modules/menu/menu.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/menu/menu.admin.inc,v retrieving revision 1.38 diff -u -r1.38 menu.admin.inc --- modules/menu/menu.admin.inc 3 Feb 2009 18:55:30 -0000 1.38 +++ modules/menu/menu.admin.inc 15 Feb 2009 06:03:55 -0000 @@ -60,6 +60,7 @@ * Recursive helper function for menu_overview_form(). */ function _menu_overview_tree_form($tree) { + drupal_add_popup(); static $form = array('#tree' => TRUE); foreach ($tree as $data) { $title = ''; @@ -97,7 +98,7 @@ $operations['edit'] = l(t('edit'), 'admin/build/menu/item/' . $item['mlid'] . '/edit'); // Only items created by the menu module can be deleted. if ($item['module'] == 'menu' || $item['updated'] == 1) { - $operations['delete'] = l(t('delete'), 'admin/build/menu/item/' . $item['mlid'] . '/delete'); + $operations['delete'] = l(t('delete'), 'admin/build/menu/item/' . $item['mlid'] . '/delete', array('attributes' => array('class' => 'popup-confirm'))); } // Set the reset column. elseif ($item['module'] == 'system' && $item['customized']) { Index: modules/node/content_types.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/node/content_types.inc,v retrieving revision 1.64 diff -u -r1.64 content_types.inc --- modules/node/content_types.inc 26 Jan 2009 14:08:43 -0000 1.64 +++ modules/node/content_types.inc 15 Feb 2009 06:03:55 -0000 @@ -10,6 +10,7 @@ * Displays the content type admin overview page. */ function node_overview_types() { + drupal_add_popup(); $types = node_get_types(); $names = node_get_types('names'); $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => '2')); @@ -25,7 +26,7 @@ // Set the delete column. if ($type->custom) { - $row[] = array('data' => l(t('delete'), 'admin/build/node-type/' . $type_url_str . '/delete')); + $row[] = array('data' => l(t('delete'), 'admin/build/node-type/' . $type_url_str . '/delete', array('attributes' => array('class' => 'popup-confirm')))); } else { $row[] = array('data' => ''); Index: modules/system/popup.tpl.php =================================================================== RCS file: modules/system/popup.tpl.php diff -N modules/system/popup.tpl.php --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/system/popup.tpl.php 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,16 @@ + + Index: misc/popup.js =================================================================== RCS file: misc/popup.js diff -N misc/popup.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ misc/popup.js 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,310 @@ +// $Id$ + +/** + * Popup Modal Dialog API + * + * Provide an API for building and displaying JavaScript, in-page, popup modal dialogs (lightbox style). + * Modality is provided by a fixed, semi-opaque div, positioned in front of the page contents. + * + * Popups xhtml is themeable, but the outter most element must have id="popup". + */ + +/** + * Create the popup object/namespace. + */ +Drupal.popup = function() {}; +Drupal.popup.contentSelector = '#content-content'; // The theme must wrap it's page content. +Drupal.popup.titleSelector = '#page-title'; // The theme must wrap it's page title. + +/** + * Attach the popup behavior to the all the requested links on the page. + * + * @param context + * The jQuery object to apply the behaviors to. + */ +Drupal.behaviors.popup = { + attach: function(context) { + // Add keybinding to close popup. + var $body = $('body'); + if(!$body.hasClass('popup-processed')) { + $body.addClass('popup-processed'); + $(document).bind('keydown', Drupal.popup.keyHandle); + + // If we are on the status report page, run the selector test and report the result. + status = 'admin/reports/status'; + current = window.location.href; + if (current.substring(current.length - status.length) === status) { + Drupal.popup.testContentSelector(); + } + } + + Drupal.popup.attach(context, '.popup', {noUpdate: true}); + // Only attach to confirmation if content selector is correct for theme. + if ($(Drupal.popup.contentSelector).length === 1) { + Drupal.popup.attach(context, '.popup-confirm', {reloadWhenDone: true}); // whole page reload. + } + } +}; + +/** + * Attach the popup behavior to a particular link. + * + * @param selector + * jQuery selector for links to attach popup behavior to. + * @param options + * Hash of options associated with these links. + */ +Drupal.popup.attach = function(context, selector, options) { + $(selector, context).not('.popup-processed').each(function() { + var $element = $(this); + // Tag the element as attached, so it is not processed again. + $element.addClass('popup-processed'); + + // Append note to link title (will show on hover). + var title = $element.attr('title') || ''; + $element.attr('title', title + Drupal.t('[Popup]')); + + // Attach the on-click popup behavior to the element. + $element.click(function(e){ + var element = this; + + // If element is inside of a #popup div, show alert and bail out. + if ($(element).parents('#popup').length) { + alert("Sorry, popup-in-popup is not supported (yet)."); + return false; + } + + return Drupal.popup.openPath(element, options); + }); + }); +}; + + +/** + * Generic dialog builder. + * Add a popup to the page. + */ +Drupal.popup.showPopup = function(title, body, buttons, width) { + Drupal.popup.addOverlay(); + var $popup = $(Drupal.theme('popupDialog', title, body, buttons)); + // Start with dialog off the side. Hiding it with display:none causes flash in FF2. + $popup.css('left', '-9999px'); + if (width) { + $popup.css('width', width); + } + $('body').append($popup); // Add the popup to the DOM. + + // Adding button functions + if (buttons) { + jQuery.each(buttons, function (id, button) { + $('#'+id).click(button.func); + }); + } + $('#popup-close').click(Drupal.popup.close); + $('.popup-close').click(function(){Drupal.popup.close(); return false;}); + + // center on the screen, adding in offsets if the window has been scrolled + var popupWidth = $popup.width(); + var windowWidth = $(window).width(); + var left = (windowWidth / 2) - (popupWidth / 2) + Drupal.popup.scrollLeft(); + + // Get popup's height on the page. + // Need to do it this way, since we get visible flash in FF2 if popup is not visible! + var popupHeight = $popup.height(); + var windowHeight = $(window).height(); + if (popupHeight > (0.9 * windowHeight) ) { // Must fit in 90% of window. + popupHeight = 0.9 * windowHeight; + $popup.height(popupHeight); + } + var top = (windowHeight / 2) - (popupHeight / 2) + Drupal.popup.scrollTop(); + + $popup.css('top', top).css('left', left); // Position the popup to be visible. + + this.refocus(); // TODO: capture the focus when it leaves the dialog. + Drupal.popup.removeLoading(); // Remove the loading img. + + return false; +}; + +Drupal.popup.removePopup = function() { + $('#popup').remove(); +}; + +/** + * Use Ajax to open the URL inside a popup window. + * + * @param element + * Element that was clicked to open the popup. + * @param options + * Hash of options controlling how the popup interacts with the underlying page. + */ +Drupal.popup.openPath = function(element, options) { + // let the user know something is happening + $('body').css("cursor", "wait"); + + Drupal.popup.addOverlay(); + Drupal.popup.addLoading(); + + var href = options.href ? options.href : element.href; + $(document).trigger('popup_open_path', [element, href]); // Broadcast Popup Open Path event. + + ajaxOptions = { + url: href, + success: function(page) { + $title = $(Drupal.popup.titleSelector, page); + $content = $(Drupal.popup.contentSelector, page); + Drupal.popup.showPopup($title.html(), $content.html(), null, options.width); + }, + complete: function() { + $('body').css("cursor", "auto"); // Return the cursor to normal state. + }, + error: function() { + Drupal.popup.message("Unable to open: " + href); + } + }; + $.ajax(ajaxOptions); + + return false; +}; + +/** + * Simple popup that functions like the browser's alert box. + */ +Drupal.popup.message = function(title, message) { + message = message || ''; + var buttons = { + 'popup_ok': {title: Drupal.t('OK'), func: Drupal.popup.close} + }; + Drupal.popup.showPopup(title, message, buttons); +}; + +/** + * Handle any special keys when popup is active. + */ +Drupal.popup.keyHandle = function(e) { + if (!e) { + e = window.event; + } + if (e.keyCode === 27) { // esc + Drupal.popup.close(); + } +}; + +Drupal.popup.addOverlay = function() { + var $overlay = $('#popup-overlay'); + if ($overlay.length === 0) { // Overlay does not already exist, so create it. + $overlay = $(Drupal.theme('popupOverlay')); + $overlay.css('opacity', '0.4'); // for ie6(?) + // Doing absolute positioning, so make overlay's size equal the entire body. + $doc = $(document); + $overlay.width($doc.width()).height($doc.height()); + $overlay.click(Drupal.popup.close); + $('body').prepend($overlay); + } +}; + +Drupal.popup.removeOverlay = function() { + $('#popup-overlay').remove(); +}; + +Drupal.popup.addLoading = function() { + var $loading = $('#popup-loading'); + if ($loading.length === 0) { // Loading graphic does not already exist, so create it. + var waitImageSize = 100; + var left = ($(window).width() / 2) - (waitImageSize / 2) + Drupal.popup.scrollLeft(); + var top = ($(window).height() / 2) - (waitImageSize / 2) + Drupal.popup.scrollTop(); + $loading = $( Drupal.theme('popupLoading', left, top) ); + $('body').prepend($loading); + } +}; + +Drupal.popup.removeLoading = function() { + $('#popup-loading').remove(); +}; + +/** + * Remove everything. + */ +Drupal.popup.close = function() { + Drupal.popup.removePopup(); + Drupal.popup.removeLoading(); + Drupal.popup.removeOverlay(); + return false; +}; + +/** + * Set the focus on the popup to the first visible form element, + * or the close link if there are no visible form elements. + */ +Drupal.popup.refocus = function() { + $focus = $('#popup input:visible:eq(0)'); + if ($focus.length === 0) { + $focus = $('#popup-close a'); + } + $focus.focus(); +}; + +/** + * Get current position of the left side of the browser window. + * Copied from jQuery offset. + */ +Drupal.popup.scrollLeft = function() { + return Math.max(document.documentElement.scrollLeft, document.body.scrollLeft); +}; + +/** + * Get current position of the top of the browser window. + * Copied from jQuery offset. + */ +Drupal.popup.scrollTop = function() { + return Math.max(document.documentElement.scrollTop, document.body.scrollTop); +}; + +/** + * Warn the user if ajax will not work with the current theme. + */ +Drupal.popup.testContentSelector = function() { + var body = ''; + if ($(Drupal.popup.titleSelector).length !== 1) { + body += Drupal.t('The page title must be wrapped in an element with id') + ' = "' + Drupal.popup.titleSelector + '".
'; + } + if ($(Drupal.popup.contentSelector).length !== 1) { + body += Drupal.t('The page content must be wrapped in an element with id') + ' = "' + Drupal.popup.contentSelector + '".
'; + body += Drupal.t('The popup behavior is disabled for confirmation dialogs.'); + } + if (body) { + title = Drupal.t('Your current theme does not support the popup behavior.'); + Drupal.popup.showPopup(title, body); + } +}; + +Drupal.theme.prototype.popupLoading = function(left, top) { + var loading = ''; + return loading; +}; + +Drupal.theme.prototype.popupOverlay = function() { + return ''; +}; + +Drupal.theme.prototype.popupButton = function(title, id) { + return ''; +}; + +Drupal.theme.prototype.popupDialog = function(title, body, buttons) { + var template = Drupal.settings.popup.template; + var popup = template.replace('%title', title).replace('%body', body); + + var themedButtons = ''; + if (buttons) { + jQuery.each(buttons, function (id, button) { + themedButtons += Drupal.theme('popupButton', button.title, id); + }); + } + popup = popup.replace('%buttons', themedButtons); + return popup; +}; +