/[drupal]/contributions/modules/popups/popups.js
ViewVC logotype

Contents of /contributions/modules/popups/popups.js

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.9 - (show annotations) (download) (as text)
Thu Mar 6 20:32:50 2008 UTC (20 months, 3 weeks ago) by starbow
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-beta3, HEAD
Branch point for: DRUPAL-6--1, DRUPAL-6--2, DRUPAL-7--1, DRUPAL-5
Changes since 1.8: +391 -303 lines
File MIME type: text/javascript
Backport of the initial D7 patches.
Use json as the communication protocol.
Multi-form flow.
1 // $Id:$
2
3 /**
4 * Popup Modal Dialog API
5 *
6 * Provide an API for building and displaying JavaScript, in-page, popup modal dialogs.
7 * Modality is provided by a fixed, semi-opaque div, positioned in front of the page contents
8 * TODO: I believe fixed positioning is not supported in IE6.
9 * The dialog itself is positioned absolutely, which allows for scrolling if the dialog is bigger than the page.
10 * Also, the dialog is created off-screen (left: -9999px) to allow for verticle centering without flicker.
11 *
12 * Here is an example of using this API to create a dialog:
13 *
14 * Drupal.popups.prototype.open_unsaved = function( a ) {
15 * body = Drupal.t("There are unsaved changes on this page.");
16 * buttons = {
17 * 'popups_save': { title: Drupal.t('Save Changes'), func: function(){$('#edit-submit').click()} },
18 * 'popups_submit': { title: Drupal.t('Discard Changes and Continue'), func: function(){window.location = a.href} },
19 * 'popups_cancel': { title: Drupal.t('Cancel'), func: this.close }
20 * };
21 * this.open( "Warning - Please Confirm", body, buttons );
22 * return false;
23 * };
24 */
25
26
27 /**
28 * Create the popups object, and set any defaults.
29 */
30 Drupal.popups = function() {
31 this.default_width = 600;
32 };
33 /**
34 * Generic dialog builder.
35 */
36 Drupal.popups.prototype.open = function( title, body, buttons, width ) {
37 if( !width ) {
38 width = this.default_width;
39 }
40
41 var $overlay = Drupal.popups.get_overlay();
42 if ($overlay.size() == 0) {
43 // overlay does not exist yet so create a new one.
44 $overlay = Drupal.popups.add_overlay();
45 }
46
47 // center on the screen, add in offsets if the window has been scrolled
48 var overlay_width = $overlay.width();
49 var overlay_height = $overlay.height();
50 var left = (overlay_width / 2) - (width / 2) + Drupal.popups.f_scrollLeft();
51 var top = 0; // we will reposition after adding to page
52 // Start with dialog off the side. Making it invisible causes flash.
53 var popup = Drupal.theme('popupsDialog', -9999, top, width, title, body, buttons);
54
55 $overlay.before( popup ); // Dialog is added, but still hidden.
56
57 // Adding button functions
58 if (buttons) {
59 for (var id in buttons) {
60 if (buttons[id]) { // to make jslint happy.
61 var func = buttons[id].func;
62 $('#'+id).click( func );
63 }
64 }
65 }
66 $('#popups-close').click( Drupal.popups.close );
67
68 // Get popups's height on the page, and center vertically before showing.
69 var popups_height = $('#popups').height(); // Causes flash if not visible!
70 if (popups_height < overlay_height) {
71 top = (overlay_height / 2) - (popups_height / 2) + Drupal.popups.f_scrollTop();
72 }
73 else { // popups is too big to fit on screen
74 top = 20 + Drupal.popups.f_scrollTop();
75 }
76
77 $('#popups').css('top', top).css('left', left); // Make the popup visible.
78
79 this.refocus(); // TODO: capture the focus when it leaves the dialog.
80 Drupal.popups.remove_loading(); // Remove the loading img.
81
82 return false;
83 };
84
85 Drupal.popups.message = function (message, body) {
86 var popup = new Drupal.popups();
87 var buttons = {
88 'popups_ok': { title: Drupal.t('OK'), func: Drupal.popups.close }
89 };
90 popup.open( message, body, buttons );
91 };
92
93 /************************************************************************************
94 * Theme Functions ******************************************************************
95 */
96
97 Drupal.theme.prototype.popupsLoading = function (left, top) {
98 var loading = '<div id="popups-loading">';
99 loading += '<div style="left:' + left +'px; top:' + top +'px;">';
100 // TODO - remove hardcoded path to image.
101 loading += '<img src="'+ Drupal.settings.basePath +'/sites/all/modules/popups/ajax-loader.gif" />';
102 // loading += '<img src="'+ Drupal.settings.basePath +'/misc/ajax-loader.gif" />';
103 loading += '</div></div>';
104 return loading;
105 };
106
107 Drupal.theme.prototype.popupsOverlay = function () {
108 return '<div id="popups-overlay"></div>';
109 };
110
111 Drupal.theme.prototype.popupsDialog = function(left, top, width, title, body, buttons) {
112 var popups = '<div id="popups" style="left:'+ left +'px; top:'+ top +'px;';
113 popups += ' width:'+ width +'px;';
114 popups += '" >';
115 popups += '<div id="popups-title">'+
116 ' <div id="popups-close"><a>close [X]</a></div>'+
117 ' <div class="title">' + title +'</div>'+
118 ' <div class="clear"></div>'+
119 '</div>';
120 if (body) {
121 popups += '<div id="popups-body">' + body +'</div>';
122 }
123 popups += ' <div id="popups-buttons">';
124
125 for ( var id in buttons) {
126 if (buttons[id]) {
127 var button = buttons[id];
128 popups += '<input type="button" value="'+ button.title +'" id="'+ id +'" />';
129 }
130 }
131
132 popups += ' </div>'; // end buttons
133 popups += '</div>'; // close popups
134 return popups;
135 };
136
137 /***************************************************************************************
138 * Appearence Functions (overlay, loading graphic, remove popup) ***********************
139 **************************************************************************************/
140
141 Drupal.popups.add_overlay = function() {
142 var $overlay = $( Drupal.theme('popupsOverlay') );
143
144 $overlay.css( 'opacity', '0.4' ); // for ie (?)
145 $('body').prepend( $overlay );
146 return $overlay;
147 };
148
149 Drupal.popups.get_overlay = function() {
150 return $('#popups-overlay');
151 };
152
153 Drupal.popups.remove_overlay = function() {
154 $('#popups-overlay').remove();
155 };
156
157 Drupal.popups.add_loading = function() {
158 var $overlay = Drupal.popups.get_overlay();
159 var wait_image_size = 100;
160 var left = ( $overlay.width() / 2 ) - ( wait_image_size / 2 );
161 var top = ( $overlay.height() / 2 ) - ( wait_image_size / 2 );
162 var $loading = $( Drupal.theme('popupsLoading', left, top) );
163 $('body').prepend($loading);
164 };
165
166 Drupal.popups.remove_loading = function() {
167 $('#popups-loading').remove();
168 };
169
170 Drupal.popups.remove_popup = function() {
171 $('#popups').remove();
172 };
173
174 /**
175 * Remove everything.
176 */
177 Drupal.popups.close = function() {
178 Drupal.popups.remove_popup();
179 Drupal.popups.remove_loading();
180 Drupal.popups.remove_overlay();
181 };
182
183 /**
184 * Set the focus on the popup to the first visible form element, or the first button, or the close link.
185 */
186 Drupal.popups.prototype.refocus = function() {
187 $focus = $('#popups input:visible:eq(0)')
188 if (!focus) {
189 $focus = $('#popups-close');
190 }
191 $focus.focus()
192 }
193
194 /****************************************************************************
195 * Utility functions
196 ****************************************************************************/
197
198 /**
199 * Break the URL down into it's componant parts.
200 * Might be worth turning into a core function.
201 * @param
202 * String version of URL.
203 * @return
204 * Object version of URL.
205 */
206 Drupal.popups.parseUrl = function(url) {
207 var params = [];
208 var temp = url.split('#');
209 url = temp[0];
210 var fragment = temp[1];
211
212 // Convert the existing parameter string into an array.
213 if (url.indexOf('?') > -1) {
214 temp = url.split('?');
215 url = temp[0];
216 if (temp[1]) {
217 params = temp[1].split('&');
218 }
219 }
220 return { 'url': url, 'params': params, 'fragment': fragment};
221 }
222
223 /**
224 * Rebuild the url string from the obj returned by parseUrl.
225 * @param
226 * Object version of URL.
227 * @return
228 * String version of URL.
229 */
230 Drupal.popups.buildUrl = function(u) {
231 url = u.url + '?' + u.params.join('&');
232 if (u.fragment) {
233 url += '#' + u.fragment;
234 }
235 return url;
236 }
237
238 /***********************************************************************************************
239 * Utility functions taken from http://www.softcomplex.com/docs/get_window_size_and_scrollbar_position.html
240 */
241
242 Drupal.popups.f_scrollLeft = function() {
243 return Drupal.popups.f_filterResults (
244 window.pageXOffset ? window.pageXOffset : 0,
245 document.documentElement ? document.documentElement.scrollLeft : 0,
246 document.body ? document.body.scrollLeft : 0 );
247 };
248
249 Drupal.popups.f_scrollTop = function() {
250 return Drupal.popups.f_filterResults (
251 window.pageYOffset ? window.pageYOffset : 0,
252 document.documentElement ? document.documentElement.scrollTop : 0,
253 document.body ? document.body.scrollTop : 0 );
254 };
255
256 Drupal.popups.f_filterResults = function(n_win, n_docel, n_body) {
257 var n_result = n_win ? n_win : 0;
258 if (n_docel && (!n_result || (n_result > n_docel))) {
259 n_result = n_docel;
260 }
261 return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
262 };
263
264 /***************************************************************************************
265 * Page-in-Popup Behavior
266 **************************************************************************************/
267
268 /**
269 * This part uses the Popup API to build dialogs that show the content from a Drupal page.
270 *
271 * It is assumed that the pages being returned from the Drupal server will be XML
272 * and have the following format:
273 *
274 * <popup>
275 * <title>Page Title</title>
276 * <messages>Error, warning and status messages, as HTML</messages>
277 * <path>The page's URL</path>
278 * <content>The content of the page, as HTML</content>
279 * </popup>
280 */
281
282 /**
283 * Attach the popup bevior to the all the requested links on the page.
284 *
285 * @param context: The jQuery object to apply the behaviors to.
286 */
287 Drupal.behaviors.popups = function(context) {
288 var popups = new Drupal.popups();
289
290 // Add the popup-link-in-dialog behavior to links defined in Drupal.settings.popups.links array.
291 // TODO: how to handle popups-in-popups?
292 if (Drupal.settings.popups) {
293 for (var link in Drupal.settings.popups.links) {
294 var options = Drupal.settings.popups.links[link];
295 popups.attach(context, link, options); // Needs to be seperate function for closure.
296 }
297 }
298
299 // $('a.popups', context).click( function() {return popups.open_path(this, {});} );
300 popups.attach(context, 'a.popups', {});
301 };
302
303 /**
304 * Attach the popup behavior to a particular link.
305 *
306 * @param link - link that was clicked.
307 * @param options - options associated with the link.
308 */
309 Drupal.popups.prototype.attach = function(context, link, options) {
310 var popups = this;
311 $(link, context).not('.popups-processed').each( function() {
312 $(this).click( function(e){
313 var a = this;
314 // If the option is distructive, check if the page is already modified, and offer to save.
315 var page_is_dirty = $('span.tabledrag-changed').size() > 0;
316 var will_modify_original = !options.noReload && !options.singleRow;
317 if( page_is_dirty && will_modify_original ) {
318 // The user will lose modifications, so popup dialog offering to save current state.
319 var body = Drupal.t("There are unsaved changes on this page, which you will lose if you continue.");
320 var buttons = {
321 'popups_save': {title: Drupal.t('Save Changes'), func: function(){popups.save_page(a, options)}},
322 'popups_submit': {title: Drupal.t('Continue'), func: function(){Drupal.popups.close(); popups.open_path(a, options)}},
323 'popups_cancel': {title: Drupal.t('Cancel'), func: Drupal.popups.close}
324 };
325 return popups.open( Drupal.t('Warning: Please Confirm'), body, buttons );
326 }
327 else {
328 return popups.open_path(a, options);
329 }
330 });
331 $(this).addClass('popups-processed');
332 });
333
334 };
335
336
337 /**
338 * Deal with the param string of a url to make the response popup friendly.
339 * Add 'page_override=popup' param.
340 *
341 * @param url
342 * String: The original url.
343 * @return url
344 * String: The url with the corrected parameters.
345 */
346 Drupal.popups.prepUrl = function(url) {
347 var u = Drupal.popups.parseUrl(url);
348 // Add our param to the filtered array of existing params.
349 if (jQuery.inArray('page_override=popup', u.params) == -1) {
350 u.params.unshift('page_override=popup');
351 }
352 // Rebuild the url with the new param, the old params and the old fragment.
353 return Drupal.popups.buildUrl(u);
354 }
355
356 /**
357 * Use Ajax to open the link in a popup window.
358 *
359 * @param a - link that was clicked.
360 * @param options - options associated with the link.
361 */
362 Drupal.popups.prototype.open_path = function( a, options ) {
363 var popup = this;
364 // let the user know something is happening
365 $('body').css("cursor", "wait");
366 var $overlay = Drupal.popups.add_overlay();
367 Drupal.popups.add_loading();
368
369 // var type = options.type ? options.type : 'json'; // Default to json.
370 var url = Drupal.popups.prepUrl(a.href);
371 // if (type=='html') { // Special handling for html.
372 // $.get(url, function(data) {
373 // var $response = $('<div></div>');
374 // $response.html(data);
375 // var body = $response.html();
376 // popup.open_content(document.title, body, options, a);
377 // $('body').css("cursor", "auto"); // Return the cursor to normal state.
378 // });
379 // }
380 // else { // It is a json object from Drupal.
381 $.getJSON(url, function(json) {
382 popup.open_content(json.title, json.messages + json.content, options, a);
383 $('body').css("cursor", "auto"); // Return the cursor to normal state.
384 });
385 // }
386 /*
387 $.get(url, function(data) {
388 var $data = $(data);
389 if ($data.size() == 1) { // Expecting XML with single root.
390 var title = $data.find('title').text();
391 var messages = $data.find('messages').text();
392 var content = messages + $data.find('content').text();
393 popup.open_content(title, content, options, a);
394 $('body').css("cursor", "auto"); // Return the cursor to normal state.
395 }
396 else { // Not XML, so show entire HTML page in popup.
397 // Filter the html - There must be a there a better way?
398 var $response = $('<div></div>');
399 $response.html(data);
400 var body = $response.html();
401 // var msg = 'Messages: ' + $('.messages', $response).text();
402 // Drupal.popups.message('Error: Bad response.', msg);
403 // Drupal.popups.remove_loading();
404 var title = document.title;
405 popup.open_content(title, body, options, a);
406 $('body').css("cursor", "auto"); // Return the cursor to normal state.
407 }
408 });
409 */
410 return false;
411 };
412
413
414 Drupal.popups.prototype.open_content = function(title, content, options, a) {
415 this.open(title, content);
416 // Add behaviors to content in popup.
417 // TODO: d-n-d: need to click to let go of selection.
418 Drupal.attachBehaviors($('#popups-body'));
419 // Adding collapse moves focus.
420 this.refocus();
421
422 // If the popup contains a form, capture submits.
423 var $form = $('form', '#popups-body');
424 $form.ajaxForm({
425 dataType: 'json',
426 beforeSubmit: Drupal.popups.beforeSubmit,
427 success: function(response, status) { Drupal.popups.formSuccess(response, options, a) },
428 });
429 }
430
431 /**
432 * Do before the form in the popup is submitted.
433 *
434 */
435 Drupal.popups.beforeSubmit = function(form_data, $form, options) {
436 Drupal.popups.remove_popup(); // Remove just the dialog, but not the overlay.
437 Drupal.popups.add_loading();
438 // Send the original page back to Drupal with a flag to return the form results unthemed.
439 options.url = Drupal.popups.prepUrl(options.url);
440 };
441
442 /**
443 * The form in the popup was successfully submitted
444 * Update the originating page.
445 * Show any messages in a popup (TODO - make this a configurable option).
446 *
447 * @param response - specially formated page contents from server.
448 * @param options - hash of per link options.
449 * @param a - the link that was clicked.
450 */
451 Drupal.popups.formSuccess = function (response, options, a) {
452 var $data = $(response);
453 if ($data.size() > 1) { // Bad html response, show an error message.
454 var $response = $('<div></div>');
455 $response.html( response );
456 var msg = 'Messages: ' + $('.messages', $response).text();
457 Drupal.popups.message('Error: Bad response.', msg);
458 Drupal.popups.remove_loading();
459 }
460 else { // Got a good response back from the server.
461 // Get into common format for testing.
462 var data = {
463 title: response.title, // $data.find('title').text();
464 messages: response.messages, // $data.find('messages').text();
465 path: response.path, // $data.find('path').text();
466 content: response.content // $data.find('content').text()
467 };
468
469 // var messages = $data.find('messages').text();
470
471 // Are we at an end point, or just moving from one popup to another?
472 // var path = $data.find('path').text();
473 if (!location.href.match(data.path)) { // Not done yet, so show results in new popup.
474 // var title = $data.find('title').text();
475 // var content = $data.find('content').text();
476 Drupal.popups.remove_loading();
477 var popups = new Drupal.popups();
478 popups.open_content(data.title, data.messages + data.content, options, a);
479 }
480 else { // Done, so show messages in dialog and embed the results in the original page.
481 if (data.messages) {
482 Drupal.popups.message(data.messages);
483 // Also insert the message into the page above the content.
484 // Might not be the standard spot, but it is the easiest to find.
485 var $next = $(Drupal.settings.popups.defaultTargetSelector);
486 $next.parent().find('div.messages').remove(); // Remove the current messages.
487 $next.before(data.messages);
488 }
489
490 // Just update a single row out of a table (still expiremental).
491 // Loop through, with special case for first element.
492 if (options.singleRow) {
493 var href = $(a).attr('href');
494 var selector = 'table a[href=' + href + ']';
495 var $new_row = $data.find(selector).parents('tr'); // new tr
496 var $target_row = $(selector).parents('tr'); // target tr.
497 for (var i in options.singleRow) {
498 var col = options.singleRow[i];
499 $new_row.find(col).contents().not('div.indentation').wrapAll('<div id="newvalue"/>');
500 $target_row.find(col).contents()
501 .not('a.tabledrag-handle').not('span.warning').not('div.indentation')
502 .wrapAll('<div id="killme"/>');
503 $('#killme').replaceWith( $new_row.find('#newvalue').html() );
504 }
505 }
506 // Update the entire content area (defined by 'target selector').
507 else if (!options.noReload) {
508 var target = options.targetSelector;
509 if (!target) {
510 target = Drupal.settings.popups.defaultTargetSelector;
511 }
512
513 // Remove page_override=popup param from form's action.
514 var action = $data.find('form').attr('action');
515 if (action) {
516 action = Drupal.popups.parseUrl(action);
517 action.params = jQuery.grep(action.params, function(n, i){
518 return n != 'page_override=popup';
519 });
520 $data.find('form').attr( 'action', Drupal.popups.buildUrl(action) );
521 }
522
523 // Update the original page.
524 // var content = $data.find('content').text();
525 var $c = $(target).html(data.content); // Inject the new content into the page.
526 Drupal.attachBehaviors($c);
527 }
528
529 // Update the title of the page.
530 if (options.updateTitle) {
531 // var title = $data.find('title').text();
532 $(Drupal.settings.popups.defaultTitleSelector).html(data.title);
533 document.title = data.title; // Also update the browser page title (TODO: include site name?).
534 }
535
536 // Done with changes to the original page, remove effects.
537 Drupal.popups.remove_loading();
538 if (!data.messages) {
539 // If there is not a messages popup remove the overlay.
540 Drupal.popups.remove_overlay();
541 }
542 } // End of updating original page.
543 } // End of good response.
544 };
545
546 /**
547 * Submit the page and reload the results, before popping up the real dialog.
548 *
549 * @param a - link that was clicked.
550 * @param options - options associated with the link.
551 */
552 Drupal.popups.prototype.save_page = function(a, options) {
553 var popups = this;
554 // TODO - what if clicking on link with option['targetSelector']?
555 var target = Drupal.settings.popups.defaultTargetSelector;
556 var $form = $('form', target);
557 var ajaxOptions = {
558 dataType: 'json',
559 beforeSubmit: Drupal.popups.beforeSubmit,
560 success: function(response, status) { // Sync up the current page contents with the submit.
561 // var $data = $(response);
562 // var content = $data.find('content').text();
563 var $c = $(target).html(response.content); // Inject the new content into the page.
564 Drupal.attachBehaviors($c);
565 Drupal.popups.close();
566 // The form has been saved, the page reloaded, now safe to show the link in a popup.
567 popups.open_path(a, options);
568 }
569 };
570 $form.ajaxSubmit( ajaxOptions ); // Submit the form.
571 };
572

  ViewVC Help
Powered by ViewVC 1.1.2