Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.594 diff -u -r1.594 system.module --- modules/system/system.module 1 Apr 2008 20:05:15 -0000 1.594 +++ modules/system/system.module 1 Apr 2008 20:43:19 -0000 @@ -149,6 +149,11 @@ 'arguments' => array('image_path' => NULL), ), 'system_compact_link' => array(), + 'popup_template' => array( + 'template' => 'popup-template', + ), + 'popup_save_dialog' => array(), + )); } @@ -632,6 +637,20 @@ 'type' => MENU_CALLBACK, 'file' => 'system.admin.inc', ); + + // AJAX popups: + $items['admin/settings/popup'] = array( + 'page callback' => 'drupal_get_form', + 'page arguments' => array('system_popup_settings'), + 'title' => 'AJAX Popups', + 'description' => 'Configure the AJAX popup behavior.', + 'file' => 'system.admin.inc', + ); + $items['popup/save_dialog'] = array( + 'page callback' => 'theme', + 'page arguments' => array('popup_save_dialog'), + 'access callback' => TRUE, + ); return $items; } Index: modules/system/system.css =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.css,v retrieving revision 1.48 diff -u -r1.48 system.css --- modules/system/system.css 9 Jan 2008 09:56:39 -0000 1.48 +++ modules/system/system.css 1 Apr 2008 20:43:18 -0000 @@ -542,3 +542,81 @@ span.password-confirm span { font-weight: normal; } + +/* +** AJAX popup dialog box styles +*/ + +#popup-overlay { + position: absolute; +/* width: 100%; + height: 100%; */ + background: black; + z-index: 9; + top: 0; +} +#popup-loading { + z-index: 10; + opacity: 0.75; + position: relative; +} +#popup-loading div{ + position: absolute; +} +#popup { + border: 2px solid #EDF5FA; + background: white; + position: absolute; + opacity: 1.0; + z-index: 10; + color: black; + padding: 0.5em; + width: 600px; + overflow: auto; +} +#popup-title { + /*background: #222; + color: white; + */ + border-bottom: 1px solid #b4d7f0; + background-color: #d4e7f3; + color: #455067; + font-weight: bold; + padding: 0.25em; + margin-bottom: 0.25em; +} +#popup-title div.title { + float:left; +} +#popup-title #popup-close { + float:right; + line-height: 0pt; + margin-top: 2px; /* Trying for vertical centering */ +} +#popup-title #popup-close a { + color: red; +} +#popup-title div.clear { + clear: both; +} +#popup input { + margin: 0.1em; +} +#popup input[type="text"]:focus, #popup input[type="password"]:focus, #popup textarea:focus { + background-color: #FFA; + outline: thin solid grey; +} +#popup div.messages { + background: transparent; + border: none; + padding: 0; + margin: 0; +} + +a.popup-processed:after { +/* TODO - Find a good icon + content: "[\2020]"; + vertical-align: super; + font-size: smaller; +*/ +} Index: modules/system/system.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v retrieving revision 1.66 diff -u -r1.66 system.admin.inc --- modules/system/system.admin.inc 23 Mar 2008 14:46:49 -0000 1.66 +++ modules/system/system.admin.inc 1 Apr 2008 20:43:18 -0000 @@ -116,6 +116,31 @@ } /** + * Todo - probably will be removed, if content area in themes is better specified. + * + * @return unknown + */ +function system_popup_settings() { + drupal_set_title("AJAX popup settings"); + $form = array(); + + $form['popup_title_selector'] = array( + '#type' => 'textfield', + '#title' => t('Title selector'), + '#default_value' => variable_get('popup_title_selector', 'div.left-corner > h2:eq(0)'), + '#description' => t("jQuery selector to define the page's title on your Admin theme."), + ); + $form['popup_content_selector'] = array( + '#type' => 'textfield', + '#title' => t('Content selector'), + '#default_value' => variable_get('popup_content_selector', 'div.left-corner > div.clear-block:last'), + '#description' => t('jQuery selector to define the page\'s content area on your Admin theme.'), + ); + + return system_settings_form($form); +} + +/** * Menu callback; displays a module's settings page. */ function system_settings_overview() { Index: modules/block/block.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.admin.inc,v retrieving revision 1.14 diff -u -r1.14 block.admin.inc --- modules/block/block.admin.inc 22 Dec 2007 23:24:24 -0000 1.14 +++ modules/block/block.admin.inc 1 Apr 2008 20:43:16 -0000 @@ -30,6 +30,7 @@ // Add CSS drupal_add_css(drupal_get_path('module', 'block') .'/block.css', 'module', 'all', 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'); @@ -76,7 +77,7 @@ } $form[$key]['configure'] = array('#value' => l(t('configure'), 'admin/build/block/configure/'. $block['module'] .'/'. $block['delta'])); if ($block['module'] == 'block') { - $form[$key]['delete'] = array('#value' => l(t('delete'), 'admin/build/block/delete/'. $block['delta'])); + $form[$key]['delete'] = array('#value' => l(t('delete'), 'admin/build/block/delete/'. $block['delta'], array('attributes' => array('class' => 'popup-form')))); } } Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.267 diff -u -r1.267 menu.inc --- includes/menu.inc 21 Mar 2008 08:32:24 -0000 1.267 +++ includes/menu.inc 1 Apr 2008 20:43:15 -0000 @@ -1167,6 +1167,7 @@ // Add "more help" link on admin pages if the module provides a // standalone help page. if ($arg[0] == "admin" && module_exists('help') && module_invoke($name, 'help', 'admin/help#'. $arg[2], $empty_arg) && $help) { + drupal_add_popup(); $output .= theme("more_help_link", url('admin/help/'. $arg[2])); } } Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.418 diff -u -r1.418 theme.inc --- includes/theme.inc 25 Mar 2008 14:10:01 -0000 1.418 +++ includes/theme.inc 1 Apr 2008 20:43:16 -0000 @@ -1449,7 +1449,7 @@ * Returns code that emits the 'more help'-link. */ function theme_more_help_link($url) { - return ''; + return ''; } /** @@ -1459,7 +1459,7 @@ * * @see theme_feed_icon() * @param $url - * The url of the feed. + * The URL of the feed. */ function theme_xml_icon($url) { if ($image = theme('image', 'misc/xml.png', t('XML feed'), t('XML feed'))) { @@ -1471,7 +1471,7 @@ * Return code that emits an feed icon. * * @param $url - * The url of the feed. + * The URL of the feed. * @param $title * A descriptive title of the feed. */ @@ -1485,7 +1485,7 @@ * Returns code that emits the 'more' link used on blocks. * * @param $url - * The url of the main page + * The URL of the main page * @param $title * A descriptive verb for the link, like 'Read more' */ @@ -1616,6 +1616,22 @@ } /** + * An unusual type of theme, this function returns json version of content for + * the save-on-changed ajax popup dialog. + */ +function theme_popup_save_dialog() { + print drupal_json(array( + 'title' => t('Warning: please confirm'), + 'content' => t("There are unsaved changes on this page, which you will lose if you continue."), + 'buttons' => array( + 'save' => t('Save changes'), + 'submit' => t('Continue'), + 'cancel' => t('Cancel'), + ), + )); +} + +/** * @} End of "defgroup themeable". */ Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.762 diff -u -r1.762 common.inc --- includes/common.inc 31 Mar 2008 20:50:05 -0000 1.762 +++ includes/common.inc 1 Apr 2008 20:43:14 -0000 @@ -155,7 +155,7 @@ * Add a feed URL for the current page. * * @param $url - * A url for the feed. + * A URL for the feed. * @param $title * The title of the feed. */ @@ -477,7 +477,7 @@ 'Content-Length' => 'Content-Length: '. strlen($data) ); - // If the server url has a user then attempt to use basic authentication + // If the server URL has a user then attempt to use basic authentication if (isset($uri['user'])) { $defaults['Authorization'] = 'Authorization: Basic '. base64_encode($uri['user'] . (!empty($uri['pass']) ? ":". $uri['pass'] : '')); } @@ -2209,6 +2209,24 @@ } /** + * Attach the popup behavior to the page. + */ +function drupal_add_popup() { + static $added = FALSE; + if (!$added) { + drupal_add_js('misc/popup.js', 'core'); + drupal_add_js('misc/jquery.form.js'); + $settings = array( 'popup' => array( + 'defaultTargetSelector' => variable_get('popup_content_selector', 'div.left-corner > div.clear-block:last'), + 'defaultTitleSelector' => variable_get('popup_title_selector', 'div.left-corner > h2:eq(0)'), + 'template' => theme('popup_template'), + )); + drupal_add_js( $settings, 'setting' ); + $added = TRUE; + } +} + +/** * Aggregate JS files, putting them in the files directory. * * @param $files Index: modules/filter/filter.module =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v retrieving revision 1.207 diff -u -r1.207 filter.module --- modules/filter/filter.module 13 Mar 2008 21:26:08 -0000 1.207 +++ modules/filter/filter.module 1 Apr 2008 20:43:17 -0000 @@ -477,6 +477,7 @@ $value = filter_resolve_format($value); $formats = filter_formats(); + drupal_add_popup(); $extra = theme('filter_tips_more_info'); if (count($formats) > 1) { @@ -581,7 +582,7 @@ * @ingroup themeable */ function theme_filter_tips_more_info() { - return '

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

