// $Id: float_window.src.js,v 1.1 2008/12/05 15:40:11 skilip Exp $

/**
 * floatWin(options)
 * 
 * @description: Adds a floating window to body
 *
 * @to do: - Fix the maximal height of the floating window in IE 
 *         - Add a fixed top and left position on init. And a save last position option 
 *
 * @return Object Returns a floafing window object.
 * @param Object options All options to pass to the window on init 
 * 
 *  				window_id         String the id prefix for the window, 
 *  				window_class      String the class prefix for the window, 
 *  				resizable         Boolean whether the window can be maximized or not, 
 *  				show              Boolean Whether the window must be shown on init, 
 *  				add_close_button  Boolean Whether the window must have a close button (default: true), 
 *  				draggable         Boolean Whether the window is draggable (default: true), 
 *          show_button       String A jQuery selector for the element which shows the window after an onclick event
 *          onload_callback   String A callback script triggered after the window has loaded
 *  				min_width         String || Number The minimal width of the window. e.a.: 10, '10px' or '10%'. If the content is wider, this value will be overwritten by the contents width 
 *  				min_height        String || Number The minimal height of the window. e.a.: 10, '10px' or '10%'. If the content is higher, this value will be overwritten by the contents height
 * 
 */
function floatWin(options) {
  var fw                            = this;
  fw.options                        = (options) || {};

  // Configuration variables
  fw.window_id                      = 'float_window';
  fw.window_class                   = 'float_window';
  fw.resizable            	        = true;
  fw.add_close_button               = true;
  fw.draggable                      = true;
  fw.show                           = true;
  fw.show_button                    = false;
  fw.onload_callback                = false;
  fw.ajax_success_callback          = false;
  fw.min_width                      = 300;
  fw.min_height                     = 80;
  fw.margin                         = {top:6, bottom:14, left:14, right:14};

  // Other variables
  fw.content_properties             = {};
  fw.float_win_dimensions           = {};
  fw.window_visible                 = false;
  fw.maximized                      = false;
  fw.reload_content_after_messages  = true;
  fw.new_content_loaded             = false;
  fw.form_disabled                  = true;
  fw.close                          = false;
  fw.window_obj;
  fw.window_obj;
  fw.window_title_obj;
  fw.redirect;
  fw.dragged;
  fw.clicked_url;

  /**
   * Initializes the floating window
   */
  fw.init = function() {
    //trace(fw.options);
    fw.setConfigurationOptions();
    fw.placeWindow();
  
    // We need a timeout because the some browser can scroll down after the page is loaded.
    // If we omit a timeout, sometimes the floating window is not properly aligned.
    if(fw.show) setTimeout(function() { eval('window.float_windows.' + fw.window_id + '.showWindow();'); }, 300);
    setTimeout(function() { eval('window.float_windows.' + fw.window_id + '.onLoadHandler();'); }, 300);
  
    fw.addEventListeners(true);
  };

  /**
   * This function checks for all passed arguments in the options object. 
   * If a configuration option is given, the default values is overwritten.
   */
  fw.setConfigurationOptions = function() {
    // Here we check wheter a required setting is set in the arguments options. If not, we'll set the default value
    for(var option in fw.options) if(fw.options[option] !== undefined && fw.options[option] !== '') fw[option] = fw.options[option];
  };

  /**
   * Places an instance of a floating window in the body
   */
  fw.placeWindow = function() {
    // First check if the window is not placed already
    if($("body").find("#" + fw.window_id).size() > 0) return;
  
    var window_html = '';
    window_html += '	<table style="visibility:hidden;" id="' + fw.window_id + '" class="' + fw.window_class + '">'; // We'll hide it using visibility:hiddden. This way we can align the floating window at the browser's window before it's visible
    window_html += '			<tr>';
    window_html += '				<td class="' + fw.window_class + '_top_left_td"></td>';
    window_html += '				<td class="' + fw.window_class + '_top_center_td">';
    window_html += '					<div class="' + fw.window_class + '_title_container">';
    window_html += '						<h4 id="' + fw.window_id + '_title" class="' + fw.window_class + '_title">&nbsp;</h4>';
    window_html += '					</div>';
    window_html += '				</td>';
    window_html += '				<td class="' + fw.window_class + '_top_right_td"></td>';
    window_html += '			</tr>';
    window_html += '			<tr>';
    window_html += '				<td class="' + fw.window_class + '_middle_left_td"></td>';
    window_html += '				<td id="' + fw.window_id + '_middle_center_td" class="' + fw.window_class + '_middle_center_td">';
    window_html += '				  <div id="' + fw.window_id + '_content_container" class="' + fw.window_class + '_content_container"></div>';
    window_html += '				</td>';
    window_html += '				<td class="' + fw.window_class + '_middle_right_td"></td>';
    window_html += '			</tr>';
    window_html += '			<tr>';
    window_html += '				<td class="' + fw.window_class + '_bottom_left_td"></td>';
    window_html += '				<td class="' + fw.window_class + '_bottom_center_td"></td>';
    window_html += '				<td class="' + fw.window_class + '_bottom_right_td"></td>';
    window_html += '			</tr>';
    window_html += '		</table>';
  
    fw.window_obj = $(window_html).appendTo('body');
		fw.window_title_obj = fw.window_obj.find("#" + fw.window_id + '_title');
		fw.window_content_obj = fw.window_obj.find("#" + fw.window_id + '_content_container');

    if(fw.add_close_button)   fw.close_button_obj     = $('<div id="' + fw.window_id + '_close_button" class="' + fw.window_class + '_close_button"></div>').appendTo('#' + fw.window_id + ' .' + fw.window_class + '_title_container');
    if(fw.resizable)          fw.resizable_button_obj = $('<div id="' + fw.window_id + '_max_min_button" class="' + fw.window_class + '_maximize_button"></div>').appendTo('#' + fw.window_id + ' .' + fw.window_class + '_title_container');
  
    fw.left_td_width = parseInt(fw.window_obj.find('tr:eq(0) td:eq(0)').css('width'));
    fw.right_td_width = parseInt(fw.window_obj.find('tr:eq(0) td:eq(2)').css('width'));

  	fw.window_obj.find('td:not(#' + fw.window_id + '_middle_center_td)').cssPNGFix();
  };

  /**
   * Trigger a callback script after the window is placed in the DOM.
   */
  fw.onLoadHandler = function() {
    if(fw.onload_callback) eval(fw.onload_callback);
  };

  /**
   * Trigger a callback script after the content is placed after an AJAX response
   */
  fw.onAJAXLoadHandler = function() {
    if(fw.ajax_success_callback) eval(fw.ajax_success_callback);
  };

  /**
   * Sets the content for the floating window
   */
  fw.setContent = function() {
    fw.new_content_loaded = false;
  
    // If there's a drupal function given, we'll get the content by an AJAX request
    if(fw.content_properties.ajax_params) {
      if(!fw.ajax_done) {
        fw.getAJAXContent();
        return;
      };
      fw.ajax_done = false;
    };
  
		fw.stopLoading();
  
    if(fw.reload_content_after_messages) {
      fw.window_title_obj.html(fw.content_properties.title);
      fw.window_content_obj.html(fw.content_properties.content);
      fw.new_content_loaded = true;
    };
		
    // reset reload_content_after_messages
    fw.reload_content_after_messages = true;
		
		if(!fw.dragged) fw.align(); // Only align if the window has not been dragged
		
		fw.window_obj.css({'visibility':'visible'});

    fw.onAJAXLoadHandler();

		// Add event listeners to the AJAX loaded content
		if(fw.content_properties.ajax_params) fw.contentAddEventListeners();
  };

  /**
   * Gets the content for the floating window by an AJAX request
   */
  fw.getAJAXContent = function() {
    fw.content_properties.ajax_params.clicked_url = fw.clicked_url;
    $.ajax({
      url:Drupal.settings.float_window_ajax_response_path, 
      data:fw.content_properties.ajax_params,
      dataType:'json',
      type:'POST',
      success:function(json) {
        if(json.content)                fw.content_properties.content = json.content;
        if(json.title)                  fw.content_properties.title = json.title;
        if(json.height)                 fw.min_height = json.height;
        if(json.width)                  fw.min_width = json.width;
        if(json.messages)               fw.handleResponseMessages(json.messages, json.redirect);
        if(json.ajax_success_callback)  fw.ajax_success_callback = json.ajax_success_callback;

        if(!json.messages && !json.content && json.redirect && fw.reload_content_after_messages) {
          fw.redirect(json.redirect);
        };

        // If a submit function has set $form_state['close_float_window'] to TRUE, 
        // the window will be closed and reinitialized.
        if(json.close) {
          fw.hideWindow(true);
        }
        else {
          fw.window_content_obj.find('.form-submit').removeAttr('disabled');
          fw.form_disabled = false;
          fw.ajax_done = true;
          fw.setContent();
        };
      }
    });
  };

  /**
   * Add event listeners to the AJAX loaded content
   */
  fw.contentAddEventListeners = function() {
    if(fw.new_content_loaded) {
      fw.window_content_obj.find('.form-submit').not('[float_window_content_func]').click(function() {
        // Avoid multiple submits before the AJAX request gets it's response
        $(this).attr('disabled', 'disabled');
        fw.form_disabled = true;

        // To avoid a submit when the user clicks ENTER after selecting a result of an autocomplete list,
        // we'll just check weither the autocomplete div exists or not.
        if ($('#autocomplete').size() == 0)  {
          fw.contentFormSubmit($(this).val());
        }
        return false;
      });
    };
  
    // Here we look for elements with the attribute float_window_content_func. 
    // If the element is clicked, the form of the value of float_window_content_func replaces the current content
    fw.window_content_obj.find('[float_window_content_func]').click(function() {
      var form_args = {float_window_content_func:$(this).attr('float_window_content_func')};
      if(fw.content_properties.ajax_params.is_form) form_args.is_form = true;
      fw.content_properties.ajax_params = form_args;
      fw.dragged = false;
      fw.startLoading();
      fw.setContent();
      return false;
    });
  
    if(window.floatwin_show_buttons) {
      $.each(window.floatwin_show_buttons, function(winid, selector) {
        fw.window_obj.find(selector).click(function() {
          window.float_windows[winid].showWindow();
          return false;
        });
      });
    };

    fw.window_content_obj.find('.form-text.error').keyup(function() {
      if($(this).val() !== '') {
        $(this).removeClass('error');
        $(this).unbind('keyup');
      };
    });
  
    if(fw.window_content_obj.find('input[@type=file]').size() > 0) {
      // If the ajaxFileUpload is not insitialized, create an instance first
      if(!Drupal.ajax_file_upload) {
        Drupal.ajax_file_upload = new ajaxUpload();
      } else {
        if(fw.new_content_loaded) {
          Drupal.ajax_file_upload.init();
        };
      };
    };
  
    Drupal.behaviors.collapse();
  };

  /**
   * Displays drupal messages and/or errors returned by an AJAX request
   */
  fw.handleResponseMessages = function(messages, redirect) {
    fw.reload_content_after_messages = (!messages.form && !messages.error);
    var may_redirect = (fw.reload_content_after_messages && redirect);

    if(Drupal.inline_dialog) {
      if(messages.form) {
        if(!messages.error) messages.error = [];
        Drupal.inline_dialog.wrong_fields = [];
        for(var key in messages.form) {
          // Add the form error to the inline_dialog wrong fields array
          Drupal.inline_dialog.wrong_fields.push('edit-' + key);
          // Store the form error in the errors array
          messages.error.push(messages.form[key]);
        };
        delete(messages.form);
      };
      Drupal.settings.messages = messages;
      Drupal.inline_dialog.drupalMessages();
    } else {
      for(var type in messages) {
        var result = '';
        for(var key in messages[type]) result += messages[type][key] + "\n";
        alert(result);
      };
    };
    if(may_redirect) fw.redirect(redirect, (Drupal.inline_dialog));
  };

  /**
   * If the form has set a #redirect, we will do this using javascript
   */
  fw.redirect = function(direction, use_inline_dialog) {
    if(use_inline_dialog) {
      Drupal.inline_dialog.redirect = direction;
    } else {
      window.location = direction;
    };
  };

  /**
   * Handle the form submition. This will be don by an AJAX request, instead of having a page refresh
   */
  fw.contentFormSubmit = function(op) {
    // trace('contentFormSubmit');
    // Store all form values in an object, so we can pass it as ajax parameters
    var form_args = {
      float_window_content_func:fw.window_content_obj.find('input[@name=form_id]').attr('value'),
      is_form:fw.content_properties.ajax_params.is_form
    };
    var form_elements = ['input', 'textarea', 'select'];
  
    // lets loop through all valid form elements and store it in form_values
    // We'll pass it to the AJAX function as arguments so Drupal will get the form values
    for(var i = 0; i < form_elements.length; i++) {
      fw.window_content_obj.find(form_elements[i]).each(function() {
        if($(this).attr('type') == 'checkbox') {
          form_args[$(this).attr('name')] = ($(this).attr('checked')) ? $(this).val() : 0;
        } else {
          form_args[$(this).attr('name')] = ($(this).val()) || '';
        };
      });
    };
    // Set the submittion operation
    form_args['op'] = op;
    // If we need to use the fileupload from the ajax_file_upload module, initialize the processFormFiles function
    // if(Drupal.ajax_file_upload) Drupal.ajax_file_upload.processFormFiles();
    fw.content_properties.ajax_params = form_args;
    fw.setContent();
  
    // Fixes a bug in firefox
    $('input:text').attr({'autocomplete':'off'}).blur();
  
    // In order to let user_register_validate() validate the name, we need to add '/register'
    // In user_register_validate() arg(1) must be 'register'
  };

  /**
   * Shows the floating window
   */
  fw.showWindow = function(content_properties) {
    if(content_properties) fw.content_properties = content_properties;
    fw.window_obj.swapDepths({reinit:true});
    fw.setContent(fw.content_proerties);
  };

  /**
   * Hides the window
   * @param reload bool Wheter or not to reload the ajax params. 
   *                    If this is false or not set, all form values are re-sent after reopening the window.
   */
  fw.hideWindow = function(reload) {
    if(reload) {
      fw.content_properties.ajax_params = {
        float_window_content_func:fw.content_properties.ajax_params.float_window_content_func,
        is_form:fw.content_properties.ajax_params.is_form
      };
    };
    fw.window_visible = false;
    fw.window_obj.css({'visibility':'hidden'});
  };

  /**
   * Shows a loading image inside the content container
   */
  fw.startLoading = function() {
    if(!fw.loading_img) {
      fw.loading_img = $('<div />').css({
        'background':'url(' + Drupal.settings.float_window_module_path + '/images/loading.gif) 50% 50% no-repeat',
        'width':'100%'
      });
    };
    fw.loading_img.height(fw.window_content_obj.height());
		fw.window_content_obj.html('').parent().append(fw.loading_img);
  };

  /**
   * Hides the loading image inside the content container
   */
  fw.stopLoading = function() {
    if(fw.loading_img) fw.loading_img.remove();
  };

  /**
   * Aligns the window to the browsers window
   */
  fw.align = function() {
    fw.getBrowserWindowDimensions();
    fw.getFloatWindowDimensions();
  
    fw.window_obj.css({
      height  :($.browser.msie && $.browser.version < 7) ? '10px' : 'auto',
      width   :fw.float_win_dimensions.width + 'px',
      top     :fw.float_win_dimensions.top + fw.browser_win_dimensions.scroll_top + 'px',
      left    :fw.float_win_dimensions.left + 'px'
    });
  };

  /** 
   * Returns an object of the windows dimensions
   */
  fw.addEventListeners = function(init) {
    // Only on initialization
    if(init === true) {
      if(fw.resizable) {
        // Add the click handler to the resize button
        fw.resizable_button_obj.click(function() {
          fw.dragged = true;
          fw.max_min();
        });
        // Add the doubleclick handler to the window's title
        fw.window_title_obj.dblclick(function() {
          fw.window_obj.swapDepths({reinit:true});
          fw.max_min();
        }).mousedown(function() {
          fw.window_obj.swapDepths({reinit:true});
          fw.dragged = true;
        });
      };

      fw.window_obj.mousedown(function() {
        fw.window_obj.swapDepths({reinit:true});
      });

      // If there's a close button....
      if(fw.add_close_button) {
        fw.close_button_obj.click(function() {
          fw.dragged = true;
          fw.hideWindow();
        });
      };

      // If the browser window is resized, check whether the floating window is maximized
      // If so, re-maximize the floating window, else re-align the floating window
      if(!$.browser.msie) {
        $(window).resize(function() {
          if(!fw.maximized) {
            fw.align();
          } else {
            fw.maximize();
          };
        });
      };

      // Add the drag functionality to the window's title bar
      if(fw.draggable) fw.window_obj.jqDrag(fw.window_title_obj);

      // The show_button is a selector for the DOM element(s) which will show the window after clicking on it.
      if(fw.show_button) {
        if(!window.floatwin_show_buttons) window.floatwin_show_buttons = {};
        window.floatwin_show_buttons[fw.window_id] = fw.show_button;
  
        $(fw.show_button).click(function() {
          fw.clicked_url = $(this).attr('href');
          fw.showWindow();
          return false;
        });
      };
    };
  };

	/**
	 * Toggles maximization
	 */
	fw.max_min = function() {
		if(fw.maximized == false) {
			fw.maximize();
		} else {
			fw.minimize();
		};
	};

	/**
	 * Maximizes the floating window
	 */
  fw.maximize = function() {
    $('body').css({
      overflow:'hidden'
    });
  
    fw.getFloatWindowDimensions();
    fw.getBrowserWindowDimensions();

    // Lets fix the height of the content container.
    // If the content is larger than the container, scrollbars are added
    fw.window_content_obj.css({
      height    :fw.browser_win_dimensions.height + (fw.margin.top + fw.margin.bottom) - (fw.window_obj.find('tr:eq(2) td:eq(0)').height() + fw.window_obj.find('tr:eq(0) td:eq(0)').height()),
      overflow  :'auto'
    });
    fw.window_obj.css({
      width   : fw.browser_win_dimensions.width + fw.margin.left + fw.margin.right + 'px',
      height  : fw.browser_win_dimensions.height + fw.margin.top + fw.margin.bottom + 'px',
      top     : 0 - fw.margin.top + fw.browser_win_dimensions.scroll_top + 'px',
      left    : 0 - fw.margin.left + 'px'
    });

  	if(fw.draggable && fw.window_obj.deljqDrag) {
      fw.window_obj.deljqDrag();
    };

    fw.resizable_button_obj.addClass('maximized');

    fw.maximized = true;
	};

	/**
	 * Minimizes the floating window
	 */
  fw.minimize = function() {
    $('body').css({
      overflow : 'visible'
    });
  
    fw.window_content_obj.css({
      height    :'auto',
      overflow  :'visible'
    });
  
    fw.getBrowserWindowDimensions();
  
    fw.window_obj.css({
      width   : fw.float_win_dimensions.width + 'px',
      height  : 'auto',
      top     : fw.float_win_dimensions.top + 'px',
      left    : fw.float_win_dimensions.left + 'px'
    });
  	if(fw.draggable) fw.window_obj.jqDrag(fw.window_title_obj);

    fw.resizable_button_obj.removeClass('maximized');

    fw.maximized = false;
	};

  /** 
   * Returns an object of the browser window's dimensions
   */
  fw.getBrowserWindowDimensions = function() {
    fw.browser_win_dimensions = {
      width:        window.innerWidth   || document.documentElement.clientWidth   || document.body.clientWidth    || 0,
      height:       window.innerHeight  || document.documentElement.clientHeight  || document.body.clientHeight   || 0,
      scroll_left:  window.pageXOffset  || document.documentElement.scrollLeft    || document.body.scrollLeft     || 0,
      scroll_top:   window.pageYOffset  || document.documentElement.scrollTop     || document.body.scrollTop      || 0,
      inner_width:  window.innerWidth   || document.documentElement.scrollWidth   || document.body.scrollWidth    || 0,
      inner_height: window.innerHeight  || document.documentElement.scrollHeight  || document.body.scrollHeight   || 0
    };
    return fw.browser_win_dimensions;
  };

  /** 
   * Returns an object of the float window's dimensions
   * If the window hasn't been dragged before, the positions will be recalculated
   */
  fw.getFloatWindowDimensions = function() {
    fw.float_win_dimensions.width   = (fw.dragged) ? parseInt(fw.window_obj.css('width')) : fw.calculateWidth();
    fw.float_win_dimensions.height  = (fw.dragged) ? parseInt(fw.window_obj.css('height')) : fw.calculateHeight();
    fw.float_win_dimensions.left    = (fw.dragged) ? parseInt(fw.window_obj.css('left')) : fw.calculateLeft();
    fw.float_win_dimensions.top     = (fw.dragged) ? parseInt(fw.window_obj.css('top')) : fw.calculateTop();
    return fw.float_win_dimensions;
  };
  
  /**
   * Calculate the width for the floating window
   * If the width of the title is wider than the with gaven as setting, the title width will override the width in the options
   */
  fw.calculateWidth = function() {
    // First we need to check whether the target width is larger than the tilte width.
    // If not, we need to use the title width.
    var header = fw.window_obj.find('tr:eq(0) td:eq(1) div');
    var h4 = header.find('h4');
    // Create a span to calculate the text inside the header title
    var header_temp = $('<span></span>').css({'opacity':'0', 'font-size':h4.css('font-size'), 'font-weight':h4.css('font-weight'), 'font-family':h4.css('font-family')}).html(h4.html()).appendTo('body');
    var title_width = header_temp.width();
    header_temp.remove();
    // Let's add the widths of all buttons to the title width
    header.find('div').each(function () {
      title_width += (parseInt($(this).css('width')) * 2); // We double this while we need the space for both sides of the title
    });
  
    // Get the content width
    var content_width = ($.browser.msie) ? fw.window_content_obj.width() : parseInt(fw.window_content_obj.css('width'));
    var inner_width = (content_width > title_width) ? content_width : title_width;
    var use_width = (fw.min_width > inner_width) ? fw.min_width : inner_width;
  
    // At last we need to check wheter the browsers window isn't smaller than the targer width
    if(use_width > fw.browser_win_dimensions.width) use_width = fw.browser_win_dimensions.width;

    return fw.left_td_width + fw.right_td_width + use_width;
  };

  /**
   * Calculate the height for the floating window
   */
  fw.calculateHeight = function() {
    var content_height = parseInt(fw.window_obj.height());
    return (content_height > fw.min_height) ? content_height : fw.min_height;
  };

  /**
   * Calculate the left position for the floating window
   */
  fw.calculateLeft = function() {
    var left = ((fw.browser_win_dimensions.width / 2) - (fw.float_win_dimensions.width / 2));
    return (left > 0) ? left - fw.margin.left : 0 - fw.margin.left;
  };

  /**
   * Calculate the top position for the floating window
   */
  fw.calculateTop = function() {
    var top = ((fw.browser_win_dimensions.height / 2) - (fw.float_win_dimensions.height / 2));
    return (top > 0) ? top - fw.margin.top : 0 - fw.margin.top;
  };
};


/**
 * Load appropriate scripts for the current page
 */
$(function() {
  // Check for floating window configurations
  if(window.float_windows_configurations !== undefined) {
    // There is at least one configuration. Create an object to store all floating windows
    window.float_windows = {};
  
    for(var i in float_windows_configurations) {
      // Create an instance of a floating window
      window.float_windows[float_windows_configurations[i].window_id] = new floatWin(float_windows_configurations[i]);
      window.float_windows[float_windows_configurations[i].window_id].init();
    };
    delete(float_windows_configurations);
  };
});
