comparison popups.js @ 1:4215c43e74eb

Popups: Mise à jour en version alpha6
author Franck Deroche <franck@defr.org>
date Fri, 31 Dec 2010 13:44:00 +0100
parents 76f9b43738f2
children 09e998f65556
comparison
equal deleted inserted replaced
0:76f9b43738f2 1:4215c43e74eb
1 // $Id: popups.js,v 1.9.8.12 2009/03/21 00:57:15 starbow Exp $ 1 // $Id: popups.js,v 1.9.8.19 2010/12/10 02:51:17 drewish Exp $
2 2
3 /** 3 /**
4 * Popup Modal Dialog API 4 * Popup Modal Dialog API
5 * 5 *
6 * Provide an API for building and displaying JavaScript, in-page, popups modal dialogs. 6 * Provide an API for building and displaying JavaScript, in-page, popups modal dialogs.
25 * The jQuery object to apply the behaviors to. 25 * The jQuery object to apply the behaviors to.
26 */ 26 */
27 27
28 Drupal.behaviors.popups = function(context) { 28 Drupal.behaviors.popups = function(context) {
29 Popups.saveSettings(); 29 Popups.saveSettings();
30 30
31 var $body = $('body'); 31 var $body = $('body');
32 if(!$body.hasClass('popups-processed')) { 32 if(!$body.hasClass('popups-processed')) {
33 $body.addClass('popups-processed'); 33 $body.addClass('popups-processed');
34 $(document).bind('keydown', Popups.keyHandle); 34 $(document).bind('keydown', Popups.keyHandle);
35 var $popit = $('#popit'); 35 var $popit = $('#popit');
36 if ($popit.length) { 36 if ($popit.length) {
37 $popit.remove(); 37 $popit.remove();
38 Popups.message($popit.html()); 38 Popups.message($popit.html());
39 } 39 }
40 } 40
41 41 // Make note of all the CSS and JS on the page so when we load a popup we
42 // don't try to add them a second time.
43 $('link[rel="stylesheet"][href]').each(function(i, v) {
44 Popups.originalCSS[$(this).attr('href').replace(/^(\/.+)\?\w$/, '$1')] = 1;
45 });
46 if (Drupal.settings.popups && Drupal.settings.popups.originalCSS) {
47 $.extend(Popups.originalCSS, Drupal.settings.popups.originalCSS);
48 }
49 $('script[src]').each(function(i, v) {
50 Popups.originalJS[$(this).attr('src').replace(/^(\/.+)\?\w$/, '$1')] = 1;
51 });
52 if (Drupal.settings.popups && Drupal.settings.popups.originalJS) {
53 $.extend(Popups.originalJS, Drupal.settings.popups.originalJS);
54 }
55 }
56
42 // Add the popups-link-in-dialog behavior to links defined in Drupal.settings.popups.links array. 57 // Add the popups-link-in-dialog behavior to links defined in Drupal.settings.popups.links array.
43 // Get these from current Drupal.settings, not Popups.originalSettings, as each page has it's own hooks. 58 // Get these from current Drupal.settings, not Popups.originalSettings, as each page has it's own hooks.
44 if (Drupal.settings.popups && Drupal.settings.popups.links) { 59 if (Drupal.settings.popups && Drupal.settings.popups.links) {
45 jQuery.each(Drupal.settings.popups.links, function (link, options) { 60 jQuery.each(Drupal.settings.popups.links, function (link, options) {
46 Popups.attach(context, link, Popups.options(options)); 61 Popups.attach(context, link, Popups.options(options));
47 }); 62 });
48 } 63 }
49 64
50 Popups.attach(context, '.popups', Popups.options({updateMethod: 'none'})); 65 Popups.attach(context, '.popups', Popups.options({updateMethod: 'none'}));
51 Popups.attach(context, '.popups-form', Popups.options({updateMethod: 'ajax'})); // ajax reload. 66 Popups.attach(context, '.popups-form', Popups.options({updateMethod: 'ajax'})); // ajax reload.
52 Popups.attach(context, '.popups-form-reload', Popups.options({updateMethod: 'reload'})); // whole page reload. 67 Popups.attach(context, '.popups-form-reload', Popups.options({updateMethod: 'reload'})); // whole page reload.
53 Popups.attach(context, '.popups-form-noupdate', Popups.options({updateMethod: 'none'})); // no reload at all. 68 Popups.attach(context, '.popups-form-noupdate', Popups.options({updateMethod: 'none'})); // no reload at all.
54 }; 69 };
55 70
56 // *************************************************************************** 71 // ***************************************************************************
57 // Popups Namespace ********************************************************** 72 // Popups Namespace **********************************************************
65 Popups = function(){}; 80 Popups = function(){};
66 81
67 /** 82 /**
68 * Static variables in the Popups namespace. 83 * Static variables in the Popups namespace.
69 */ 84 */
70 Popups.popupStack = []; 85 Popups.popupStack = [];
71 Popups.addedCSS = []; 86 Popups.addedCSS = {};
72 Popups.addedJS = []; 87 Popups.addedJS = {};
88 Popups.originalCSS = {};
89 Popups.originalJS = {};
73 Popups.originalSettings = null; // The initial popup options of the page. 90 Popups.originalSettings = null; // The initial popup options of the page.
74 /** 91 /**
75 * Each popup object gets it's own set of options. 92 * Each popup object gets it's own set of options.
76 * These are the defaults. 93 * These are the defaults.
77 */ 94 */
78 Popups.defaultOptions = { 95 Popups.defaultOptions = {
79 doneTest: null, // null, *path*, *regexp*. how do we know when a multiform flow is done? 96 doneTest: null, // null, *path*, *regexp*. how do we know when a multiform flow is done?
80 updateMethod: 'ajax', // none, ajax, reload, *callback* 97 updateMethod: 'ajax', // none, ajax, reload, *callback*
81 updateSource: 'initial', // initial, final. Only used if updateMethod != none. 98 updateSource: 'initial', // initial, final. Only used if updateMethod != none.
82 href: null, 99 onUpdate: '', // Only used if updateMethod == callback.
100 href: null,
83 width: null, // Override the width specified in the css. 101 width: null, // Override the width specified in the css.
84 targetSelectors: null, // Hash of jQuery selectors that define the content to be swapped out. 102 targetSelectors: null, // Hash of jQuery selectors that define the content to be swapped out.
85 titleSelectors: null, // Array of jQuery selectors to place the new page title. 103 titleSelectors: null, // Array of jQuery selectors to place the new page title.
86 reloadOnError: false, // Force the entire page to reload if the popup href is unaccessable. 104 reloadOnError: false, // Force the entire page to reload if the popup href is unaccessable.
87 noMessage: false, // Don't show drupal_set_message messages. 105 noMessage: false, // Don't show drupal_set_message messages.
88 skipDirtyCheck: false, // If true, this popup will not check for edits on the originating page. 106 skipDirtyCheck: false, // If true, this popup will not check for edits on the originating page.
89 hijackDestination: true // Use the destiination param to force a form submit to return to the originating page. 107 hijackDestination: true // Use the destiination param to force a form submit to return to the originating page.
90 }; 108 };
91 109
92 // *************************************************************************** 110 // ***************************************************************************
93 // Popups.Popup Object ******************************************************* 111 // Popups.Popup Object *******************************************************
94 // *************************************************************************** 112 // ***************************************************************************
96 * A Popup is a single modal dialog. 114 * A Popup is a single modal dialog.
97 * The popup object encapslated all the info about a single popup. 115 * The popup object encapslated all the info about a single popup.
98 */ 116 */
99 Popups.Popup = function() { 117 Popups.Popup = function() {
100 this.id = 'popups-' + Popups.nextCounter(); 118 this.id = 'popups-' + Popups.nextCounter();
101 119
102 // These properties are needed if the popup contains a form that will be ajax submitted. 120 // These properties are needed if the popup contains a form that will be ajax submitted.
103 this.parent = null; // The popup that spawned this one. If parent is null, this popup was spawned by the original page. 121 this.parent = null; // The popup that spawned this one. If parent is null, this popup was spawned by the original page.
104 this.path = null; // If popup is showing content from a url, this is that path. 122 this.path = null; // If popup is showing content from a url, this is that path.
105 this.element = null; // The DOM element that was clicked to launch this popup. 123 this.element = null; // The DOM element that was clicked to launch this popup.
106 this.options = null; // An option array that control how the popup behaves. See Popups.defaultOptions for explainations. 124 this.options = null; // An option array that control how the popup behaves. See Popups.defaultOptions for explainations.
124 return $('#' + this.id + ' .popups-footer'); 142 return $('#' + this.id + ' .popups-footer');
125 }; 143 };
126 144
127 /** 145 /**
128 * Create the jQuery wrapped html at the heart of the popup object. 146 * Create the jQuery wrapped html at the heart of the popup object.
129 * 147 *
130 * @param title 148 * @param title
131 * String 149 * String
132 * @param body 150 * @param body
133 * String/HTML 151 * String/HTML
134 * @param buttons 152 * @param buttons
139 Popups.Popup.prototype.fill = function(title, body, buttons) { 157 Popups.Popup.prototype.fill = function(title, body, buttons) {
140 return $(Drupal.theme('popupDialog', this.id, title, body, buttons)); 158 return $(Drupal.theme('popupDialog', this.id, title, body, buttons));
141 } 159 }
142 160
143 /** 161 /**
144 * Hide the popup by pushing it off to the side. 162 * Hide the popup by pushing it off to the side.
145 * Just making it display:none causes flash in FF2. 163 * Just making it display:none causes flash in FF2.
146 */ 164 */
147 Popups.Popup.prototype.hide = function() { 165 Popups.Popup.prototype.hide = function() {
148 this.$popup().css('left', '-9999px'); 166 this.$popup().css('left', '-9999px');
149 }; 167 };
154 172
155 Popups.Popup.prototype.open = function(title, body, buttons, width){ 173 Popups.Popup.prototype.open = function(title, body, buttons, width){
156 return Popups.open(this, title, body, buttons, width); 174 return Popups.open(this, title, body, buttons, width);
157 }; 175 };
158 176
159 Popups.Popup.prototype.removePopup = function() { 177 Popups.Popup.prototype.removePopup = function() {
160 Popups.removePopup(this); 178 Popups.removePopup(this);
161 }; 179 };
162 180
163 /** 181 /**
164 * Remove everything. 182 * Remove everything.
165 */ 183 */
166 Popups.Popup.prototype.close = function() { 184 Popups.Popup.prototype.close = function() {
181 $focus.focus(); 199 $focus.focus();
182 }; 200 };
183 201
184 /** 202 /**
185 * Return a selector that will find target content on the layer that spawned this popup. 203 * Return a selector that will find target content on the layer that spawned this popup.
186 * This is needed for the popup to do ajax updates. 204 * This is needed for the popup to do ajax updates.
187 */ 205 */
188 Popups.Popup.prototype.targetLayerSelector = function() { 206 Popups.Popup.prototype.targetLayerSelector = function() {
189 if (this.parent === null) { 207 if (this.parent === null) {
190 return 'body'; // Select content in the original page. 208 return 'body'; // Select content in the original page.
191 } 209 }
192 else { 210 else {
193 return '#' + this.parent.id; // Select content in the parent popup. 211 return '#' + this.parent.id; // Select content in the parent popup.
194 } 212 }
195 }; 213 };
196 214
197 /** 215 /**
198 * Determine if we are at an end point of a form flow, or just moving from one popups to another. 216 * Determine if we are at an end point of a form flow, or just moving from one popups to another.
199 * 217 *
200 * @param path 218 * @param path
201 * The path of the page that the form flow has moved to. 219 * The path of the page that the form flow has moved to.
202 * This path is relative to the base_path. 220 * This path is relative to the base_path.
203 * Ex: node/add/story, not http://localhost/drupal6/node/add/story or drupa6/node/add/story. 221 * Ex: node/add/story, not http://localhost/drupal6/node/add/story or drupa6/node/add/story.
204 * @return bool 222 * @return bool
205 */ 223 */
206 Popups.Popup.prototype.isDone = function(path) { 224 Popups.Popup.prototype.isDone = function(path) {
207 // console.log("Doing isDone for popup: " + this.id + ", now at " + path );
208 var done; 225 var done;
209 if (this.options.doneTest) { 226 if (this.options.doneTest) {
210 // Test if we are at the path specified by doneTest. 227 // Test if we are at the path specified by doneTest.
211 done = (path === this.options.doneTest || path.match(this.options.doneTest)); 228 done = (path === this.options.doneTest || path.match(this.options.doneTest));
212 } 229 }
213 else { 230 else {
214 if (this.parent) { 231 if (this.parent) {
215 // Test if we are back to the parent popup's path. 232 // Test if we are back to the parent popup's path.
216 done = (path === this.parent.path); 233 done = (path === this.parent.path);
217 // console.log("Lookin at parent: " + this.parent.path + ". Done = " + done);
218 } 234 }
219 else { 235 else {
220 // Test if we are back to the original page's path. 236 // Test if we are back to the original page's path.
221 done = (path === Popups.originalSettings.popups.originalPath); 237 done = (path === Popups.originalSettings.popups.originalPath);
222 // console.log("Lookin at original page: " + Popups.originalSettings.popups.originalPath + ". Done = " + done); 238 }
223 } 239 };
224 }; 240 return done;
225 return done;
226 }; 241 };
227 242
228 243
229 // *************************************************************************** 244 // ***************************************************************************
230 // Popups Functions ********************************************************** 245 // Popups Functions **********************************************************
231 // *************************************************************************** 246 // ***************************************************************************
232 247
233 /** 248 /**
234 * Test if the param has been set. 249 * Test if the param has been set.
235 * Used to distinguish between a value set to null or false and on not yet unset. 250 * Used to distinguish between a value set to null or false and on not yet unset.
236 */ 251 */
237 Popups.isset = function(v) { 252 Popups.isset = function(v) {
238 return (typeof(v) !== 'undefined'); 253 return (typeof(v) !== 'undefined');
239 }; 254 };
262 return Popups.popupStack.pop(); 277 return Popups.popupStack.pop();
263 }; 278 };
264 279
265 /** 280 /**
266 * Build an options hash from defaults. 281 * Build an options hash from defaults.
267 * 282 *
268 * @param overrides 283 * @param overrides
269 * Hash of values to override the defaults. 284 * Hash of values to override the defaults.
270 */ 285 */
271 Popups.options = function(overrides) { 286 Popups.options = function(overrides) {
272 var defaults = Popups.defaultOptions; 287 var defaults = Popups.defaultOptions;
273 return Popups.overrideOptions(defaults, overrides); 288 return Popups.overrideOptions(defaults, overrides);
274 } 289 }
275 290
276 /** 291 /**
277 * Build an options hash. 292 * Build an options hash.
278 * Also maps deprecated options to current options. 293 * Also maps deprecated options to current options.
279 * 294 *
280 * @param defaults 295 * @param defaults
281 * Hash of default values 296 * Hash of default values
282 * @param overrides 297 * @param overrides
283 * Hash of values to override the defaults with. 298 * Hash of values to override the defaults with.
284 */ 299 */
287 for(var option in defaults) { 302 for(var option in defaults) {
288 var value; 303 var value;
289 if (Popups.isset(overrides[option])) { 304 if (Popups.isset(overrides[option])) {
290 options[option] = overrides[option]; 305 options[option] = overrides[option];
291 } 306 }
292 else { 307 else {
293 options[option] = defaults[option]; 308 options[option] = defaults[option];
294 } 309 }
295 } 310 }
296 // Map deprecated options. 311 // Map deprecated options.
297 if (overrides['noReload'] || overrides['noUpdate']) { 312 if (overrides['noReload'] || overrides['noUpdate']) {
298 options['updateMethod'] = 'none'; 313 options['updateMethod'] = 'none';
299 } 314 }
300 if (overrides['reloadWhenDone']) { 315 if (overrides['reloadWhenDone']) {
301 options['updateMethod'] = 'reload'; 316 options['updateMethod'] = 'reload';
302 } 317 }
303 if (overrides['afterSubmit']) { 318 if (overrides['afterSubmit']) {
304 options['updateMethod'] = 'callback'; 319 options['updateMethod'] = 'callback';
305 options['onUpdate'] = overrides['afterSubmit']; 320 options['onUpdate'] = overrides['afterSubmit'];
306 } 321 }
307 if (overrides['forceReturn']) { 322 if (overrides['forceReturn']) {
308 options['doneTest'] = overrides['forceReturn']; 323 options['doneTest'] = overrides['forceReturn'];
309 } 324 }
310 return options; 325 return options;
311 } 326 }
312 327
313 /** 328 /**
314 * Attach the popups behavior to all elements inside the context that match the selector. 329 * Attach the popups behavior to all elements inside the context that match the selector.
319 * jQuery selector for elements to attach popups behavior to. 334 * jQuery selector for elements to attach popups behavior to.
320 * @param options 335 * @param options
321 * Hash of options associated with these links. 336 * Hash of options associated with these links.
322 */ 337 */
323 Popups.attach = function(context, selector, options) { 338 Popups.attach = function(context, selector, options) {
324 // console.log(options);
325 $(selector, context).not('.popups-processed').each(function() { 339 $(selector, context).not('.popups-processed').each(function() {
326 var $element = $(this); 340 var $element = $(this);
327 341
328 // Mark the element as processed. 342 // Mark the element as processed.
329 $element.addClass('popups-processed'); 343 $element.addClass('popups-processed');
330 344
331 // Append note to link title. 345 // Append note to link title.
332 var title = ''; 346 var title = '';
333 if ($element.attr('title')) { 347 if ($element.attr('title')) {
334 title = $element.attr('title') + ' '; 348 title = $element.attr('title') + ' ';
335 } 349 }
336 title += Drupal.t('[Popup]'); 350 title += Drupal.t('[Popup]');
337 $element.attr('title', title); 351 $element.attr('title', title);
338 352
339 // Attach the on-click popup behavior to the element. 353 // Attach the on-click popup behavior to the element.
340 $element.click(function(event){ 354 $element.click(function(event){
341 return Popups.clickPopupElement(this, options); 355 return Popups.clickPopupElement(this, options);
342 }); 356 });
343 }); 357 });
344 }; 358 };
345 359
346 /** 360 /**
347 * Respond to click by opening a popup. 361 * Respond to click by opening a popup.
348 * 362 *
349 * @param element 363 * @param element
350 * The element that was clicked. 364 * The element that was clicked.
351 * @param options 365 * @param options
352 * Hash of options associated with the element. 366 * Hash of options associated with the element.
353 */ 367 */
354 Popups.clickPopupElement = function(element, options) { 368 Popups.clickPopupElement = function(element, options) {
355 Popups.saveSettings(); 369 Popups.saveSettings();
356 370
357 // If the element contains a on-popups-options attribute, override default options param. 371 // If the element contains a on-popups-options attribute, override default options param.
358 if ($(element).attr('on-popups-options')) { 372 if ($(element).attr('on-popups-options')) {
359 var overrides = Drupal.parseJson($(element).attr('on-popups-options')); 373 var overrides = Drupal.parseJson($(element).attr('on-popups-options'));
360 options = Popups.overrideOptions(options, overrides); 374 options = Popups.overrideOptions(options, overrides);
361 } 375 }
362 376
363 // The parent of the new popup is the currently active popup. 377 // The parent of the new popup is the currently active popup.
364 var parent = Popups.activePopup(); 378 var parent = Popups.activePopup();
365 379
366 // If the option is distructive, check if the page is already modified, and offer to save. 380 // If the option is distructive, check if the page is already modified, and offer to save.
367 var willModifyOriginal = !(options.updateMethod === 'none' || options.skipDirtyCheck); 381 var willModifyOriginal = !(options.updateMethod === 'none' || options.skipDirtyCheck);
368 if (willModifyOriginal && Popups.activeLayerIsEdited()) { 382 if (willModifyOriginal && Popups.activeLayerIsEdited()) {
369 // The user will lose modifications, so show dialog offering to save current state. 383 // The user will lose modifications, so show dialog offering to save current state.
370 Popups.offerToSave(element, options, parent); 384 Popups.offerToSave(element, options, parent);
371 } 385 }
372 else { 386 else {
373 // Page is clean, or popup is safe, so just open it. 387 // Page is clean, or popup is safe, so just open it.
374 Popups.openPath(element, options, parent); 388 Popups.openPath(element, options, parent);
375 } 389 }
376 return false; 390 return false;
377 }; 391 };
378 392
379 /** 393 /**
380 * Test if the active layer been edited. 394 * Test if the active layer been edited.
381 * Active layer is either the original page, or the active Popup. 395 * Active layer is either the original page, or the active Popup.
382 */ 396 */
383 Popups.activeLayerIsEdited = function() { 397 Popups.activeLayerIsEdited = function() {
384 var layer = Popups.activePopup(); 398 var layer = Popups.activePopup();
385 var $context = Popups.getLayerContext(layer); 399 var $context = Popups.getLayerContext(layer);
386 // TODO: better test for edited page, maybe capture change event on :inputs. 400 // TODO: better test for edited page, maybe capture change event on :inputs.
387 var edited = $context.find('span.tabledrag-changed').length; 401 var edited = $context.find('span.tabledrag-changed').length;
388 return edited; 402 return edited;
389 } 403 }
390 404
391 /** 405 /**
392 * Show dialog offering to save form on parent layer. 406 * Show dialog offering to save form on parent layer.
393 * 407 *
394 * @param element 408 * @param element
395 * The DOM element that was clicked. 409 * The DOM element that was clicked.
396 * @param options 410 * @param options
397 * The options associated with that element. 411 * The options associated with that element.
398 * @param parent 412 * @param parent
404 var buttons = { 418 var buttons = {
405 'popup_save': {title: Drupal.t('Save Changes'), func: function(){Popups.saveFormOnLayer(element, options, parent);}}, 419 'popup_save': {title: Drupal.t('Save Changes'), func: function(){Popups.saveFormOnLayer(element, options, parent);}},
406 'popup_submit': {title: Drupal.t('Continue'), func: function(){popup.removePopup(); Popups.openPath(element, options, parent);}}, 420 'popup_submit': {title: Drupal.t('Continue'), func: function(){popup.removePopup(); Popups.openPath(element, options, parent);}},
407 'popup_cancel': {title: Drupal.t('Cancel'), func: function(){popup.close();}} 421 'popup_cancel': {title: Drupal.t('Cancel'), func: function(){popup.close();}}
408 }; 422 };
409 popup.open(Drupal.t('Warning: Please Confirm'), body, buttons); 423 popup.open(Drupal.t('Warning: Please Confirm'), body, buttons);
410 }; 424 };
411 425
412 /** 426 /**
413 * Generic dialog builder. 427 * Generic dialog builder.
414 * Adds the newly built popup into the DOM. 428 * Adds the newly built popup into the DOM.
415 * 429 *
416 * TODO: capture the focus if it tabs out of the dialog. 430 * TODO: capture the focus if it tabs out of the dialog.
417 * 431 *
418 * @param popup 432 * @param popup
419 * Popups.Popup object to fill with content, place in the DOM, and show on the screen. 433 * Popups.Popup object to fill with content, place in the DOM, and show on the screen.
420 * @param String title 434 * @param String title
423 * String: body of new dialog. 437 * String: body of new dialog.
424 * @param buttons (optional) 438 * @param buttons (optional)
425 * Hash of button parameters. 439 * Hash of button parameters.
426 * @param width (optional) 440 * @param width (optional)
427 * Width of new dialog. 441 * Width of new dialog.
428 * 442 *
429 * @return popup object 443 * @return popup object
430 */ 444 */
431 Popups.open = function(popup, title, body, buttons, width){ 445 Popups.open = function(popup, title, body, buttons, width){
432 Popups.addOverlay(); 446 Popups.addOverlay();
433 447
434 if (Popups.activePopup()) { 448 if (Popups.activePopup()) {
435 // Hiding previously active popup. 449 // Hiding previously active popup.
436 Popups.activePopup().hide(); 450 Popups.activePopup().hide();
437 } 451 }
438 452
439 if (!popup) { 453 if (!popup) {
440 // Popup object was not handed in, so create a new one. 454 // Popup object was not handed in, so create a new one.
441 popup = new Popups.Popup(); 455 popup = new Popups.Popup();
442 } 456 }
443 Popups.push(popup); // Put this popup at the top of the stack. 457 Popups.push(popup); // Put this popup at the top of the stack.
447 popup.hide(); // Hide the new popup until it is finished and sized. 461 popup.hide(); // Hide the new popup until it is finished and sized.
448 462
449 if (width) { 463 if (width) {
450 $popup.css('width', width); 464 $popup.css('width', width);
451 } 465 }
452 466
453 // Add the new popup to the DOM. 467 // Add the new popup to the DOM.
454 $('body').append($popup); 468 $('body').append($popup);
455 469
456 // Add button function callbacks. 470 // Add button function callbacks.
457 if (buttons) { 471 if (buttons) {
458 jQuery.each(buttons, function(id, button){ 472 jQuery.each(buttons, function(id, button){
459 $('#' + id).click(button.func); 473 $('#' + id).click(button.func);
460 }); 474 });
461 } 475 }
462 476
463 // Add the default click-to-close behavior. 477 // Add the default click-to-close behavior.
464 popup.$popupClose().click(function(){ 478 popup.$popupClose().click(function(){
465 return Popups.close(popup); 479 return Popups.close(popup);
466 }); 480 });
467 481
468 Popups.resizeAndCenter(popup); 482 Popups.resizeAndCenter(popup);
469 483
470 // Focus on the first input element in the popup window. 484 // Focus on the first input element in the popup window.
471 popup.refocus(); 485 popup.refocus();
472 486
473 // TODO - this isn't the place for this - should mirror addLoading calls. 487 // TODO - this isn't the place for this - should mirror addLoading calls.
474 // Remove the loading image. 488 // Remove the loading image.
475 Popups.removeLoading(); 489 Popups.removeLoading();
476 490
477 return popup; 491 return popup;
478 }; 492 };
479 493
480 /** 494 /**
481 * Adjust the popup's height to fit it's content. 495 * Adjust the popup's height to fit it's content.
482 * Move it to be centered on the screen. 496 * Move it to be centered on the screen.
483 * This undoes the effects of popup.hide(). 497 * This undoes the effects of popup.hide().
484 * 498 *
485 * @param popup 499 * @param popup
486 */ 500 */
487 Popups.resizeAndCenter = function(popup) { 501 Popups.resizeAndCenter = function(popup) {
488 var $popup = popup.$popup(); 502 var $popup = popup.$popup();
489 503
490 // center on the screen, adding in offsets if the window has been scrolled 504 // center on the screen, adding in offsets if the window has been scrolled
491 var popupWidth = $popup.width(); 505 var popupWidth = $popup.width();
492 var windowWidth = Popups.windowWidth(); 506 var windowWidth = Popups.windowWidth();
493 var left = (windowWidth / 2) - (popupWidth / 2) + Popups.scrollLeft(); 507 var left = (windowWidth / 2) - (popupWidth / 2) + Popups.scrollLeft();
494 508
495 // Get popups's height on the page. 509 // Get popups's height on the page.
496 $popup.css('height', 'auto'); // Reset height. 510 $popup.css('height', 'auto'); // Reset height.
497 var popupHeight = $popup.height(); 511 var popupHeight = $popup.height();
498 $popup.height(popupHeight); 512 $popup.height(popupHeight);
499 var windowHeight = Popups.windowHeight(); 513 var windowHeight = Popups.windowHeight();
500 514
501 if (popupHeight > (0.9 * windowHeight) ) { // Must fit in 90% of window. 515 if (popupHeight > (0.9 * windowHeight) ) { // Must fit in 90% of window.
502 popupHeight = 0.9 * windowHeight; 516 popupHeight = 0.9 * windowHeight;
503 $popup.height(popupHeight); 517 $popup.height(popupHeight);
504 } 518 }
505 var top = (windowHeight / 2) - (popupHeight / 2) + Popups.scrollTop(); 519 var top = (windowHeight / 2) - (popupHeight / 2) + Popups.scrollTop();
506 520
507 $popup.css('top', top).css('left', left); // Position the popups to be visible. 521 $popup.css('top', top).css('left', left); // Position the popups to be visible.
508 }; 522 };
509 523
510 524
511 /** 525 /**
512 * Create and show a simple popup dialog that functions like the browser's alert box. 526 * Create and show a simple popup dialog that functions like the browser's alert box.
513 */ 527 */
514 Popups.message = function(title, message) { 528 Popups.message = function(title, message) {
547 }; 561 };
548 562
549 /***************************************************************************** 563 /*****************************************************************************
550 * Appearence Functions (overlay, loading graphic, remove popups) ********* 564 * Appearence Functions (overlay, loading graphic, remove popups) *********
551 *****************************************************************************/ 565 *****************************************************************************/
552 566
553 /** 567 /**
554 * Add full page div between the page and the dialog, to make the popup modal. 568 * Add full page div between the page and the dialog, to make the popup modal.
555 */ 569 */
556 Popups.addOverlay = function() { 570 Popups.addOverlay = function() {
557 var $overlay = $('#popups-overlay'); 571 var $overlay = $('#popups-overlay');
558 if (!$overlay.length) { // Overlay does not already exist, so create it. 572 if (!$overlay.length) { // Overlay does not already exist, so create it.
559 $overlay = $(Drupal.theme('popupOverlay')); 573 $overlay = $(Drupal.theme('popupOverlay'));
560 $overlay.css('opacity', '0.4'); // for ie6(?) 574 $overlay.css('opacity', '0.4'); // for ie6(?)
561 // Doing absolute positioning, so make overlay's size equal the entire body. 575 // Doing absolute positioning, so make overlay's size equal the entire body.
562 var $doc = $(document); 576 var $doc = $(document);
563 $overlay.width($doc.width()).height($doc.height()); 577 $overlay.width($doc.width()).height($doc.height());
564 $overlay.click(function(){Popups.close();}); 578 $overlay.click(function(){Popups.close();});
565 $('body').prepend($overlay); 579 $('body').prepend($overlay);
566 } 580 }
567 }; 581 };
568 582
594 Popups.removeLoading = function() { 608 Popups.removeLoading = function() {
595 $('#popups-loading').remove(); 609 $('#popups-loading').remove();
596 }; 610 };
597 611
598 // Should I fold this function into Popups.pop? 612 // Should I fold this function into Popups.pop?
599 Popups.removePopup = function(popup) { 613 Popups.removePopup = function(popup) {
600 // console.log("Popups.removePopup: " + popup);
601 if (!Popups.isset(popup)) { 614 if (!Popups.isset(popup)) {
602 popup = Popups.activePopup(); 615 popup = Popups.activePopup();
603 } 616 }
604 if (popup) { 617 if (popup) {
605 // console.log('removing '+popup.id);
606 popup.$popup().remove(); 618 popup.$popup().remove();
607 Popups.popupStack.splice(Popups.popupStack.indexOf(popup), 1); // Remove popup from stack. Probably should rework into .pop() 619 Popups.popupStack.splice(jQuery.inArray(popup,Popups.popupStack), 1); // Remove popup from stack. Probably should rework into .pop()
608 } 620 }
609 // else { 621 };
610 // console.log("Popups.removePopup - there is no popup to remove.");
611 // }
612 };
613 622
614 /** 623 /**
615 * Remove everything. 624 * Remove everything.
616 */ 625 */
617 Popups.close = function(popup) { 626 Popups.close = function(popup) {
642 651
643 /** 652 /**
644 * Restore the page's original Drupal.settings. 653 * Restore the page's original Drupal.settings.
645 */ 654 */
646 Popups.restoreSettings = function() { 655 Popups.restoreSettings = function() {
647 Drupal.settings = Popups.originalSettings; 656 Drupal.settings = Popups.originalSettings;
648 }; 657 };
649 658
650 /** 659 /**
651 * Remove as much of the effects of jit loading as possible. 660 * Remove as much of the effects of jit loading as possible.
652 */ 661 */
653 Popups.restorePage = function() { 662 Popups.restorePage = function() {
654 Popups.restoreSettings(); 663 Popups.restoreSettings();
655 // Remove the CSS files that were jit loaded for popup. 664 // Remove the CSS files that were jit loaded for popup.
656 for (var i in Popups.addedCSS) { 665 for (var i in Popups.addedCSS) if (Popups.addedCSS.hasOwnProperty(i)) {
657 var link = Popups.addedCSS[i]; 666 $('link[href='+ Popups.addedCSS[i] + ']').remove();
658 $('link[href='+ $(link).attr('href') + ']').remove();
659 } 667 }
660 Popups.addedCSS = []; 668 Popups.addedCSS = [];
661 }; 669 };
662 670
663 671
682 /** 690 /**
683 * Get the height of the browser window. 691 * Get the height of the browser window.
684 * Fixes jQuery & Opera bug - http://drupal.org/node/366093 692 * Fixes jQuery & Opera bug - http://drupal.org/node/366093
685 */ 693 */
686 Popups.windowHeight = function() { 694 Popups.windowHeight = function() {
687 if ($.browser.opera && $.browser.version > "9.5" && $.fn.jquery <= "1.2.6") { 695 if ($.browser.opera && $.browser.version > "9.5" && $.fn.jquery <= "1.2.6") {
688 return document.documentElement.clientHeight; 696 return document.documentElement.clientHeight;
689 } 697 }
690 return $(window).height(); 698 return $(window).height();
691 }; 699 };
692 700
693 /** 701 /**
694 * Get the height of the browser window. 702 * Get the height of the browser window.
695 * Fixes jQuery & Opera bug - http://drupal.org/node/366093 703 * Fixes jQuery & Opera bug - http://drupal.org/node/366093
696 */ 704 */
697 Popups.windowWidth = function() { 705 Popups.windowWidth = function() {
698 if ($.browser.opera && $.browser.version > "9.5" && $.fn.jquery <= "1.2.6") { 706 if ($.browser.opera && $.browser.version > "9.5" && $.fn.jquery <= "1.2.6") {
699 return document.documentElement.clientWidth; 707 return document.documentElement.clientWidth;
700 } 708 }
701 return $(window).width(); 709 return $(window).width();
702 }; 710 };
703 711
718 /** 726 /**
719 * Add additional CSS to the page. 727 * Add additional CSS to the page.
720 */ 728 */
721 Popups.addCSS = function(css) { 729 Popups.addCSS = function(css) {
722 Popups.addedCSS = []; 730 Popups.addedCSS = [];
723 for (var type in css) { 731 for (var type in css) if (css.hasOwnProperty(type)) {
724 for (var file in css[type]) { 732 for (var file in css[type]) if (css[type].hasOwnProperty(file)) {
725 var link = css[type][file]; 733 var link = css[type][file];
734 var href = $(link).attr('href');
726 // Does the page already contain this stylesheet? 735 // Does the page already contain this stylesheet?
727 if (!$('link[href='+ $(link).attr('href') + ']').length) { 736 if (!Popups.originalCSS[href.replace(/^(\/.+)\?\w$/, '$1')] && !Popups.addedCSS[href]) {
728 $('head').append(link); 737 $('head').append(link);
729 Popups.addedCSS.push(link); // Keep a list, so we can remove them later. 738 Popups.addedCSS[href] = 1; // Keep a list, so we can remove them later.
730 } 739 }
731 } 740 }
732 } 741 }
733 }; 742 };
734 743
737 */ 746 */
738 Popups.addJS = function(js) { 747 Popups.addJS = function(js) {
739 // Parse the json info about the new context. 748 // Parse the json info about the new context.
740 var scripts = []; 749 var scripts = [];
741 var inlines = []; 750 var inlines = [];
742 for (var type in js) { 751 var src;
752 for (var type in js) if (js.hasOwnProperty(type)) {
743 if (type != 'setting') { 753 if (type != 'setting') {
744 for (var file in js[type]) { 754 for (var file in js[type]) if (js[type].hasOwnProperty(file)) {
745 if (type == 'inline') { 755 if (type == 'inline') {
746 inlines.push($(js[type][file]).text()); 756 inlines.push($(js[type][file]).text());
747 } 757 }
748 else { 758 else {
749 scripts.push($(js[type][file]).attr('src')); 759 src = $(js[type][file]).attr('src');
760 if (!Popups.originalJS[src.replace(/^(\/.+)\?\w$/, '$1')] && !Popups.addedJS[src]) {
761 // Get the script from the server and execute it.
762 $.ajax({
763 type: 'GET',
764 url: src,
765 dataType: 'script',
766 async : false,
767 success: function(script) {
768 eval(script);
769 }
770 });
771 // Mark the js as added to the underlying page.
772 Popups.addedJS[src] = 1;
773 }
750 } 774 }
751 } 775 }
752 } 776 }
753 } 777 }
754 778
755 // Add new JS settings to the page, needed for #ahah properties to work. 779 // Add new JS settings to the page, needed for #ahah properties to work.
756 Drupal.settings = js.setting; 780 Drupal.settings = js.setting;
757 // console.log('js.setting...');
758 // console.log(js.setting);
759
760 for (var i in scripts) {
761 var src = scripts[i];
762 if (!$('script[src='+ src + ']').length && !Popups.addedJS[src]) {
763 // Get the script from the server and execute it.
764 $.ajax({
765 type: 'GET',
766 url: src,
767 dataType: 'script',
768 async : false,
769 success: function(script) {
770 eval(script);
771 }
772 });
773 // Mark the js as added to the underlying page.
774 Popups.addedJS[src] = true;
775 }
776 }
777 781
778 return inlines; 782 return inlines;
779 }; 783 };
780 784
781 /** 785 /**
782 * Execute the jit loaded inline scripts. 786 * Execute the jit loaded inline scripts.
783 * Q: Do we want to re-excute the ones already in the page? 787 * Q: Do we want to re-excute the ones already in the page?
784 * 788 *
785 * @param inlines 789 * @param inlines
786 * Array of inline scripts. 790 * Array of inline scripts.
787 */ 791 */
788 Popups.addInlineJS = function(inlines) { 792 Popups.addInlineJS = function(inlines) {
789 // Load the inlines into the page. 793 // Load the inlines into the page.
790 for (var n in inlines) { 794 for (var n in inlines) {
791 // If the script is not already in the page, execute it. 795 // If the script is not already in the page, execute it.
792 if (!$('script:not([src]):contains(' + inlines[n] + ')').length) { 796 //if (!$('script:not([src]):contains(' + inlines[n] + ')').length) {
793 eval(inlines[n]); 797 eval(inlines[n]);
794 } 798 //}
795 } 799 }
796 }; 800 };
797 801
798 Popups.beforeSend = function(xhr) { 802 Popups.beforeSend = function(xhr) {
799 xhr.setRequestHeader("X-Drupal-Render-Mode", 'json/popups'); 803 xhr.setRequestHeader("X-Drupal-Render-Mode", 'json/popups');
825 Popups.openPath = function(element, options, parent) { 829 Popups.openPath = function(element, options, parent) {
826 Popups.saveSettings(); 830 Popups.saveSettings();
827 831
828 // Let the user know something is happening. 832 // Let the user know something is happening.
829 $('body').css("cursor", "wait"); 833 $('body').css("cursor", "wait");
830 834
831 // TODO - get nonmodal working. 835 // TODO - get nonmodal working.
832 if (!options.nonModal) { 836 if (!options.nonModal) {
833 Popups.addOverlay(); 837 Popups.addOverlay();
834 } 838 }
835 Popups.addLoading(); 839 Popups.addLoading();
836 840
837 var href = options.href ? options.href : element.href; 841 var href = options.href ? options.href : element.href;
838 $(document).trigger('popups_open_path', [element, href]); // Broadcast Popup Open Path event. 842 $(document).trigger('popups_open_path', [element, href]); // Broadcast Popup Open Path event.
839 843
840 var params = {}; 844 var params = {};
841 // Force the popups to return back to the orignal page when forms are done, unless hijackDestination option is set to FALSE. 845 // Force the popups to return back to the orignal page when forms are done, unless hijackDestination option is set to FALSE.
842 if (options.hijackDestination) { 846 if (options.hijackDestination) {
843 var returnPath; 847 var returnPath;
844 if (parent) { 848 if (parent) {
845 returnPath = parent.path; 849 returnPath = parent.path;
846 // console.log('Popup parent is ...');
847 // console.log(parent);
848 } 850 }
849 else { // No parent, so bring flow back to original page. 851 else { // No parent, so bring flow back to original page.
850 returnPath = Popups.originalSettings.popups.originalPath; 852 returnPath = Popups.originalSettings.popups.originalPath;
851 } 853 }
852 href = href.replace(/destination=[^;&]*[;&]?/, ''); // Strip out any existing destination param. 854 href = href.replace(/destination=[^;&]*[;&]?/, ''); // Strip out any existing destination param.
853 // console.log("Hijacking destination to " + returnPath); 855 params.destination = returnPath; // Set the destination to return to the parent's path.
854 params.destination = returnPath; // Set the destination to return to the parent's path.
855 } 856 }
856 857
857 var ajaxOptions = { 858 var ajaxOptions = {
858 url: href, 859 url: href,
859 dataType: 'json', 860 dataType: 'json',
860 data: params, 861 data: params,
861 beforeSend: Popups.beforeSend, 862 beforeSend: Popups.beforeSend,
862 success: function(json) { 863 success: function(json) {
863 // Add additional CSS to the page. 864 // Add additional CSS to the page.
864 Popups.addCSS(json.css); 865 Popups.addCSS(json.css);
865 var inlines = Popups.addJS(json.js); 866 var inlines = Popups.addJS(json.js);
866 var popup = Popups.openPathContent(json.path, json.title, json.messages + json.content, element, options, parent); 867 var popup = Popups.openPathContent(json.path, json.title, json.messages + json.content, element, options, parent);
867 Popups.addInlineJS(inlines); 868 Popups.addInlineJS(inlines);
868 // Broadcast an event that the path was opened. 869 // Broadcast an event that the path was opened.
869 $(document).trigger('popups_open_path_done', [element, href, popup]); 870 $(document).trigger('popups_open_path_done', [element, href, popup]);
870 }, 871 },
871 complete: function() { 872 complete: function() {
872 $('body').css("cursor", "auto"); // Return the cursor to normal state. 873 $('body').css("cursor", "auto"); // Return the cursor to normal state.
873 } 874 }
874 }; 875 };
875 876
876 var ajaxOptions; 877 var ajaxOptions;
877 if (options.reloadOnError) { 878 if (options.reloadOnError) {
878 ajaxOptions.error = function() { 879 ajaxOptions.error = function() {
879 location.reload(); // Reload on error. Is this working? 880 location.reload(); // Reload on error. Is this working?
880 }; 881 };
881 } 882 }
882 else { 883 else {
883 ajaxOptions.error = function() { 884 ajaxOptions.error = function() {
884 Popups.message("Unable to open: " + href); 885 Popups.message("Unable to open: " + href);
885 }; 886 };
886 } 887 }
887 $.ajax(ajaxOptions); 888 $.ajax(ajaxOptions);
888 889
889 return false; 890 return false;
890 }; 891 };
891 892
892 /** 893 /**
893 * Open path's content in an ajax popups. 894 * Open path's content in an ajax popups.
894 * 895 *
895 * @param title 896 * @param title
896 * String title of the popups. 897 * String title of the popups.
897 * @param content 898 * @param content
898 * HTML to show in the popups. 899 * HTML to show in the popups.
899 * @param element 900 * @param element
900 * A DOM object containing the element that was clicked to initiate the popup. 901 * A DOM object containing the element that was clicked to initiate the popup.
901 * @param options 902 * @param options
902 * Hash of options controlling how the popups interacts with the underlying page. 903 * Hash of options controlling how the popups interacts with the underlying page.
903 * @param parent 904 * @param parent
904 * Spawning popup, or null if spawned from original page. 905 * Spawning popup, or null if spawned from original page.
905 */ 906 */
906 Popups.openPathContent = function(path, title, content, element, options, parent) { 907 Popups.openPathContent = function(path, title, content, element, options, parent) {
907 var popup = new Popups.Popup(); 908 var popup = new Popups.Popup();
908 Popups.open(popup, title, content, null, options.width); 909 Popups.open(popup, title, content, null, options.width);
909 910
910 // Set properties on new popup. 911 // Set properties on new popup.
911 popup.parent = parent; 912 popup.parent = parent;
912 popup.path = path; 913 popup.path = path;
913 // console.log("Setting popup " + popup.id + " originalPath to " + path);
914 popup.options = options; 914 popup.options = options;
915 popup.element = element; 915 popup.element = element;
916 916
917 // Add behaviors to content in popups. 917 // Add behaviors to content in popups.
918 delete Drupal.behaviors.tableHeader; // Work-around for bug in tableheader.js (http://drupal.org/node/234377) 918 delete Drupal.behaviors.tableHeader; // Work-around for bug in tableheader.js (http://drupal.org/node/234377)
919 delete Drupal.behaviors.teaser; // Work-around for bug in teaser.js (sigh). 919 delete Drupal.behaviors.teaser; // Work-around for bug in teaser.js (sigh).
920 Drupal.attachBehaviors(popup.$popupBody()); 920 Drupal.attachBehaviors(popup.$popupBody());
921 // Adding collapse moves focus. 921 // Adding collapse moves focus.
922 popup.refocus(); 922 popup.refocus();
923 923
924 // If the popups contains a form, capture submits. 924 // If the popups contains a form, capture submits.
925 var $form = $('form', popup.$popupBody()); 925 var $form = $('form', popup.$popupBody());
926 if ($form.length) { 926 if ($form.length) {
927 $form.ajaxForm({ 927 $form.ajaxForm({
928 dataType: 'json', 928 dataType: 'json',
929 beforeSubmit: Popups.beforeSubmit, 929 beforeSubmit: Popups.beforeSubmit,
930 beforeSend: Popups.beforeSend, 930 beforeSend: Popups.beforeSend,
931 success: function(json, status) { 931 success: function(json, status) {
932 Popups.formSuccess(popup, json); 932 Popups.formSuccess(popup, json);
933 }, 933 },
941 941
942 /** 942 /**
943 * The form in the popups was successfully submitted 943 * The form in the popups was successfully submitted
944 * Update the originating page. 944 * Update the originating page.
945 * Show any messages in a popups. 945 * Show any messages in a popups.
946 * 946 *
947 * @param popup 947 * @param popup
948 * The popup object that contained the form that was just submitted. 948 * The popup object that contained the form that was just submitted.
949 * @param data 949 * @param data
950 * JSON object from server with status of form submission. 950 * JSON object from server with status of form submission.
951 */ 951 */
952 Popups.formSuccess = function(popup, data) { 952 Popups.formSuccess = function(popup, data) {
953 // Determine if we are at an end point, or just moving from one popups to another. 953 // Determine if we are at an end point, or just moving from one popups to another.
954 var done = popup.isDone(data.path); 954 var done = popup.isDone(data.path);
955 if (!done) { // Not done yet, so show new page in new popups. 955 if (!done) { // Not done yet, so show new page in new popups.
956 Popups.removeLoading(); 956 Popups.removeLoading();
957 Popups.openPathContent(data.path, data.title, data.messages + data.content, popup.element, popup.options, popup.parent); 957 Popups.openPathContent(data.path, data.title, data.messages + data.content, popup.element, popup.options, popup.parent);
958 } 958 }
959 else { // We are done with popup flow. 959 else { // We are done with popup flow.
960 // Execute the onUpdate callback if available. 960 // Execute the onUpdate callback if available.
961 if (popup.options.updateMethod === 'callback' && popup.options.onUpdate) { 961 if (popup.options.updateMethod === 'callback' && popup.options.onUpdate) {
962 var result = eval(popup.options.onUpdate +'(data, popup.options, popup.element)'); 962 var result = eval(popup.options.onUpdate +'(data, popup.options, popup.element)');
963 if (result === false) { // Give onUpdate callback a chance to skip normal processing. 963 if (result === false) { // Give onUpdate callback a chance to skip normal processing.
964 return; 964 return;
965 } 965 }
966 } 966 }
968 if (popup.options.updateMethod === 'reload') { // Force a complete, non-ajax reload of the page. 968 if (popup.options.updateMethod === 'reload') { // Force a complete, non-ajax reload of the page.
969 if (popup.options.updateSource === 'final') { 969 if (popup.options.updateSource === 'final') {
970 location.href = Drupal.settings.basePath + data.path; // TODO: Need to test this. 970 location.href = Drupal.settings.basePath + data.path; // TODO: Need to test this.
971 } 971 }
972 else { // Reload originating page. 972 else { // Reload originating page.
973 location.reload(); 973 location.reload();
974 } 974 }
975 } 975 }
976 else { // Normal, targeted ajax, reload behavior. 976 else { // Normal, targeted ajax, reload behavior.
977 var showingMessagePopup = false;
977 // Show messages in dialog and embed the results in the original page. 978 // Show messages in dialog and embed the results in the original page.
978 var showMessage = data.messages.length && !popup.options.noMessage; 979 // TODO - should seperate these two functions.
979 if (showMessage) { 980 // var showMessage = data.messages.length && !popup.options.noMessage;
980 var messagePopup = Popups.message(data.messages); // Popup message. 981 if (data.messages.length) {
981 if (Popups.originalSettings.popups.autoCloseFinalMessage) { 982 // If we just dismissed the last popup dialog.
982 setTimeout(function(){Popups.close(messagePopup);}, 2500); // Autoclose the message box in 2.5 seconds. 983 if (!Popups.activePopup() && !popup.options.noMessage) {
984 // Show drupal_set_message in message popup.
985 var messagePopup = Popups.message(data.messages);
986 if (Popups.originalSettings.popups.autoCloseFinalMessage) {
987 setTimeout(function(){Popups.close(messagePopup);}, 2500); // Autoclose the message box in 2.5 seconds.
988 }
989 showingMessagePopup = true;
983 } 990 }
984 991
985 // Insert the message into the page above the content. 992
993 // Insert the message into the parent layer, above the content.
986 // Might not be the standard spot, but it is the easiest to find. 994 // Might not be the standard spot, but it is the easiest to find.
987 var $next; 995 var $next;
988 if (popup.targetLayerSelector() === 'body') { 996 if (popup.targetLayerSelector() === 'body') {
989 $next = $('body').find(Popups.originalSettings.popups.defaultTargetSelector); 997 $next = $('body').find(Popups.originalSettings.popups.defaultTargetSelector);
990 } 998 }
992 $next = $(popup.targetLayerSelector()).find('.popups-body'); 1000 $next = $(popup.targetLayerSelector()).find('.popups-body');
993 } 1001 }
994 $next.parent().find('div.messages').remove(); // Remove the existing messages. 1002 $next.parent().find('div.messages').remove(); // Remove the existing messages.
995 $next.before(data.messages); // Insert new messages. 1003 $next.before(data.messages); // Insert new messages.
996 } 1004 }
997 1005
998 // Update the content area (defined by 'targetSelectors'). 1006 // Update the content area (defined by 'targetSelectors').
999 if (popup.options.updateMethod !== 'none') { 1007 if (popup.options.updateMethod !== 'none') {
1000 Popups.testContentSelector(); // Kick up warning message if selector is bad. 1008 Popups.testContentSelector(); // Kick up warning message if selector is bad.
1001 1009
1002 Popups.restoreSettings(); // Need to restore original Drupal.settings.popups.links before running attachBehaviors. This probably has CSS side effects! 1010 Popups.restoreSettings(); // Need to restore original Drupal.settings.popups.links before running attachBehaviors. This probably has CSS side effects!
1003 if (popup.options.targetSelectors) { // Pick and choose what returned content goes where. 1011 if (popup.options.targetSelectors) { // Pick and choose what returned content goes where.
1004 jQuery.each(popup.options.targetSelectors, function(t_new, t_old) { 1012 jQuery.each(popup.options.targetSelectors, function(t_new, t_old) {
1005 if(!isNaN(t_new)) { 1013 if(!isNaN(t_new)) {
1006 t_new = t_old; // handle case where targetSelectors is an array, not a hash. 1014 t_new = t_old; // handle case where targetSelectors is an array, not a hash.
1007 } 1015 }
1008 // console.log("Updating target " + t_old + ' with ' + t_new);
1009 var new_content = $(t_new, data.content); 1016 var new_content = $(t_new, data.content);
1010 // console.log("new content... ");
1011 // console.log(new_content);
1012 var $c = $(popup.targetLayerSelector()).find(t_old).html(new_content); // Inject the new content into the original page. 1017 var $c = $(popup.targetLayerSelector()).find(t_old).html(new_content); // Inject the new content into the original page.
1013 1018
1014 Drupal.attachBehaviors($c); 1019 Drupal.attachBehaviors($c);
1015 }); 1020 });
1016 } 1021 }
1017 else { // Put the entire new content into default content area. 1022 else { // Put the entire new content into default content area.
1018 var $c = $(popup.targetLayerSelector()).find(Popups.originalSettings.popups.defaultTargetSelector).html(data.content); 1023 var $c = $(popup.targetLayerSelector()).find(Popups.originalSettings.popups.defaultTargetSelector).html(data.content);
1019 // console.log("updating entire content area.") 1024 Drupal.attachBehaviors($c);
1020 Drupal.attachBehaviors($c);
1021 } 1025 }
1022 } 1026 }
1023 1027
1024 // Update the title of the page. 1028 // Update the title of the page.
1025 if (popup.options.titleSelectors) { 1029 if (popup.options.titleSelectors) {
1026 jQuery.each(popup.options.titleSelectors, function() { 1030 jQuery.each(popup.options.titleSelectors, function() {
1027 $(''+this).html(data.title); 1031 $(''+this).html(data.title);
1028 }); 1032 });
1029 } 1033 }
1030 1034
1031 // Done with changes to the original page, remove effects. 1035 // Done with changes to the original page, remove effects.
1032 Popups.removeLoading(); 1036 Popups.removeLoading();
1033 if (!showMessage) { 1037 if (!showingMessagePopup) {
1034 // If there is not a messages popups, close current layer. 1038 // If there is not a messages popups, pop the stack.
1035 Popups.close(); 1039 // Sending in null to Popups.close reveales the next popup in the stack.
1040 // If the stack is empty, it will remove the overlay.
1041 Popups.close(null);
1036 } 1042 }
1037 } 1043 }
1038 1044
1039 // Broadcast an event that popup form was done and successful. 1045 // Broadcast an event that popup form was done and successful.
1040 $(document).trigger('popups_form_success', [popup]); 1046 $(document).trigger('popups_form_success', [popup]);
1041 1047
1042 } // End of updating spawning layer. 1048 } // End of updating spawning layer.
1043 }; 1049 };
1044 1050
1045 1051
1046 /** 1052 /**
1047 * Get a jQuery object for the content of a layer. 1053 * Get a jQuery object for the content of a layer.
1048 * @param layer 1054 * @param layer
1049 * Either a popup, or null to signify the original page. 1055 * Either a popup, or null to signify the original page.
1050 */ 1056 */
1051 Popups.getLayerContext = function(layer) { 1057 Popups.getLayerContext = function(layer) {
1052 var $context; 1058 var $context;
1053 if (!layer) { 1059 if (!layer) {
1054 $context = $('body').find(Popups.originalSettings.popups.defaultTargetSelector); 1060 $context = $('body').find(Popups.originalSettings.popups.defaultTargetSelector);
1055 } 1061 }
1056 else { 1062 else {
1065 * @param element 1071 * @param element
1066 * Element that was clicked to open a new popup. 1072 * Element that was clicked to open a new popup.
1067 * @param options 1073 * @param options
1068 * Hash of options controlling how the popups interacts with the underlying page. 1074 * Hash of options controlling how the popups interacts with the underlying page.
1069 * @param layer 1075 * @param layer
1070 * Popup with form to save, or null if form is on original page. 1076 * Popup with form to save, or null if form is on original page.
1071 */ 1077 */
1072 Popups.saveFormOnLayer = function(element, options, layer) { 1078 Popups.saveFormOnLayer = function(element, options, layer) {
1073 var $context = Popups.getLayerContext(layer); 1079 var $context = Popups.getLayerContext(layer);
1074 var $form = $context.find('form'); 1080 var $form = $context.find('form');
1075 var ajaxOptions = { 1081 var ajaxOptions = {
1076 dataType: 'json', 1082 dataType: 'json',
1077 beforeSubmit: Popups.beforeSubmit, 1083 beforeSubmit: Popups.beforeSubmit,
1078 beforeSend: Popups.beforeSend, 1084 beforeSend: Popups.beforeSend,
1079 success: function(response, status) { 1085 success: function(response, status) {
1080 // Sync up the current page contents with the submit. 1086 // Sync up the current page contents with the submit.
1081 var $c = $context.html(response.content); // Inject the new content into the page. 1087 var $c = $context.html(response.content); // Inject the new content into the page.
1082 Drupal.attachBehaviors($c); 1088 Drupal.attachBehaviors($c);
1083 // The form has been saved, the page reloaded, now safe to show the triggering link in a popup. 1089 // The form has been saved, the page reloaded, now safe to show the triggering link in a popup.
1084 Popups.openPath(element, options, layer); 1090 Popups.openPath(element, options, layer);
1085 } 1091 }
1086 }; 1092 };
1087 $form.ajaxSubmit(ajaxOptions); // Submit the form. 1093 $form.ajaxSubmit(ajaxOptions); // Submit the form.
1088 }; 1094 };
1089 1095
1090 /** 1096 /**
1091 * Warn the user if ajax updates will not work 1097 * Warn the user if ajax updates will not work
1092 * due to mismatch between the theme and the theme's popup setting. 1098 * due to mismatch between the theme and the theme's popup setting.
1100 msg += Drupal.t('There is no element that matches ') + '"' + target + '"\n'; 1106 msg += Drupal.t('There is no element that matches ') + '"' + target + '"\n';
1101 } 1107 }
1102 else if (hits > 1) { 1108 else if (hits > 1) {
1103 msg += Drupal.t('There are multiple elements that match: ') + '"' + target + '"\n'; 1109 msg += Drupal.t('There are multiple elements that match: ') + '"' + target + '"\n';
1104 } 1110 }
1105 msg += Drupal.t('Go to admin/build/themes/settings, select your theme, and edit the "Content Selector" field'); 1111 msg += Drupal.t('Go to admin/build/themes/settings, select your theme, and edit the "Content Selector" field');
1106 alert(msg); 1112 alert(msg);
1107 } 1113 }
1108 }; 1114 };
1109 1115
1110 1116
1128 }; 1134 };
1129 1135
1130 Drupal.theme.prototype.popupDialog = function(popupId, title, body, buttons) { 1136 Drupal.theme.prototype.popupDialog = function(popupId, title, body, buttons) {
1131 var template = Drupal.theme('popupTemplate', popupId); 1137 var template = Drupal.theme('popupTemplate', popupId);
1132 var popups = template.replace('%title', title).replace('%body', body); 1138 var popups = template.replace('%title', title).replace('%body', body);
1133 1139
1134 var themedButtons = ''; 1140 var themedButtons = '';
1135 if (buttons) { 1141 if (buttons) {
1136 jQuery.each(buttons, function (id, button) { 1142 jQuery.each(buttons, function (id, button) {
1137 themedButtons += Drupal.theme('popupButton', button.title, id); 1143 themedButtons += Drupal.theme('popupButton', button.title, id);
1138 }); 1144 });
1139 } 1145 }
1140 popups = popups.replace('%buttons', themedButtons); 1146 popups = popups.replace('%buttons', themedButtons);
1141 return popups; 1147 return popups;
1142 }; 1148 };
1143 1149
1144 Drupal.theme.prototype.popupTemplate = function(popupId) { 1150 Drupal.theme.prototype.popupTemplate = function(popupId) {
1145 var template; 1151 var template;
1153 template += ' <div class="popups-buttons">%buttons</div>'; 1159 template += ' <div class="popups-buttons">%buttons</div>';
1154 template += ' <div class="popups-footer"></div>'; 1160 template += ' <div class="popups-footer"></div>';
1155 template += '</div>'; 1161 template += '</div>';
1156 return template; 1162 return template;
1157 }; 1163 };
1158