'; + 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.10 diff -u -r1.10 filter.admin.inc --- modules/filter/filter.admin.inc 19 Feb 2008 14:07:21 -0000 1.10 +++ modules/filter/filter.admin.inc 1 Apr 2008 20:43:16 -0000 @@ -159,7 +159,8 @@ // Composition tips (guidelines) $tips = _filter_tips($format->format, FALSE); - $extra = '

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

'; + drupal_add_popup(); + $extra = '

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

'; $tiplist = theme('filter_tips', $tips, FALSE, $extra); if (!$tiplist) { $tiplist = '

'. t('No guidelines available.') .'

'; Index: modules/menu/menu.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/menu/menu.admin.inc,v retrieving revision 1.29 diff -u -r1.29 menu.admin.inc --- modules/menu/menu.admin.inc 23 Feb 2008 08:13:09 -0000 1.29 +++ modules/menu/menu.admin.inc 1 Apr 2008 20:43:17 -0000 @@ -61,6 +61,7 @@ */ function _menu_overview_tree_form($tree) { static $form = array('#tree' => TRUE); + drupal_add_popup(); foreach ($tree as $data) { $title = ''; $item = $data['link']; @@ -97,11 +98,11 @@ $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-form'))); } // Set the reset column. elseif ($item['module'] == 'system' && $item['customized']) { - $operations['reset'] = l(t('reset'), 'admin/build/menu/item/'. $item['mlid'] .'/reset'); + $operations['reset'] = l(t('reset'), 'admin/build/menu/item/'. $item['mlid'] .'/reset', array('attributes' => array('class' => 'popup-form'))); } $form[$mlid]['operations'] = array(); Index: modules/node/content_types.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/node/content_types.inc,v retrieving revision 1.50 diff -u -r1.50 content_types.inc --- modules/node/content_types.inc 27 Jan 2008 18:03:05 -0000 1.50 +++ modules/node/content_types.inc 1 Apr 2008 20:43:17 -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'), t('Type'), t('Description'), array('data' => t('Operations'), 'colspan' => '2')); @@ -29,7 +30,7 @@ // Set the delete column. if ($type->custom) { - $row[] = array('data' => l(t('delete'), 'admin/content/node-type/'. $type_url_str .'/delete')); + $row[] = array('data' => l(t('delete'), 'admin/content/node-type/'. $type_url_str .'/delete', array('attributes' => array('class' => 'popup-form')))); } else { $row[] = array('data' => ''); Index: modules/user/user.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v retrieving revision 1.19 diff -u -r1.19 user.admin.inc --- modules/user/user.admin.inc 20 Feb 2008 13:46:43 -0000 1.19 +++ modules/user/user.admin.inc 1 Apr 2008 20:43:20 -0000 @@ -901,9 +901,16 @@ $header = array(array('data' => t('Access type'), 'field' => 'status'), array('data' => t('Rule type'), 'field' => 'type'), array('data' => t('Mask'), 'field' => 'mask'), array('data' => t('Operations'), 'colspan' => 2)); $result = db_query("SELECT aid, type, status, mask FROM {access}". tablesort_sql($header)); $access_types = array('user' => t('username'), 'mail' => t('e-mail'), 'host' => t('host')); + drupal_add_popup(); $rows = array(); while ($rule = db_fetch_object($result)) { - $rows[] = array($rule->status ? t('allow') : t('deny'), $access_types[$rule->type], $rule->mask, l(t('edit'), 'admin/user/rules/edit/'. $rule->aid), l(t('delete'), 'admin/user/rules/delete/'. $rule->aid)); + $rows[] = array( + $rule->status ? t('allow') : t('deny'), + $access_types[$rule->type], + $rule->mask, + l(t('edit'), 'admin/user/rules/edit/'. $rule->aid), + l(t('delete'), 'admin/user/rules/delete/'. $rule->aid, array('attributes' => array('class' => 'popup-form'))) + ); } if (empty($rows)) { $rows[] = array(array('data' => ''. t('There are currently no access rules.') .'', 'colspan' => 5)); Index: misc/tableheader.js =================================================================== RCS file: /cvs/drupal/drupal/misc/tableheader.js,v retrieving revision 1.16 diff -u -r1.16 tableheader.js --- misc/tableheader.js 30 Jan 2008 10:17:39 -0000 1.16 +++ misc/tableheader.js 1 Apr 2008 20:43:16 -0000 @@ -12,6 +12,11 @@ return; } + // If there are no sticky tables in this context, just return. + if (!$('table.sticky-enabled', context).size()) { + return; + } + // Keep track of all cloned table headers. var headers = []; Index: modules/help/help.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/help/help.admin.inc,v retrieving revision 1.5 diff -u -r1.5 help.admin.inc --- modules/help/help.admin.inc 25 Nov 2007 11:11:17 -0000 1.5 +++ modules/help/help.admin.inc 1 Apr 2008 20:43:17 -0000 @@ -23,7 +23,7 @@ $output = ''; if (module_hook($name, 'help')) { $module = drupal_parse_info_file(drupal_get_path('module', $name) .'/'. $name .'.info'); - drupal_set_title($module['name']); + drupal_set_title(t('Help: '). $module['name']); $temp = module_invoke($name, 'help', "admin/help#$name", drupal_help_arg()); if (empty($temp)) { Index: modules/path/path.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/path/path.admin.inc,v retrieving revision 1.7 diff -u -r1.7 path.admin.inc --- modules/path/path.admin.inc 8 Jan 2008 10:35:42 -0000 1.7 +++ modules/path/path.admin.inc 1 Apr 2008 20:43:17 -0000 @@ -41,7 +41,12 @@ $rows = array(); $destination = drupal_get_destination(); while ($data = db_fetch_object($result)) { - $row = array(check_plain($data->dst), check_plain($data->src), l(t('edit'), "admin/build/path/edit/$data->pid", array('query' => $destination)), l(t('delete'), "admin/build/path/delete/$data->pid", array('query' => $destination))); + $row = array( + check_plain($data->dst), + check_plain($data->src), + l(t('edit'), "admin/build/path/edit/$data->pid", array('query' => $destination)), + l(t('delete'), "admin/build/path/delete/$data->pid", array('query' => $destination, 'attributes' => array('class' => 'popup-form'))) + ); if ($multilanguage) { $row[4] = $row[3]; $row[3] = $row[2]; 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,464 @@ +// $Id:$ + +/** + * Popup Modal Dialog API + * + * Provide an API for building and displaying JavaScript, in-page, popup modal dialogs. + * Modality is provided by a fixed, semi-opaque div, positioned in front of the page contents. + * + */ + +/** + * Attach the popup bevior to the all the requested links on the page. + * + * @param context + * The jQuery object to apply the behaviors to. + */ +Drupal.behaviors.popup = function(context) { + var $body = $('body'); + if(!$body.hasClass('popup-processed')) { + $(document).bind('keydown', Drupal.popup.keyHandle); + $body.addClass('popup-processed'); + } + Drupal.popup.attach(context, 'a.popup', {}); + Drupal.popup.attach(context, 'a.popup-form', {updatePage: true}); + Drupal.popup.attach(context, 'a.popup-nonmodal', {nonModal: true}); +}; + +/** + * Create the popup object/namespace. + */ +Drupal.popup = function() {}; + +/** + * 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 $link = $(this); + $link.click( function(e){ + var a = this; + // If the option is distructive, check if the page is already modified, and offer to save. + var pageIsDirty = $('span.tabledrag-changed').size() > 0; + var willModifyOriginal = options.updatePage; + if( pageIsDirty && willModifyOriginal ) { + // The user will lose modifications, so popup dialog offering to save current state. + $.getJSON(Drupal.settings.basePath + 'popup/save_dialog', function(json) { + // Attach behaviors to the buttons in the save-if-modified dialog. + var buttons = { + 'popups_save': {title: json.buttons.save, func: function(){Drupal.popup.savePage(a, options);}}, + 'popups_submit': {title: json.buttons.submit, func: function(){Drupal.popup.removePopup(); Drupal.popup.openPath(a, options);}}, + 'popups_cancel': {title: json.buttons.cancel, func: Drupal.popup.close} + }; + Drupal.popup.open(json.title, json.content, buttons); + }); + return false; + } + else { + return Drupal.popup.openPath(a, options); + } + }); + var title = $link.attr('title') || ''; + $link.attr('title', title + Drupal.t('[Popup]')); // Append note to link title. + $link.addClass('popup-processed'); + }); +}; + + +/** + * Generic dialog builder. + */ +Drupal.popup.open = function(title, body, buttons) { + + Drupal.popup.addOverlay(); // TODO - nonModal option. + var $popup = $(Drupal.theme('popupsDialog', title, body, buttons)); + // Start with dialog off the side. Making it invisible causes flash in FF2. + $popup.css('left', -9999); + $('body').append( $popup ); // Add the popup to the DOM. + + // Adding button functions + if (buttons) { + for (var id in buttons) { + if (buttons[id]) { // to make jslint happy. + var func = buttons[id].func; + $('#'+id).click( func ); + } + } + } + $('#popup-close').click( Drupal.popup.close ); + + // 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. + // Causes 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; +}; + +/** + * Simple popup that functions like the browser's alert box. + */ +Drupal.popup.message = function(message, body) { + body = body || ''; + var buttons = { + 'popups_ok': { title: Drupal.t('OK'), func: Drupal.popup.close } + }; + Drupal.popup.open( message, body, buttons ); +}; + +/** + * Handle any special keys when popup is active. + */ +Drupal.popup.keyHandle = function(e) { + if (!e) { + e = window.event; + } + switch (e.keyCode) { + case 27: // esc + Drupal.popup.close(); + break; + case 191: // '?' key, show help. + if (e.shiftKey && e.ctrlKey) { + var $help = $('a.popup.more-help'); + if ($help.size()) { + $help.click(); + } + else { + Drupal.popup.message(Drupal.t("Sorry, there is no additional help for this page")); + } + } + break; + } +}; + +/***************************************************************************** + * Appearence Functions (overlay, loading graphic, remove popup) ********* + *****************************************************************************/ + +Drupal.popup.removePopup = function() { + $('#popup').fadeOut('fast', function() { + $('#popup').remove(); + }); +}; + +Drupal.popup.addOverlay = function() { + var $overlay = $('#popup-overlay'); + if (!$overlay.size()) { // Overlay does not already exist, so create it. + $overlay = $(Drupal.theme('popupsOverlay')); + $overlay.css('opacity', '0.4'); // for ie6(?) + $body = $('body'); + // Doing absolute positioning, so make overlay's size equal the entire body. + $overlay.width($body.width()).height($body.height()); + $overlay.click(Drupal.popup.close); + $body.prepend($overlay); + } +// return $overlay; +}; + +//Drupal.popup.getOverlay = function() { +// return $('#popup-overlay'); +//}; + +Drupal.popup.removeOverlay = function() { + $('#popup-overlay').fadeOut('fast', function() { + $('#popup-overlay').remove(); + }); +}; + +Drupal.popup.addLoading = function() { + var $loading = $('#popup-loading'); + if (!$loading.size()) { // Overlay 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('popupsLoading', 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(); +}; + +/** + * Set the focus on the popup to the first visible form element, or the first button, or the close link. + */ +Drupal.popup.refocus = function() { + $focus = $('#popup input:visible:eq(0)'); + if (!focus) { + $focus = $('#popup-close'); // Doesn't seem to work. + } + $focus.focus(); +}; + +/**************************************************************************** + * Theme Functions ******************************************************** + ****************************************************************************/ + +Drupal.theme.prototype.popupsLoading = function(left, top) { + var loading = ''; + return loading; +}; + +Drupal.theme.prototype.popupsOverlay = function() { + return ''; +}; + +Drupal.theme.prototype.popupsButton = function(title, id) { + return ''; +}; + +Drupal.theme.prototype.popupsDialog = function(title, body, buttons) { + var template = Drupal.settings.popup.template; + var popup = template.replace('%title', title).replace('%body', body); + + var themedButtons = ''; + for ( var id in buttons) { + if (buttons[id]) { + themedButtons += Drupal.theme('popupsButton', buttons[id].title, id); + } + } + popup = popup.replace('%buttons', themedButtons); + return popup; +}; + +/**************************************************************************** + * Utility scroll functions taken from: *** + * http://www.softcomplex.com/docs/get_window_size_and_scrollbar_position.html + ****************************************************************************/ + +// FROM jQuery offset +// elem = this[0]; +// doc = elem.ownerDocument; +// add(box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft), +// box.top + Math.max(doc.documentElement.scrollTop, doc.body.scrollTop)); + +Drupal.popup.scrollLeft = function() { + return Math.max(document.documentElement.scrollLeft, document.body.scrollLeft); +// return Drupal.popup.filterResults ( +// window.pageXOffset ? window.pageXOffset : 0, +// document.documentElement ? document.documentElement.scrollLeft : 0, +// document.body ? document.body.scrollLeft : 0 ); +}; + +Drupal.popup.scrollTop = function() { + return Math.max(document.documentElement.scrollTop, document.body.scrollTop); +// return Drupal.popup.filterResults ( +// window.pageYOffset ? window.pageYOffset : 0, +// document.documentElement ? document.documentElement.scrollTop : 0, +// document.body ? document.body.scrollTop : 0 ); +}; + +Drupal.popup.filterResults = function(win, docel, body) { + var result = win ? win : 0; + if (docel && (!result || (result > docel))) { + result = docel; + } + return body && (!result || (result > body)) ? body : result; +}; + + +/**************************************************************************** + * Page & Form in popup functions *** + ****************************************************************************/ + +/** + * Use Ajax to open the link in a popup window. + * + * @param a + * Link 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(a, options) { + // let the user know something is happening + $('body').css("cursor", "wait"); + + // TODO - get nonmodal working. + if (!options.nonModal) { + Drupal.popup.addOverlay(); + } + Drupal.popup.addLoading(); + + // Set custom headers for all following requests. + $.ajaxSetup({ + beforeSend: function(xhr) { + xhr.setRequestHeader("X-Drupal-Render-Mode", 'json/popup'); + } + }); + + $.getJSON(a.href, function(json) { + Drupal.popup.openContent(json.title, json.messages + json.content, options); + $('body').css("cursor", "auto"); // Return the cursor to normal state. + }); + + return false; +}; + +/** + * Open content in an ajax popup. + * + * @param title + * String title of the popup. + * @param content + * HTML to show in the popup. + * @param options + * Hash of options controlling how the popup interacts with the underlying page. + */ +Drupal.popup.openContent = function(title, content, options) { + Drupal.popup.open(title, content); + // Add behaviors to content in popup. + // TODO: d-n-d: need to click to let go of selection. + Drupal.attachBehaviors($('#popup-body')); + // Adding collapse moves focus. + Drupal.popup.refocus(); + + // If the popup contains a form, capture submits. + var $form = $('form', '#popup-body'); + $form.ajaxForm({ + dataType: 'json', + beforeSubmit: Drupal.popup.beforeSubmit, + success: function(response, status) { + Drupal.popup.formSuccess(response, options); + } + }); +}; + +/** + * Do before the form in the popup is submitted. + */ +Drupal.popup.beforeSubmit = function(formData, $form, options) { + Drupal.popup.removePopup(); // Remove just the dialog, but not the overlay. + Drupal.popup.addLoading(); +}; + +/** + * The form in the popup was successfully submitted + * Update the originating page. + * Show any messages in a popup (TODO - make this a configurable option). + * + * @param response + * JSON object from server with status of form submission. + * @param options + * Hash of options controlling how the popup interacts with the underlying page. + * //noReload: bool, does the popup effect the underlying page. + * updatePage: bool, does the popup effect the underlying page. + * updateTitle: bool, does the popup change the title of the page. + * nonModal: bool, does the popup block access to the underlying page. + * targetSelector: jQuery selector, overrides defaultTargetSelector. + */ +Drupal.popup.formSuccess = function(response, options) { + if (response.status != 'redirect') { // something went wrong + Drupal.popup.message( "Error: " + response.messages + response.content); + } + else { // Got a good response back from the server. + $.getJSON(response.path, function(data) { + // Determine if we are at an end point, or just moving from one popup to another. + if (!location.href.match(data.path)) { // Not done yet, so show results in new popup. + Drupal.popup.removeLoading(); + Drupal.popup.openContent(data.title, data.messages + data.content, options); + } + else { // Done, so show messages in dialog and embed the results in the original page. + if (data.messages) { + Drupal.popup.message(data.messages); + // Also insert the message into the page above the content. + // Might not be the standard spot, but it is the easiest to find. + var $next = $(Drupal.settings.popup.defaultTargetSelector); + $next.parent().find('div.messages').remove(); // Remove the current messages. + $next.before(data.messages); + } + + // Update the entire content area (defined by 'target selector'). + if (options.updatePage) { + var target = options.targetSelector; + if (!target) { + target = Drupal.settings.popup.defaultTargetSelector; + } + // Update the original page. + var $c = $(target).html(data.content); // Inject the new content into the page. + Drupal.attachBehaviors($c); + } + + // Update the title of the page. + if (options.updateTitle) { + $(Drupal.settings.popup.defaultTitleSelector).html(data.title); + document.title = data.title; // Also update the browser page title (TODO: include site name?). + } + + // Done with changes to the original page, remove effects. + Drupal.popup.removeLoading(); + if (!data.messages) { + // If there is not a messages popup remove the overlay. + Drupal.popup.removeOverlay(); + } + } // End of updating original page. + }); // End of good response. + } +}; + +/** + * Submit the page and reload the results, before popping up the real dialog. + * + * @param a + * Link that was clicked to open the popup. + * @param options + * Hash of options controlling how the popup interacts with the underlying page. + */ +Drupal.popup.savePage = function(a, options) { + var target = Drupal.settings.popup.defaultTargetSelector; + var $form = $('form', target); + var ajaxOptions = { + dataType: 'json', + beforeSubmit: Drupal.popup.beforeSubmit, + success: function(response, status) { + // Sync up the current page contents with the submit. + $.ajax({ + url: response.path, + dataType: 'json', + beforeSend: function(xhr) { + xhr.setRequestHeader("X-Drupal-Render-Mode", 'json/popup'); + }, + success: function(json) { + // Update the original page. + var $c = $(target).html(json.content); // Inject the new content into the page. + Drupal.attachBehaviors($c); + // The form has been saved and the page reloaded, now safe to open the clicked link in a popup. + Drupal.popup.openPath(a, options); + } + }); + } + }; + $form.ajaxSubmit(ajaxOptions); // Submit the form. +}; Index: modules/system/popup-template.tpl.php =================================================================== RCS file: modules/system/popup-template.tpl.php diff -N modules/system/popup-template.tpl.php --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/system/popup-template.tpl.php 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,13 @@ + + +