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 20 Mar 2008 01:11:24 -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_popups(); $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: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.417 diff -u -r1.417 theme.inc --- includes/theme.inc 20 Feb 2008 13:39:29 -0000 1.417 +++ includes/theme.inc 20 Mar 2008 01:11:22 -0000 @@ -1449,7 +1449,7 @@ * Returns code that emits the 'more help'-link. */ function theme_more_help_link($url) { - 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 20 Mar 2008 01:11:22 -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_popups(); + $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: index.php =================================================================== RCS file: /cvs/drupal/drupal/index.php,v retrieving revision 1.94 diff -u -r1.94 index.php --- index.php 26 Dec 2007 08:46:48 -0000 1.94 +++ index.php 20 Mar 2008 01:11:19 -0000 @@ -32,8 +32,7 @@ } } elseif (isset($return)) { - // Print any value (including an empty string) except NULL or undefined: - print theme('page', $return); + print render('page', $return); } drupal_page_footer(); 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 20 Mar 2008 01:11:22 -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/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 20 Mar 2008 01:11:22 -0000 @@ -10,6 +10,7 @@ * Displays the content type admin overview page. */ function node_overview_types() { + drupal_add_popups(); $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/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 20 Mar 2008 01:11:22 -0000 @@ -61,6 +61,7 @@ */ function _menu_overview_tree_form($tree) { static $form = array('#tree' => TRUE); + drupal_add_popups(); 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/popuppage/popuppage.js =================================================================== RCS file: modules/popuppage/popuppage.js diff -N modules/popuppage/popuppage.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/popuppage/popuppage.js 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,247 @@ +// $Id:$ + +/** + * This module uses the Popup API to build dialogs that show the content from a Drupal page. + * + * It is assumed that the pages being returned from the Drupal server will be JSON + * and have the following format: + * + * response.title = The page title. + * response.messages = Error, warning and status messages, as HTML. + * response.path = The page URL. + * response.content = The content of the page, as HTML. + */ + +/** + * 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.popuppage = function(context) { + var popups = new Drupal.popups(); + + // Add the popup-link-in-dialog behavior to links defined in Drupal.settings.popups.links array. + // TODO: how to handle popups-in-popups? + for (var link in Drupal.settings.popups.links) { + var options = Drupal.settings.popups.links[link]; + popups.attach(context, link, options); // Needs to be seperate function for closure. + } + popups.attach(context, 'a.popup', {}); +}; + +/** + * Attach the popup behavior to a particular link. + * + * @param link - link that was clicked. + * @param options - options associated with the link. + */ +Drupal.popups.prototype.attach = function(context, link, options) { + var popups = this; + $(link, context).not('.popups-processed').each( function() { + $(this).click( function(e){ + var a = this; + // If the option is distructive, check if the page is already modified, and offer to save. + var page_is_dirty = $('span.tabledrag-changed').size() > 0; + var will_modify_original = !options.noReload && !options.singleRow; + if( page_is_dirty && will_modify_original ) { + // The user will lose modifications, so popup dialog offering to save current state. +/* + var body = Drupal.t("There are unsaved changes on this page, which you will lose if you continue."); + var buttons = { + 'popups_save': {title: Drupal.t('Save Changes'), func: function(){popups.save_page(a, options)}}, + 'popups_submit': {title: Drupal.t('Continue'), func: function(){Drupal.popups.close(); popups.open_path(a, options)}}, + 'popups_cancel': {title: Drupal.t('Cancel'), func: Drupal.popups.close} + }; + return popups.open( Drupal.t('Warning: Please Confirm'), body, buttons ); +*/ + $.getJSON(Drupal.settings.basePath + 'popuppage/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(){popups.save_page(a, options)}}, + 'popups_submit': {title: json.buttons.submit, func: function(){Drupal.popups.close(); popups.open_path(a, options)}}, + 'popups_cancel': {title: json.buttons.cancel, func: Drupal.popups.close} + }; + popups.open(json.title, json.content, buttons); + }); + return false; +// // Attach the save/continue/cancel behaviors +// $('#popups_save').click(function(){popups.save_page(a, options)}), +// $('#popups_submit').click(function(){Drupal.popups.close(); popups.open_path(a, options)}), +// $('#popups_cancel').click(Drupal.popups.close); +/* + var buttons = { + 'popups_save': {title: Drupal.settings.popups.save.buttons.save, func: function(){popups.save_page(a, options)}}, + 'popups_submit': {title: Drupal.settings.popups.save.buttons.submit, func: function(){Drupal.popups.close(); popups.open_path(a, options)}}, + 'popups_cancel': {title: Drupal.settings.popups.save.buttons.cancel, func: Drupal.popups.close} + }; + return popups.open( Drupal.settings.popups.save.title, Drupal.settings.popups.save.message, buttons ); +*/ + } + else { + return popups.open_path(a, options); + } + }); + $(this).addClass('popups-processed'); + }); +}; + +/** + * Use Ajax to open the link in a popup window. + * + * @param a - link that was clicked. + * @param options - options associated with the link. + */ +Drupal.popups.prototype.open_path = function( a, options ) { + var popup = this; + // let the user know something is happening + $('body').css("cursor", "wait"); + var $overlay = Drupal.popups.add_overlay(); + Drupal.popups.add_loading(); + + // Set custom headers for all following requests. + $.ajaxSetup({ + dataType: 'json', + beforeSend: function(xhr) { +// xhr.setRequestHeader('Accept', 'application/json'); // this might be a good idea? + xhr.setRequestHeader("X-Drupal-Render-Mode", 'json/popup'); + } + }); + + $.getJSON(a.href, function(json) { + popup.open_content(json.title, json.messages + json.content, options, a); + $('body').css("cursor", "auto"); // Return the cursor to normal state. + }); + + return false; +}; + + +Drupal.popups.prototype.open_content = function(title, content, options, a) { + this.open(title, content); + // Add behaviors to content in popup. + // TODO: d-n-d: need to click to let go of selection. + Drupal.attachBehaviors($('#popups-body')); + // Adding collapse moves focus. + this.refocus(); + + // If the popup contains a form, capture submits. + var $form = $('form', '#popups-body'); + $form.ajaxForm({ + dataType: 'json', + beforeSubmit: Drupal.popups.beforeSubmit, + success: function(response, status) { Drupal.popups.formSuccess(response, options, a) }, + }); +} + +/** + * Do before the form in the popup is submitted. + * + */ +Drupal.popups.beforeSubmit = function(form_data, $form, options) { + Drupal.popups.remove_popup(); // Remove just the dialog, but not the overlay. + Drupal.popups.add_loading(); +}; + +/** + * 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 data - JSON object from server with status of form submission. + * @param options - hash of per link options. + * @param a - the link that was clicked. + */ +Drupal.popups.formSuccess = function (response, options, a) { + if (response.status != 'redirect') { // something went wrong + Drupal.popups.message( "Error: " + response.messages + response.content); + } + else { // Got a good response back from the server. + $.getJSON(response.path, function(data) { + // Are we 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.popups.remove_loading(); + var popups = new Drupal.popups(); + popups.open_content(data.title, data.messages + data.content, options, a); + } + else { // Done, so show messages in dialog and embed the results in the original page. + if (data.messages) { + Drupal.popups.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.popups.defaultTargetSelector); + $next.parent().find('div.messages').remove(); // Remove the current messages. + $next.before(data.messages); + } + + // Just update a single row out of a table (still expiremental). + // Loop through, with special case for first element. + if (options.singleRow) { + var href = $(a).attr('href'); + var selector = 'table a[href=' + href + ']'; + var $new_row = $data.find(selector).parents('tr'); // new tr + var $target_row = $(selector).parents('tr'); // target tr. + for (var i in options.singleRow) { + var col = options.singleRow[i]; + $new_row.find(col).contents().not('div.indentation').wrapAll(''); + $target_row.find(col).contents() + .not('a.tabledrag-handle').not('span.warning').not('div.indentation') + .wrapAll(''); + $('#killme').replaceWith( $new_row.find('#newvalue').html() ); + } + } + // Update the entire content area (defined by 'target selector'). + else if (!options.noReload) { + var target = options.targetSelector; + if (!target) { + target = Drupal.settings.popups.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.popups.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.popups.remove_loading(); + if (!data.messages) { + // If there is not a messages popup remove the overlay. + Drupal.popups.remove_overlay(); + } + } // 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. + * @param options - options associated with the link. + */ +Drupal.popups.prototype.save_page = function(a, options) { + var popups = this; + // TODO - what if clicking on link with option['targetSelector']? + var target = Drupal.settings.popups.defaultTargetSelector; + var $form = $('form', target); + var ajaxOptions = { + dataType: 'json', + beforeSubmit: Drupal.popups.beforeSubmit, + success: function(response, status) { // Sync up the current page contents with the submit. + var $data = $(response); + var content = $data.find('content').text(); + var $c = $(target).html(content); // Inject the new content into the page. + Drupal.attachBehaviors($c); + Drupal.popups.close(); + // The form has been saved, the page reloaded, now safe to show the link in a popup. + popups.open_path(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 @@ + + + Index: modules/popuppage/popuppage.module =================================================================== RCS file: modules/popuppage/popuppage.module diff -N modules/popuppage/popuppage.module --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/popuppage/popuppage.module 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,360 @@ + Add vocab. Adding second item to page does not trigger d-n-d transformation. + * Might be a problem with Taxonomy. Menus doesn't have problem (adds d-n-d on first item). + */ + + +// ************************************************************************** +// CORE HOOK FUNCTIONS **************************************************** +// ************************************************************************** + +/** + * hook_menu + * + * @return array of new menu items. + */ +function popuppage_menu() { + $items['admin/settings/popuppage'] = array( + 'page callback' => 'drupal_get_form', + 'page arguments' => array('popuppage_admin_settings'), + 'title' => 'Popups', + 'description' => 'Configure the page-in-a-dialog behavior.', + ); + $items['popuppage/save_dialog'] = array( +// 'page callback' => 'popuppage_save_dialog', + 'page callback' => 'theme', + 'page arguments' => array('popup_save_dialog'), + 'access callback' => true, + ); + + return $items; +} + +function popuppage_theme() { + return array( + 'popup_template' => array( + 'template' => 'popup-template', +// 'arguments' => array('title' => '%title', 'body' => '%body', 'buttons' => '%buttons'), + ), + 'popup_save_dialog' => array(), + ); +} + +/** + * An unusual type of theme, this function returns a block of text for the + * save-on-changed ajax popup dialog. + */ +/* +function theme_popup_save() { + $body = t("There are unsaved changes on this page, which you will lose if you continue."); + $body .= ' array( +// 'save' => t('Save Changes'), +// 'submit' => t('Continue'), +// 'cancel' => t('Cancel'), +// ), +// ); +} +*/ + +/** + * 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'), + ), + )); +} + +// ??? + +/** + * hook_init + * + * Look at the page path and see if popup behavior has been requested for any links in this page. + */ +function popuppage_init() { + $popups = popuppage_get_popups(); + if (isset($popups[$_GET['q']])) { + popuppage_add_popups( $popups[$_GET['q']] ); + } +// watchdog( "server", print_r($_SERVER['HTTP_X_DRUPAL_RENDERER'], TRUE) ); + +} + +/** + * hook_form_alter + * + * Look at the form_id and see if popup behavior has been requested for any links in this form. + * + * @param form_array $form + * @param array $form_state + * @param str $form_id: + */ +function popuppage_form_alter(&$form, $form_state, $form_id) { +// print $form_id; + // Add popup behavior to the form if requested. + $popups = popuppage_get_popups(); + if (isset($popups[$form_id])) { + popuppage_add_popups( $popups[$form_id] ); + } +} + +// ************************************************************************** +// UTILITY FUNCTIONS ****************************************************** +// ************************************************************************** + +/** + * Build the list of popup rules from all modules that implement hook_popups. + * + * @todo - Add some caching so we don't rebuild everytime. + */ +function popuppage_get_popups() { + static $popups = NULL; + if (!isset($popups)) { + $popups = module_invoke_all('popups'); + } + return $popups; +} + +/** + * Attach the popup behavior to the page. + * + * The default behavoir of a popup is to open a form that will modify the original page. The popup submits + * the form and reloads the original page with the resulting new content. The popup then replaces + * the original page's content area with the new copy of that content area. + * + * @param array $rule: Array of rules to apply to the page or form, keyed by jQuery link selector. + * Options: + * noReload: Does the popup NOT modify the original page (Default: false). + * updateTitle: Does the popup modify the title of the current page (Default: false). + * surpressMessages: Don't show the messages the form returns in a popup (Default: false). + * targetSelector: Defines the area on the original page that will be updated (Default: system-wide setting) + * resultsSubselector: Defines the resulting new content to put into the targetSelector (Default: use the entire new results) + * TODO - come up with a good use cases for resultsSubselector and targetSelector. + * singleRow: Array of selectors descripting the elements inside a row to be replaced (Default: replace entire targetSelector) + * additionalJavascript: Array of JavaScript files that must be included to correctly run the page in the popup. + * additionalCss: Array of CSS files that must be included to correctly style the page in the popup. + * + * Rule Format Example: + * 'admin/content/taxonomy' => array( // Act only on the links on this page. + * 'div#tabs-wrapper a:eq(1)', // No options, so use defaults. Note: Selector must select element(s). + * 'table td:nth-child(2) a' => array( + * 'noReload' => true, // Popup will not modify original page. + * ), + * ) + * + */ +function popuppage_add_popups($rules=null) { + static $added = FALSE; + if (!$added) { + drupal_add_popups(); +// drupal_add_js('misc/popupapi.js', 'core'); +// drupal_add_js(drupal_get_path('module', 'popuppage') .'/popuppage.js'); +// drupal_add_js('misc/jquery.form.js'); + if (is_array($rules)) { + $settings = array( 'popups' => array( + 'defaultTargetSelector' => variable_get('popups_content_selector', 'div.left-corner > div.clear-block:last'), + 'defaultTitleSelector' => variable_get('popups_title_selector', 'div.left-corner > h2:eq(0)'), +// 'template' => theme('popup_template'), + 'links' => array(), // ??? + )); + foreach ($rules as $popup_selector => $options) { + if (is_array($options)) { + $settings['popups']['links'][$popup_selector] = $options; + if (isset($options['additionalJavascript']) && is_array($options['additionalJavascript'])) { + foreach ($options['additionalJavascript'] as $file) { + drupal_add_js($file); + } + } + if (isset($options['additionalCss']) && is_array($options['additionalCss'])) { + foreach ($options['additionalCss'] as $file) { + drupal_add_css($file); + } + } + } + else { + $settings['popups']['links'][$options] = array(); + } + } + drupal_add_js( $settings, 'setting' ); + } + + $added = TRUE; + } +} + +/** + * hook_popups + * + * This implements hook_popups, defined in popuppage_get_popups. + * It adds page-in-popup behavior to the core admin pages. + * See the comments in popuppage_add_popups for explination of the options. + * + * @return: Array of link selectors to apply popup behavior to. + * Keyed by path or form_id. + */ +function popuppage_popups() { +// $operations = preg_replace('/[\W]+/', '-', strtolower(t('Operations'))); + + return array( + 'admin/build/block' => array( // Blocks admin page. + '#tabs-wrapper a[href$=admin/build/block/add]', // Add Block + '#blocks a[href~=admin/build/block/configure]', // configure + '#blocks a[href~=admin/build/block/delete]', // delete + ), + 'admin/build/path' => array( // URL aliases admin page. + '#tabs-wrapper a[href$=admin/build/path/add]', // Add alias + 'td:nth-child(3) a[href~=admin/build/path/edit]', // edit alias + 'td:nth-child(4) a[href~=admin/build/path/delete]', // delete alias + ), + 'admin/content/taxonomy' => array( // Taxonomy admin page. + // TODO: If there are not more than one items to start with, d-n-d files aren't loaded into page. + // This causes trouble when the 2nd item is added, no d-n-d. + // Might be bug in taxonomy table building (or at least inconsistancy). + '#tabs-wrapper a[href$=admin/content/taxonomy/add/vocabulary]' => array( // Add vocabulary + 'additionalJavascript' => array('misc/tabledrag.js'), + ), + '#taxonomy-overview-vocabularies td a:contains('. t('edit vocabulary') .')', // edit vocabulary + '#taxonomy-overview-vocabularies td a:contains('. t('list terms') .')' => array( // list terms + 'noReload' => true, + 'additionalJavascript' => array('misc/tabledrag.js'), + ), + '#taxonomy-overview-vocabularies td a:contains('. t('add terms') .')' => array( // add terms + 'noReload' => true, + 'additionalJavascript' => array('misc/collapse.js'), + ), + ), + 'admin/content/types' => array( // Content Type admin page + '#tabs-wrapper a[href$=admin/content/types/add]' => array( // Add content type + 'additionalJavascript' => array('misc/collapse.js'), + ), + 'table td:nth-child(4) a, table td:nth-child(5) a' // edit, delete + ), + 'admin/content/node' => array( // Existing Content admin page + '#node-admin-content td a:contains('. t('edit') .')' => array( // edit + 'additionalJavascript' => array('misc/collapse.js'), + // TODO: teaser.js not working: Drupal.settings.teaser has no properties + // 'additionalJavascript' => array('misc/collapse.js', 'misc/teaser.js'), + ) + ), + 'page_node_form' => array( // Node edit form + 'a[href$=filter/tips]' => array( // Fixes insane "More information..." link + 'noReload' => true, + //TODO 'addCloseButton' => true - just an idea. + ) + ), + 'admin/content/comment' => array( // Comments admin page. + 'table td:nth-child(2) a' => array( // view (TODO: popup too small) + 'noReload' => true, + 'additionalCss' => array(), //TODO: needs some custom css to get #comments to stay in the popup (no -25px margin). + ), + '#comment-admin-overview td a:contains('. t('edit') .')' => array( // edit + 'additionalJavascript' => array('misc/collapse.js'), + ), + ), + 'admin/user/rules' => array( // Access rules admin page. + '#tabs-wrapper a[href$=admin/user/rules/add]', // Add rule + 'table td:nth-child(4) a, table td:nth-child(5) a', // edit, delete + '#tabs-wrapper a[href$=/admin/user/rules/check]' => array( // Check rule + 'noReload' => true, + ), + ), + 'admin/user/user' => array( // Manage all users admin page. + //Add user (TODO: Can't test, keeps crashing apache!) + '#tabs-wrapper a[href$=admin/user/user/create]' => array( + // TODO: "translate has no properties" user.js line 16. + 'additionalJavascript' => array(drupal_get_path('module', 'user') .'/user.js'), + ), + '#user-admin-account td:nth-child(2) a' => array( // View the user + 'noReload' => true, + ), + + ), + 'menu_overview_form' => array( // Menu admin form. + // Add Item, , edit, delete + '#tabs-wrapper a:eq(1), table#menu-overview td:nth-child(5) a, table#menu-overview td:nth-child(6) a', + '#tabs-wrapper a:eq(2)' => array( // Edit menu: update just page title. + 'updateTitle' => true, + 'noReload' => true, + ), + ), + + // CCK - Manage fields page. + 'content_admin_field_overview_form' => array( + 'div#tabs-wrapper a:eq(0)' => array( // Edit + 'updateTitle' => true, + 'noReload' => true, + ), + 'div#tabs-wrapper a:eq(2)' => array( // Display fields + 'noReload' => true, + ), + 'div#tabs-wrapper a:eq(3), div#tabs-wrapper a:eq(4)', // Add field, Add group + 'table td:nth-child(5) a' => array( // configure + 'singleRow' => array( 'td:eq(0)' ), // Just update the row (still expiremental). + ), + 'table td:nth-child(6) a', // remove + ), + ); +} + +// ************************************************************************** +// ADMIN SETTINGS ********************************************************* +// ************************************************************************** + +function popuppage_admin_settings() { + drupal_set_title("Popups Settings"); + $form = array(); + + $form['popuppage_title_selector'] = array( + '#type' => 'textfield', + '#title' => t('Title Selector'), + '#default_value' => variable_get('popuppage_title_selector', 'div.left-corner > h2:eq(0)'), + '#description' => t("jQuery selector to define the page's title on your Admin theme."), + ); + $form['popuppage_content_selector'] = array( + '#type' => 'textfield', + '#title' => t('Content Selector'), + '#default_value' => variable_get('popuppage_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); +} Index: misc/popupapi.js =================================================================== RCS file: misc/popupapi.js diff -N misc/popupapi.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ misc/popupapi.js 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,462 @@ +// $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.popups = function(context) { + $body = $('body'); + if(!$body.hasClass('popups-processed')) { + $(document).bind('keydown', Drupal.popups.keyHandle); + $body.addClass('popups-processed'); + } + Drupal.popups.attach(context, 'a.popup', {}); + Drupal.popups.attach(context, 'a.popup-form', {updatePage: true}); + Drupal.popups.attach(context, 'a.popup-nonmodal', {nonModal: true}); +}; + +/** + * Create the popups object/namespace. + */ +Drupal.popups = 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.popups.attach = function(context, selector, options) { + $(selector, context).not('.popups-processed').each( function() { + $(this).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 + 'popuppage/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.popups.savePage(a, options)}}, + 'popups_submit': {title: json.buttons.submit, func: function(){Drupal.popups.removePopup(); Drupal.popups.openPath(a, options)}}, + 'popups_cancel': {title: json.buttons.cancel, func: Drupal.popups.close} + }; + Drupal.popups.open(json.title, json.content, buttons); + }); + return false; + } + else { + return Drupal.popups.openPath(a, options); + } + }); + $(this).addClass('popups-processed'); + }); +}; + + +/** + * Generic dialog builder. + */ +Drupal.popups.open = function(title, body, buttons) { + + Drupal.popups.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); +// $overlay.before( $popup ); // Add the popup to the DOM. + $('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 ); + } + } + } + $('#popups-close').click( Drupal.popups.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.popups.scrollLeft(); + +// var top; + // Get popups'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.popups.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.popups.removeLoading(); // Remove the loading img. + + return false; +}; + +/** + * Simple popup that functions like the browser's alert box. + */ +Drupal.popups.message = function(message, body) { + body = body || ''; + var buttons = { + 'popups_ok': { title: Drupal.t('OK'), func: Drupal.popups.close } + }; + Drupal.popups.open( message, body, buttons ); +}; + +/** + * Handle any special keys when popup is active. + */ +Drupal.popups.keyHandle = function(e) { + if (!e) { + e = window.event; + } + switch (e.keyCode) { + case 27: // esc + Drupal.popups.close(); + return false; + case 191: // '?' key, show help. + if (e.shiftKey && e.ctrlKey) { +// console.log("Show Help"); + var $help = $('a.popup.more-help'); + if ($help.size()) { + $help.click(); + } + else { + Drupal.popups.message(Drupal.t("Sorry, there is no additional help for this page")); + } + } + default: // all other keys +// console.log( e.keyCode ); +// return true; + } +}; + +/***************************************************************************** + * Appearence Functions (overlay, loading graphic, remove popup) ********* + *****************************************************************************/ + +Drupal.popups.removePopup = function() { + $('#popups').fadeOut('fast', function() { + $('#popups').remove(); + }); +}; + +Drupal.popups.addOverlay = function() { + var $overlay = $('#popups-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.popups.close); + $body.prepend($overlay); + } +// return $overlay; +}; + +//Drupal.popups.getOverlay = function() { +// return $('#popups-overlay'); +//}; + +Drupal.popups.removeOverlay = function() { + $('#popups-overlay').fadeOut('fast', function() { + $('#popups-overlay').remove(); + }); +}; + +Drupal.popups.addLoading = function() { + var $loading = $('#popups-loading'); + if (!$loading.size()) { // Overlay does not already exist, so create it. + var waitImageSize = 100; + var left = ($(window).width() / 2) - (waitImageSize / 2) + Drupal.popups.scrollLeft();; + var top = ($(window).height() / 2) - (waitImageSize / 2) + Drupal.popups.scrollTop();; + $loading = $( Drupal.theme('popupsLoading', left, top) ); + $('body').prepend($loading); + } +}; + +Drupal.popups.removeLoading = function() { + $('#popups-loading').remove(); +}; + +/** + * Remove everything. + */ +Drupal.popups.close = function() { + Drupal.popups.removePopup(); + Drupal.popups.removeLoading(); + Drupal.popups.removeOverlay(); +}; + +/** + * Set the focus on the popup to the first visible form element, or the first button, or the close link. + */ +Drupal.popups.refocus = function() { + $focus = $('#popups input:visible:eq(0)') + if (!focus) { + $focus = $('#popups-close'); // Doesn't seem to work. + } + $focus.focus() +}; + +/**************************************************************************** + * Theme Functions ******************************************************** + ****************************************************************************/ + +Drupal.theme.prototype.popupsLoading = function(left, top) { + var loading = '