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