// Copyright (c) 2006-2007 Sébastien Gruhier
// (http://xilinus.com, http://itseb.com)
// 
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// VERSION 1.9b2

showWindow = function(params,url,refelement,updateparams)
	{
		var win = new Window(params);
		win.show(); 
		var contentDiv = $(win.getId()+'_content');
		contentDiv.setAttribute('refelement',refelement);
    if(updateparams)
    {
		  new Ajax.Updater(win.getId()+'_content',url, {method:'post', asynchronous:true, evalScripts:true, parameters: updateparams});
    } else {
		  new Ajax.Updater(win.getId()+'_content',url, {method:'get', asynchronous:true, evalScripts:true, parameters: 'window='+win.getId()+'_content'});
    }
	}


var Window = Class.create();

Window.keepMultiModalWindow = false;
Window.hasEffectLib = !Object.isUndefined(Effect);
Window.resizeEffectDuration = 0.4;

Window.prototype = {
  // Constructor
  // Available parameters : className, blurClassName, title, minWidth, minHeight, maxWidth, maxHeight, width, height, top, left, bottom, right, resizable, zIndex, opacity, recenterAuto, wiredDrag
  //                        hideEffect, showEffect, showEffectOptions, hideEffectOptions, effectOptions, url, draggable, closable, minimizable, maximizable, parent, onload
  //                        add all callbacks (if you do not use an observer)
  //                        onDestroy onStartResize onStartMove onResize onMove onEndResize onEndMove onFocus onBlur onBeforeShow onShow onHide onMinimize onMaximize onClose
  
  initialize: function() {
    var id;
    var optionIndex = 0;
    // For backward compatibility like win = new Window("id", {...}) instead of win = new Window({id: "id", ...})
    if (arguments.length > 0) {
      if (typeof arguments[0] == 'string' ) {
        id = arguments[0];
        optionIndex = 1;
      }
      else
        id = arguments[0] ? arguments[0].id : null;
    }

    // Generate unique ID if not specified
    if (!id)
      id = 'window_' + new Date().getTime();
      
    if ($(id))
      alert('Window ' + id + ' is already registered in the DOM! Make sure you use setDestroyOnClose() or destroyOnClose: true in the constructor');

    this.id = id;
    
    this.options = Object.extend({
      className:         'dialog',
      blurClassName:     null,
      minWidth:          120, 
      minHeight:         40,
      maxWidth:          null, 
      maxHeight:         null,
      resizable:         true,
      closable:          true,
      closeKeyCode:      Event.KEY_ESC,
      closeOnKey:        false,
      minimizable:       true,
      maximizable:       true,
      draggable:         true,
      haveShadow:        false,
      ieShadow:          'color=#AAAAAA, direction=135, strength=6',
      userData:          null,
      showEffect:        (Window.hasEffectLib ? Effect.Appear : Element.show),
      hideEffect:        (Window.hasEffectLib ? Effect.Fade : Element.hide),
      showEffectOptions: {},
      hideEffectOptions: {},
      effectOptions:     null,
      parent:            document.body,
      title:             '',
      url:               null,
      onload:            Prototype.emptyFunction,
      width:             200,
      height:            300,
      opacity:           1,
      recenterAuto:      true,
      showCentered:      false,
      wiredDrag:         false,
      closeCallback:     null,
      destroyOnClose:    false,
      gridX:             1, 
      gridY:             1      
    }, arguments[optionIndex] || {});

    if (this.options.blurClassName)
      this.options.focusClassName = this.options.className;
      
    if (Object.isUndefined(this.options.top) &&  Object.isUndefined(this.options.bottom))
      this.options.top = (document.viewport.getHeight() - this.options.height) / 2;
    if (Object.isUndefined(this.options.left) && Object.isUndefined(this.options.right))
      this.options.left = (document.viewport.getWidth() - this.options.width) / 2;
    if (this.options.effectOptions) {
      Object.extend(this.options.hideEffectOptions, this.options.effectOptions);
      Object.extend(this.options.showEffectOptions, this.options.effectOptions);
      if (this.options.showEffect == Element.Appear)
        this.options.showEffectOptions.to = this.options.opacity;
    }
    if (Window.hasEffectLib) {
      if (this.options.showEffect == Effect.Appear)
        this.options.showEffectOptions.to = this.options.opacity;
    
      if (this.options.hideEffect == Effect.Fade)
        this.options.hideEffectOptions.from = this.options.opacity;
    }
    if (this.options.hideEffect == Element.hide)
      this.options.hideEffect = function(){ Element.hide(this.element); if (this.options.destroyOnClose) this.destroy(); }.bind(this)

    if (this.options.parent != document.body)  
      this.options.parent = $(this.options.parent);
    
    if (this.options.showCentered)
      this.centered = true;
        
    this.width = this.options.width;
    this.height = this.options.height;

    this.constraint = false;
    this.constraintPad = {top: 0, left:0, bottom:0, right:0};

    this._createWindow(id);
    Windows.register(this);

    this.setSize(this.options.width, this.options.height);

    this._setPosition();

    // Bind event listener
    this.eventMouseDown = this._initDrag.bindAsEventListener(this);
    this.eventMouseUp   = this._endDrag.bindAsEventListener(this);
    this.eventMouseMove = this._updateDrag.bindAsEventListener(this);
    this.eventMouseDownContent = this.toFront.bindAsEventListener(this);
    this.eventResize = this._recenter.bindAsEventListener(this);
    this.eventCloseKey = this._closeOnKey.bindAsEventListener(this);
 
    Event.observe(this.titleBar, 'mousedown', this.eventMouseDown);
    Event.observe(this.statusBar, 'mousedown', this.eventMouseDown);
    Event.observe(this.content, 'mousedown', this.eventMouseDownContent);
    Event.observe(window, 'resize', this.eventResize);
    Event.observe(window, 'scroll', this.eventResize);
    Event.observe(this.options.parent, 'scroll', this.eventResize);
    
    if (this.options.draggable)  {
      this.titleBar.addClassName('top_draggable');
      this.statusBar.addClassName('bottom_draggable');
    }
    
    if (this.options.resizable)
      Event.observe(this.sizer, 'mousedown', this.eventMouseDown);
    
    if (this.options.closeOnKey) this.setCloseOnKey();

    this.storedLocation = null;
    
    if (this.options.zIndex) this.setZIndex(this.options.zIndex);
  },
  
  // Destructor
  destroy: function() {
    this._notify('onDestroy');
    Event.stopObserving(this.titleBar, 'mousedown', this.eventMouseDown);
    Event.stopObserving(this.statusBar, 'mousedown', this.eventMouseDown);
    Event.stopObserving(this.content, 'mousedown', this.eventMouseDownContent);
    
    Event.stopObserving(window, 'resize', this.eventResize);
    Event.stopObserving(window, 'scroll', this.eventResize);
    Event.stopObserving(this.options.parent, 'scroll', this.eventResize);
    
    Event.stopObserving(this.content, 'load', this.options.onload);

    if (this._oldParent) {
      var content = this.getContent();
      var originalContent = null;
      for(var i = 0; i < content.childNodes.length; i++) {
        originalContent = content.childNodes[i];
        if (originalContent.nodeType == 1) 
          break;
        originalContent = null;
      }
      if (originalContent)
        this._oldParent.appendChild(originalContent);
      this._oldParent = null;
    }

    if (this.sizer)
      Event.stopObserving(this.sizer, 'mousedown', this.eventMouseDown);

    if (this.options.closeOnKey) {
      Event.stopObserving(document, 'keydown', this.eventCloseKey);
    }
    
    if (this.options.url) 
      this.content.src = null

	if ($('wired_frame'))
	  $('wired_frame').remove();
	
    Element.remove(this.element);
    Windows.unregister(this);      
  },

  // Creates HTML window code
  _createWindow: function(id) {
    var className = this.options.className;
    
    if (!$('overlay_modal'))
      this.options.parent.insertBefore(this._createOverlay(), this.options.parent.firstChild);

    this.element = new Element('div', {id: id, className: 'dialog'});
    this.element.setStyle({opacity: this.options.opacity});
    
    this.titleBar = new Element('h1', {id: id + '_top', className: className + '_title'}).update(this.options.title);
    var topDivs = new Element('div', {className: className + '_nw'}).insert(new Element('div', {className: className + '_ne'}).insert(this.titleBar));

    this.content = new Element('div', {id: id + '_content', className: className + '_content'});
    Event.observe(this.content, 'load', this.options.onload);
    if (this.options.url) this.content.insert(this._createIFrame(id));
    
    var midDivs = new Element('div', {className: className + '_w'}).insert(new Element('div', {className: className + '_e'}).insert(this.content));

    this.statusBar = new Element('div', {id: id + '_statusbar', className: className + '_statusbar'});
    this.sizer = new Element('div', {id: id + '_sizer', className: className + (this.options.resizable ? '_sizer' : '_se')});
    var bottomDivs = new Element('div', {className: className + '_sw'}).insert(this.sizer.insert(this.statusBar));

    this.element.insert(topDivs).insert(midDivs).insert(bottomDivs);

    if (this.options.haveShadow) {
	  if (Prototype.Browser.IE)
	    this.element.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(' + this.options.ieShadow + ')';
      else
      	this.element.insert(this._createShadow());
    }
    
    if (this.options.closable) {
      var closeLink = new Element('a', {href: '#', className: className + '_close'});
      closeLink.observe('click', function(event) {
        this.close();
        Event.stop(event);
      }.bindAsEventListener(this));

      this.element.insert(closeLink);
    }
    if (this.options.maximizable) {
      var maximizeLink = new Element('a', {href: '#', className: className + '_maximize'});
      maximizeLink.observe('click', function(event) {
        this.maximize();
        Event.stop(event);
      }.bindAsEventListener(this));

      this.element.insert(maximizeLink);
    }
    if (this.options.minimizable) {
      var minimizeLink = new Element('a', {href: '#', className: className + '_minimize'});
      minimizeLink.observe('click', function(event) {
        this.minimize();
        Event.stop(event);
      }.bindAsEventListener(this));

      this.element.insert(minimizeLink);
    }

    var indicator = new Element('div', {id: id + '_content_indicator', className: className + '_progress'});
    indicator.setStyle({display: 'none'});

    this.element.insert(indicator);

    this.element.hide();
    this.options.parent.insertBefore(this.element, this.options.parent.firstChild);

    this.borderHeight = this._getWindowBorderHeight();
  },
  
  _createIFrame: function(id) {
    this.iframe = new Element('iframe', {id: id + '_iframe', name: id + '_content', frameBorder: 0, src: this.options.url});
    return this.iframe;
  },
  
  _createOverlay: function() {
    var objOverlay = new Element('div', {id: 'overlay_modal', className: this.options.className + '_overlay'});
    objOverlay.hide();

    return objOverlay;
  },
  
  _createShadow: function() {
    this.shadow = new Element('div', {className: 'shadow'});
    var bottomShadow = new Element('div', {className: 'shadow_sw'}).insert(new Element('div', {className: 'shadow_se'}).insert(new Element('div', {className: 'shadow_s'})));
    
    this.shadow.insert(new Element('div', {className: 'shadow_ne'}));
    this.shadow.insert(new Element('div', {className: 'shadow_e'}));
    this.shadow.insert(bottomShadow);

    return this.shadow;
  },
  
  _getWindowBorderHeight: function() {
    if (this.element.visible())
      return this.titleBar.getHeight() + this.statusBar.getHeight();
    
    this.element.setStyle({visibility: 'hidden'}).show();
    var bordersHeight = this.titleBar.getHeight() + this.statusBar.getHeight();
    this.element.hide().setStyle({visibility: 'visible'});

    return bordersHeight;
  },

  _setPosition: function() {
  	if (Object.isUndefined(this.options.right))
  	  this.element.setStyle({left: parseFloat(this.options.left) + 'px'});
    else
      this.element.setStyle({right: parseFloat(this.options.right) + 'px'});

    if (Object.isUndefined(this.options.bottom))
      this.element.setStyle({top: parseFloat(this.options.top) + 'px'});
    else
      this.element.setStyle({bottom: parseFloat(this.options.bottom) + 'px'});

	this.element.setStyle({visibility: 'hidden'}).show();
    var offsetPosition = this.element.positionedOffset();
    this.element.hide().setStyle({visibility: 'visible'});  
    
    this.element.setStyle({right: null, bottom: null, left: offsetPosition[0] + 'px', top: offsetPosition[1] + 'px'});
  },

  _computeSize: function() {
    if (this.height)
      this.element.setStyle({'height': this.height + 'px'});
    
    if (this.width)
      this.element.setStyle({'width': this.width + 'px'});

    this.element.setStyle({visibility: 'hidden'}).show();

    var dim = this.element.getDimensions();
    if (!this.height) this.height = dim.height + 2;
    if (!this.width) this.width = dim.width + 2;

    this.element.hide().setStyle({visibility: 'visible'});
  },

  // Gets window content
  getContent: function () {
    return this.content;
  },

  getId: function () {
    return this.id;
  },

  getURL: function() {
    return this.options.url || null;
  },

  getTitle: function() {
    return this.titleBar.innerHTML;
  },

  getLocation: function() {
    return {top: this.element.getStyle('top'), left: this.element.getStyle('left')};
  },

  isVisible: function() {
    return this.element.visible();
  },

  isMinimized: function() {
    return this.minimized;
  },

  isMaximized: function() {
    return (this.storedLocation != null);
  },

  // Gets window size
  getSize: function() {
    return {width: this.width, height: this.height};
  },

  setZIndex: function(zindex) {
    this.element.setStyle({zIndex: zindex});
    Windows.updateZindex(zindex, this);
  },

  setTitle: function(newTitle) {
    text = String.interpret(newTitle);
      
    this.titleBar.update(text);
  },

  setStatusBar: function(element) {
    if (typeof(element) == 'object') {
      if (this.statusBar.firstChild)
        this.statusBar.replaceChild(element, this.statusBar.firstChild);
      else
        this.statusBar.appendChild(element);
    }
    else
      this.statusBar.innerHTML = element;
  },

  // Sets window location
  setLocation: function(top, left) {
    top = this._updateTopConstraint(top);
    left = this._updateLeftConstraint(left);

    var e = this.currentDrag || this.element;
    e.setStyle({top: top + 'px', left: left + 'px'});
  },

  // Sets window size
  setSize: function(width, height, useEffect) {    
    width = parseFloat(width);
    height = parseFloat(height);

    // Check min and max size
    if (!this.minimized && width < this.options.minWidth)
      width = this.options.minWidth;

    if (!this.minimized && height < this.options.minHeight)
      height = this.options.minHeight;
      
    if (this.options.maxHeight && height > this.options.maxHeight)
      height = this.options.maxHeight;

    if (this.options.maxWidth && width > this.options.maxWidth)
      width = this.options.maxWidth;
    
    this.width = width;
    this.height = height;

    if (this.element.visible() && Window.hasEffectLib && Effect.ResizeWindow && useEffect) {
      new Effect.ResizeWindow(this, null, null, this.width, height, {duration: Window.resizeEffectDuration});
    } else {
      var e = this.currentDrag || this.element;
      e.setStyle({width: this.width + 'px', height: this.height + 'px'})

      if (!this.currentDrag || this.currentDrag == this.element) {
        // Update content size
        this.content.setStyle({height: this.height - this.borderHeight + 'px'});
        
        // Resize Automatic Shadow
        if (this.shadow) this.element.down('.shadow_e').setStyle({height: (this.height - 20) + 'px'});
      }
    }
  },

  // Sets close callback, if it sets, it should return true to be able to close the window.
  setCloseCallback: function(callback) {
    this.options.closeCallback = callback;
  },

  // Sets the content with an element id
  setContent: function(id, autoresize, autoposition) {  // autoresize, autoposition why here???? move to other function / contruct function
    var element = $(id);
    if (null == element) throw 'Unable to find element "' + id + '" in DOM';
    this._oldParent = element.parentNode;

    if (autoresize) 
      d = Element.getDimensions(element);
    if (autoposition) 
      p = Element.cumulativeOffset(element);

    var content = this.getContent();
    // Clear HTML (and even iframe)
    this.setHTMLContent('');
    content = this.getContent();
    
    content.appendChild(element);
    element.show();
    if (autoresize) 
      this.setSize(d.width, d.height);
    if (autoposition) 
      this.setLocation(p[1], p[0]);
  },
  
  // Sets the content with HTML string
  setHTMLContent: function(html) {
    if (this.iframe) {
      this.iframe.remove();
      this.options.url = null;
      this.iframe = null;
    }

    this.content.update(String.interpret(html));
  },
  
  // Sets the content with Ajax
  setAjaxContent: function(url, options, showCentered, showModal) {  // showCentered, showModal why here???? move to other function / contruct function
    this.showFunction = showCentered ? 'showCenter' : 'show';
    this.showModal = showModal || false;
  
    options = options || {};

    this.onComplete = options.onComplete;
    if (!this._onCompleteHandler)
      this._onCompleteHandler = this._setAjaxContent.bind(this);
    options.onComplete = this._onCompleteHandler;

    new Ajax.Request(url, options);
    options.onComplete = this.onComplete;
  },
  
  _setAjaxContent: function(originalRequest) {
    this.setHTMLContent(originalRequest.responseText);
    if (this.onComplete)
      this.onComplete(originalRequest);
    this.onComplete = null;
    this[this.showFunction](this.showModal)
  },
  
  // Sets the content to an URL (or update current URL)
  setURL: function(url) {
    this.options.url = url;
    
    if (!this.iframe) this.content.update().insert(this._createIFrame(this.id));

    this.iframe.src = this.options.url;
  },

  // Detroys itself when closing 
  setDestroyOnClose: function() {
    this.options.destroyOnClose = true;
  },
  
  setConstraint: function(bool, padding) {
    this.constraint = bool;
    this.constraintPad = Object.extend(this.constraintPad, padding || {});
    // Reset location to apply constraint
    this.setLocation(parseFloat(this.element.style.top), parseFloat(this.element.style.left));
  },
  
  setCloseOnKey: function(keyCode) {
    if (keyCode) this.options.closeKeyCode = keyCode;
    this.options.closeOnKey = true;
    
    Event.observe(document, 'keydown', this.eventCloseKey);
  },

  _closeOnKey: function(event) {
    if (event.keyCode == this.options.closeKeyCode) this.close();
  },
  
  changeClassName: function(newClassName) {
    var allParts = ['_nw', '_ne', '_title', '_w', '_e', '_content', '_sw', '_sizer', '_statusbar', '_close', '_maximize', '_minimize'];
    
    allParts.each(function(value) {
      $$('#' + this.id + ' .' + this.options.className + value).invoke('removeClassName', this.options.className + value).invoke('addClassName', newClassName + value);
    }.bind(this));
    this.options.className = newClassName;
    
    this.borderHeight = this._getWindowBorderHeight();
  },

  // Brings window to front
  toFront: function() {
    if (this.element.style.zIndex < Windows.maxZIndex)  
      this.setZIndex(Windows.maxZIndex + 1);
  },

  // Displays window modal state or not
  show: function(modal) {
    if (modal) {
      Windows.addModalWindow(this);
      
      this.modal = true;      
      this.setZIndex(Windows.maxZIndex + 1);
      //Windows.unsetOverflow(this);
    } else {
      if (!this.element.style.zIndex) this.setZIndex(Windows.maxZIndex + 1);        
    }
    
    if (!this.width || !this.height)
      this._computeSize();
      
    this.setSize(this.width, this.height);

    if (this.centered)
      this._center(this.centerTop, this.centerLeft);
    
    this._notify('onBeforeShow');
    
    this.options.showEffect(this.element, this.options.showEffectOptions);  
      
    Windows.focusedWindow = this
    this._notify('onShow');   
  },

  // Displays window modal state or not at the center of the page
  showCenter: function(modal, top, left) {
    this.centered = true;
    this.centerTop = top;
    this.centerLeft = left;

    this.show(modal);
  },

  refresh: function() {
    if (this.iframe) this.iframe.src = this.options.url;
  },

  // Hides window
  hide: function() {
    if (this.modal) {
      Windows.removeModalWindow(this);
      //Windows.resetOverflow();
    }
    // To avoid bug on scrolling bar
    this.oldStyle = this.getContent().getStyle('overflow') || 'auto'
    this.getContent().setStyle({overflow: 'hidden'});

    this.options.hideEffect(this.element, this.options.hideEffectOptions);  

    if (!this.doNotNotifyHide)
      this._notify('onHide');
  },

  close: function() {
    // Asks closeCallback if exists
    if (this.element.visible()) {
      if (this.options.closeCallback && !this.options.closeCallback(this)) 
        return;

      if (this.options.destroyOnClose) {
        var destroyFunc = this.destroy.bind(this);
        if (this.options.hideEffectOptions.afterFinish) {
          var func = this.options.hideEffectOptions.afterFinish;
          this.options.hideEffectOptions.afterFinish = function() {func(); destroyFunc();}
        }
        else 
          this.options.hideEffectOptions.afterFinish = function() {destroyFunc();}
      }
      Windows.updateFocusedWindow();
      
      this.doNotNotifyHide = true;
      this.hide();
      this.doNotNotifyHide = false;
      this._notify('onClose');
    }
  },
  
  minimize: function() {
    if (this.isMaximized() || this.resizing)
      return;
    
    if (!this.minimized) {
      this.minimized = true;
      
      this.heightOrg = this.height;
      
      if (Window.hasEffectLib && Effect.ResizeWindow)
        new Effect.ResizeWindow(this, null, null, null, this.borderHeight, {duration: Window.resizeEffectDuration});
      else
        this.content.hide();
    } 
    else {
      this.minimized = false;

      if (Window.hasEffectLib && Effect.ResizeWindow)
        new Effect.ResizeWindow(this, null, null, null, this.heightOrg, {duration: Window.resizeEffectDuration});
      else
        this.content.show();

      this.toFront();
    }
    this._notify('onMinimize');
    
    // Store new location/size if need be
    this._saveCookie();
  },
  
  maximize: function() {
    if (this.isMinimized() || this.resizing)
      return;

    if (this.storedLocation != null) {
      this._restoreLocation();
      //Windows._showSelect();
    }
    else {
      this._storeLocation();
      
      var pageSize = Windows.getPageSize(this.options.parent);
      var left = pageSize.pageLeft;
      var top = pageSize.pageTop;

      if (this.constraint) {
        pageSize.windowWidth -= Math.max(0, this.constraintPad.left) + Math.max(0, this.constraintPad.right);
        pageSize.windowHeight -= Math.max(0, this.constraintPad.top) + Math.max(0, this.constraintPad.bottom);
        left +=  Math.max(0, this.constraintPad.left);
        top +=  Math.max(0, this.constraintPad.top);
      }

      var width = pageSize.windowWidth;
      var height = pageSize.windowHeight;

//      Windows._hideSelect();
//      Windows._showSelect(this.id);

      if (Window.hasEffectLib && Effect.ResizeWindow)
        new Effect.ResizeWindow(this, top, left, width, height, {duration: Window.resizeEffectDuration});
      else {
        if (this.shadow) this.shadow.hide();
        this.setSize(width, height);
        this.element.setStyle({left: left + 'px', top: top + 'px'});
      }

      this.toFront();
    }
    this._notify('onMaximize');

    // Store new location/size if need be
    this._saveCookie();
  },

  _center: function(top, left) {    
    var pageSize = Windows.getPageSize(this.options.parent);
    if (Object.isUndefined(top))
      top = (pageSize.windowHeight - (this.height))/2;
    top += pageSize.pageTop;
    
    if (Object.isUndefined(left))
      left = (pageSize.windowWidth - (this.width))/2;
    left += pageSize.pageLeft;
    this.setLocation(top, left);
    this.toFront();
  },
  
  _recenter: function(event) {     
    if (this.centered) {
      var pageSize = Windows.getPageSize(this.options.parent);

      // Check for this stupid IE that sends dumb events ?????
      if (this.pageSize && this.pageSize.windowWidth == pageSize.windowWidth && this.pageSize.windowHeight == pageSize.windowHeight && 
          this.pageSize.pageLeft == pageSize.pageLeft && this.pageSize.pageTop == pageSize.pageTop) 
        return;
      this.pageSize = pageSize;
      
      // set height of Overlay to take up whole page and show
      if ($('overlay_modal'))
        $('overlay_modal').setStyle({height: (pageSize.pageHeight + 'px')});
      
      if (this.options.recenterAuto)
        this._center(this.centerTop, this.centerLeft);    
    }
  },
  
  // initDrag event
  _initDrag: function(event) {
    // No resize on minimized window
    if (Event.element(event) == this.sizer && this.isMinimized())
      return;

    // No move on maximzed window
    if (Event.element(event) != this.sizer && this.isMaximized())
      return;

    // Get pointer X,Y
    this.pointer = [this._round(Event.pointerX(event), this.options.gridX), this._round(Event.pointerY(event), this.options.gridY)];
    if (this.options.wiredDrag) 
      this.currentDrag = this._createWiredElement();
    else
      this.currentDrag = this.element;
      
    // Resize
    if (Event.element(event) == this.sizer) {
      this.doResize = true;
      this.widthOrg = this.width;
      this.heightOrg = this.height;
      this.bottomOrg = parseFloat(this.element.getStyle('bottom'));
      this.rightOrg = parseFloat(this.element.getStyle('right'));
      this._notify('onStartResize');
    }
    else {
      this.doResize = false;
      this.toFront();

      if (!this.options.draggable) 
        return;
      this._notify('onStartMove');
    }    
    // Register global event to capture mouseUp and mouseMove
    Event.observe(document, 'mouseup', this.eventMouseUp);
    Event.observe(document, 'mousemove', this.eventMouseMove);
    
    // Stop selection while dragging
    document.body.ondrag = function () { return false; };
    document.body.onselectstart = function () { return false; };
    
    this.currentDrag.show();
    Event.stop(event);
  },

  // updateDrag event
  _updateDrag: function(event) {
    var pointer =  [this._round(Event.pointerX(event), this.options.gridX), this._round(Event.pointerY(event), this.options.gridY)];  
    var dx = pointer[0] - this.pointer[0];
    var dy = pointer[1] - this.pointer[1];
    
    // Resize case, update width/height
    if (this.doResize) {
      var w = this.widthOrg + dx;
      var h = this.heightOrg + dy;

      dx = this.width - this.widthOrg
      dy = this.height - this.heightOrg
      
      // Check if it's a right position, update it to keep upper-left corner at the same position
      w = this._updateWidthConstraint(w);
      h = this._updateHeightConstraint(h);

      this.setSize(w , h);
      this._notify('onResize');
    }
    // Move case, update top/left
    else {
      this.pointer = pointer;
      
      var left =  parseFloat(this.currentDrag.getStyle('left')) + dx;
      var newLeft = this._updateLeftConstraint(left);
      // Keep mouse pointer correct
      this.pointer[0] += newLeft-left;
      this.currentDrag.setStyle({left: newLeft + 'px'});

      var top =  parseFloat(this.currentDrag.getStyle('top')) + dy;
      var newTop = this._updateTopConstraint(top);
      // Keep mouse pointer correct
      this.pointer[1] += newTop - top;
      this.currentDrag.setStyle({top: newTop + 'px'});

      this._notify('onMove');
    }
      
    this.storedLocation = null;
    Event.stop(event);
  },

   // endDrag callback
   _endDrag: function(event) {
    if (this.doResize)
      this._notify('onEndResize');
    else
      this._notify('onEndMove');
    
    // Release event observing
    Event.stopObserving(document, 'mouseup', this.eventMouseUp);
    Event.stopObserving(document, 'mousemove', this.eventMouseMove);

    Event.stop(event);
    
    this._hideWiredElement();

    // Store new location/size if need be
    this._saveCookie()
      
    // Restore selection
    document.body.ondrag = null;
    document.body.onselectstart = null;
  },

  _updateLeftConstraint: function(left) {
    if (this.constraint) {
      var pageSize = Windows.getPageSize(this.options.parent);
      var constraintLeft = this.constraintPad.left + pageSize.pageLeft;
      var constraintRight = this.constraintPad.right - pageSize.pageLeft;

      if (left < constraintLeft)
        left = constraintLeft;
      if (left + this.width > pageSize.windowWidth - constraintRight) 
        left = pageSize.windowWidth - constraintRight - this.width;
    }
    return left;
  },
  
  _updateTopConstraint: function(top) {
    if (this.constraint) {
      var pageSize = Windows.getPageSize(this.options.parent);
      var constraintTop = this.constraintPad.top + pageSize.pageTop;
      var constraintBottom = this.constraintPad.bottom - pageSize.pageTop;

      if (top < constraintTop) top = constraintTop;

      if (top + this.height > pageSize.windowHeight - constraintBottom)
        top = pageSize.windowHeight - constraintBottom - this.height;
    }
    return top;
  },
  
  _updateWidthConstraint: function(width) {
    if (this.constraint) {
      var windowWidth = Windows.getPageSize(this.options.parent).windowWidth;
      var left = parseFloat(this.element.getStyle('left'));

      if (left + width > windowWidth - this.constraintPad.right)
        width = windowWidth - this.constraintPad.right - left;
    }
    return width;
  },
  
  _updateHeightConstraint: function(height) {
    if (this.constraint) {
      var windowHeight = Windows.getPageSize(this.options.parent).windowHeight;
      var top = parseFloat(this.element.getStyle('top'));

      if (top + height > windowHeight - this.constraintPad.bottom)
        height = windowHeight - this.constraintPad.bottom - top;
    }
    return height;
  },

  _storeLocation: function() {
    if (this.storedLocation == null)
      this.storedLocation = {top: this.element.getStyle('top'), left: this.element.getStyle('left'), width: this.width, height: this.height};
  },
  
  _restoreLocation: function() {
    if (this.storedLocation != null) {
      if (Window.hasEffectLib && Effect.ResizeWindow)
        new Effect.ResizeWindow(this, this.storedLocation.top, this.storedLocation.left, this.storedLocation.width, this.storedLocation.height, {duration: Window.resizeEffectDuration});
      else {
        this.element.setStyle({left: this.storedLocation.left, top: this.storedLocation.top});
        this.setSize(this.storedLocation.width, this.storedLocation.height);
        if (this.shadow) this.shadow.show();
      }
      
      this.storedLocation = null;
    }
  },

  // Stores position/size in a cookie, by default named with window id
  setCookie: function(name, expires, path, domain, secure) {
    name = name || this.id;
    this.cookie = [name, expires, path, domain, secure];

    // Get cookie
    var value = this.getCookie(name)
    // If exists
    if (value) {
      var values = value.split(',');
      var x = values[0].split(':');
      var y = values[1].split(':');

      var w = parseFloat(values[2]), h = parseFloat(values[3]);
      var mini = values[4];
      var maxi = values[5];

      this.setSize(w, h);
      if (mini == 'true')
        this.doMinimize = true; // Minimize will be done at onload window event
      else if (maxi == 'true')
        this.doMaximize = true; // Maximize will be done at onload window event

      this.element.setStyle({left: x[1]});
      this.element.setStyle({top: y[1]});
    }
  },

  getCookie: function(name) {
    var dc = document.cookie;
    var prefix = name + '=';
    var begin = dc.indexOf('; ' + prefix);
    if (begin == -1) {
      begin = dc.indexOf(prefix);
      if (begin != 0) return null;
    } else {
      begin += 2;
    }
    var end = document.cookie.indexOf(';', begin);
    if (end == -1) {
      end = dc.length;
    }
    return unescape(dc.substring(begin + prefix.length, end));
  },

  _saveCookie: function() {
    if (this.cookie) {
      var value = '';
      value += 'l:' +  (this.storedLocation ? this.storedLocation.left : this.element.getStyle('left'))
      value += ',t:' + (this.storedLocation ? this.storedLocation.top : this.element.getStyle('top'))
      value += ',' + (this.storedLocation ? this.storedLocation.width : this.width);
      value += ',' + (this.storedLocation ? this.storedLocation.height : this.height);
      value += ',' + this.isMinimized();
      value += ',' + this.isMaximized();

      parameters = this.cookie;
      document.cookie = parameters[0] + '=' + escape(value) +
      ((parameters[1]) ? '; expires=' + parameters[1].toGMTString() : '') +
      ((parameters[2]) ? '; path=' + parameters[2] : '') +
      ((parameters[3]) ? '; domain=' + parameters[3] : '') +
      ((parameters[4]) ? '; secure' : '');
    }
  },

  _createWiredElement: function() {
    var wiredElement = $('wired_frame');
    
    if (!wiredElement) {
      wiredElement = new Element('div', {id: 'wired_frame', className: this.options.className + '_wired_frame'});
      this.options.parent.insertBefore(wiredElement, this.options.parent.firstChild);
    }
    
    var dim = this.element.getDimensions();
    wiredElement.setStyle({left: this.element.getStyle('left'), top: this.element.getStyle('top'), width: dim.width + 'px', height: dim.height + 'px', zIndex: Windows.maxZIndex + 30});

    return wiredElement;
  },

  _hideWiredElement: function() {
    if (!$('wired_frame') || !this.currentDrag)
      return;
    if (this.currentDrag == this.element) 
      this.currentDrag = null;
    else {
      this.element.setStyle({left: this.currentDrag.getStyle('left'), top: this.currentDrag.getStyle('top')});

      this.currentDrag.hide();
      this.currentDrag = null;
      if (this.doResize) this.setSize(this.width, this.height);
    } 
  },

  _notify: function(eventName) {
    if (this.options[eventName])
      this.options[eventName](this);
    else
      Windows.notify(eventName, this);
  },

  _round: function(val, round) {
    return round == 1 ? val : val = Math.floor(val / round) * round;
  }
};

// Windows containers, register all page windows
var Windows = {
  windows: [],
  modalWindows: [],
  observers: [],
  focusedWindow: null,
  maxZIndex: 0,
  overlayShowEffectOptions: {duration: 0.5},
  overlayHideEffectOptions: {duration: 0.5},

  addObserver: function(observer) {
    this.removeObserver(observer);
    this.observers.push(observer);
  },
  
  removeObserver: function(observer) {  
    this.observers = this.observers.reject( function(o) { return o==observer });
  },
  
  // onDestroy onStartResize onStartMove onResize onMove onEndResize onEndMove onFocus onBlur onBeforeShow onShow onHide onMinimize onMaximize onClose
  notify: function(eventName, win) {  
    this.observers.each( function(o) {if(o[eventName]) o[eventName](eventName, win);});
  },

  // Gets window from its id
  getWindow: function(id) {
    return this.windows.find(function(d) { return d.id == id });
  },

  // Gets the last focused window
  getFocusedWindow: function() {
    return this.focusedWindow;
  },

  updateFocusedWindow: function() {
    this.focusedWindow = this.windows.length >= 2 ? this.windows[this.windows.length-2] : null; // ?????
  },

  // Registers a new window (called by Windows constructor)
  register: function(win) {
    this.windows.push(win);
  },

  // Unregisters a window (called by Windows destructor)
  unregister: function(win) {
    this.windows = this.windows.reject(function(d) { return d==win });
  }, 

  // Closes all windows
  closeAll: function() {  
    this.windows.each( function(w) {Windows.close(w.id)} );
  },

  closeAllModalWindows: function() {
    this.enableScreen('overlay_modal');     
    this.modalWindows.each( function(win) {if (win) win.close()});    
  },

  setAllWinCloseOnKey: function(keyCode) {  
    this.windows.each(function(win) {
    	win.setCloseOnKey(keyCode)
    });
  },
  
  // Minimizes a window with its id
  minimize: function(id, event) {
    var win = this.getWindow(id)
    if (win && win.visible)
      win.minimize();
    Event.stop(event);
  },
  
  // Maximizes a window with its id
  maximize: function(id, event) {
    var win = this.getWindow(id)
    if (win && win.visible)
      win.maximize();
    Event.stop(event);
  },

  // Closes a window with its id
  close: function(id, event) {
    var win = this.getWindow(id);
    if (win) 
      win.close();
    if (event)
      Event.stop(event);
  },
  
  blur: function(id) {
    var win = this.getWindow(id);  
    if (!win)
      return;
    if (win.options.blurClassName)
      win.changeClassName(win.options.blurClassName);
    if (this.focusedWindow == win)  
      this.focusedWindow = null;
    win._notify('onBlur');  
  },
  
  focus: function(id) {
    var win = this.getWindow(id);  
    if (!win)
      return;       
    if (this.focusedWindow)
      this.blur(this.focusedWindow.id)

    if (win.options.focusClassName)
      win.changeClassName(win.options.focusClassName);  
    this.focusedWindow = win;
    win._notify('onFocus');
  },

  updateZindex: function(zindex, win) { 
    if (zindex > this.maxZIndex) {   
      this.maxZIndex = zindex;    
      if (this.focusedWindow) 
        this.blur(this.focusedWindow.id)
    }
    this.focusedWindow = win;
    //if (this.focusedWindow) 
    this.focus(this.focusedWindow.id)
  },

  // Add a modal window in the stack
  addModalWindow: function(win) {
    // Disable screen if first modal window
    if (this.modalWindows.length == 0) {
      this.disableScreen('overlay_modal', win.id);
    }
    else {
      // Move overlay over all windows
      if (Window.keepMultiModalWindow) {
        $('overlay_modal').style.zIndex = Windows.maxZIndex + 1;
        Windows.maxZIndex += 1;
        this._hideSelect(this.modalWindows.last().id);
      }
      // Hide current modal window
      else
        this.modalWindows.last().element.hide();
      // Fucking IE select issue
      this._showSelect(win.id);
    }      
    this.modalWindows.push(win);    
  },
  
  removeModalWindow: function(win) {
    this.modalWindows.pop();
    
    // No more modal windows
    if (this.modalWindows.length == 0)
      this.enableScreen('overlay_modal');
    else {
      if (Window.keepMultiModalWindow) {
        this.modalWindows.last().toFront();
        this._showSelect(this.modalWindows.last().id);        
      }
      else
        this.modalWindows.last().element.show();
    }
  },

  enableScreen: function(id) {
    var objOverlay = $(id);
    
    if (objOverlay) {
      // hide lightbox and overlay
      if (Window.hasEffectLib && Windows.overlayHideEffectOptions)
        new Effect.Fade(objOverlay, Object.extend({from: objOverlay.getStyle('opacity'), to:0}, Windows.overlayHideEffectOptions));
      else {
        objOverlay.hide();
      }
      
      // make select boxes visible using old value
      this._showSelect();
    }
  },

  disableScreen: function(id, contentId) {
    var objOverlay = $(id);

    var pageSize = this.getPageSize(objOverlay.parentNode);

    // Hide select boxes as they will 'peek' through the image in IE, store old value
    if (contentId && Prototype.Browser.IE) {
      this._hideSelect();
      this._showSelect(contentId);
    }
  
    objOverlay.setStyle({zIndex: Windows.maxZIndex + 1});
    Windows.maxZIndex++;
    
    // set height of Overlay to take up whole page and show
    objOverlay.style.height = pageSize.pageHeight + 'px';
    if (Window.hasEffectLib && Windows.overlayShowEffectOptions) {
      new Effect.Appear(objOverlay, Object.extend({from: 0, to: objOverlay.getStyle('opacity')}, Windows.overlayShowEffectOptions));
    }
    else
      objOverlay.show();
  },

  getPageSize: function(parent){
    var pageSize, pageWidth, pageTop, pageLeft, windowSize;
    
    if (parent != document.body) {
      windowSize = parent.getDimensions();
      pageWidth = parent.scrollWidth;
      pageHeight = parent.scrollHeight;
      pageTop = parent.scrollTop;
      pageLeft = parent.scrollLeft;
    }
    else {
      windowSize = document.viewport.getDimensions();
      var scroolOffset = document.viewport.getScrollOffsets();
      
      pageWidth = (document.documentElement && document.documentElement.scrollWidth) ? document.documentElement.scrollWidth : (document.body.scrollWidth > document.body.offsetWidth) ? document.body.scrollWidth : document.body.offsetWidth;
      pageHeight = (document.documentElement && document.documentElement.scrollHeight) ? document.documentElement.scrollHeight : (document.body.scrollHeight > document.body.offsetHeight) ? document.body.scrollHeight : document.body.offsetHeight;
      pageTop = scroolOffset[1];
      pageLeft = scroolOffset[0];

      // for small pages with total height less then height of the viewport
      if(pageHeight < windowSize.height) pageHeight = windowSize.height;

      // for small pages with total width less then width of the viewport
      if(pageWidth < windowSize.width) pageWidth = windowSize.width;
    }

    return {pageWidth: pageWidth, pageHeight: pageHeight, windowWidth: windowSize.width, windowHeight: windowSize.height, pageTop: pageTop, pageLeft: pageLeft};
  },

  _hideSelect: function(id) {
    if (Prototype.Browser.IE) {
      id = id ==  null ? '' : '#' + id + ' ';
      $$(id + 'select').each(function(element) {
        if (Object.isUndefined(element.oldVisibility)) {
          element.oldVisibility = element.style.visibility ? element.style.visibility : 'visible';
          element.style.visibility = 'hidden';
        }
      });
    }
  },
  
  _showSelect: function(id) {
    if (Prototype.Browser.IE) {
      id = id ==  null ? '' : '#' + id + ' ';
      $$(id + 'select').each(function(element) {
        if (!Object.isUndefined(element.oldVisibility)) {
          // Why?? Ask IE
          try {
            element.style.visibility = element.oldVisibility;
          } catch(e) {
            element.style.visibility = 'visible';
          }
          element.oldVisibility = null;
        }
        else {
          if (element.style.visibility)
            element.style.visibility = 'visible';
        }
      });
    }
  }
};


var Dialog = {
  init: function(parameters) {
  	this.options = {
      parent:         document.body,
      className:      'dialog',
      height:         null,
      width:          null,
      resizable:      false,
      minimizable:    false,
      maximizable:    false,
      draggable:      false,
      closable:       false,
      okLabel:        'Ok',
      cancelLabel:    'Cancel',
      buttonClass:    '',
      showProgress:   false
    };
    
    parameters = parameters || {};
    
    this.options = Object.extend(this.options, parameters || {});
    // Backward compatibility    
    this.options = Object.extend(this.options, parameters.windowParameters || {});
  },
  
  confirm: function(content, parameters) {
    content = content || '';
	this.init(parameters);

    // Get Ajax return before
    if (!Object.isString(content)) {
      this._runAjaxRequest(content, this.options, Dialog.confirm);
      return;
    }
    
    var inputOk = new Element('input', {type: 'button', value: this.options.okLabel, className: this.options.buttonClass + ' cancel_button'});
    inputOk.onclick = this.okCallback.bindAsEventListener(this);
    
    var inputCancel = new Element('input', {type: 'button', value: this.options.cancelLabel, className: this.options.buttonClass + ' ok_button'});
    inputCancel.onclick = this.cancelCallback.bindAsEventListener(this);

    this.content = new Element('div', {className: this.options.className + '_message'}).insert(content).insert(new Element('div', {className: this.options.className + '_buttons'}).insert(inputOk).insert(inputCancel));

    return this._openDialog(this.content, this.options)
  },
  
  alert: function(content, parameters) {
    content = content || '';
	this.init(parameters);

    // Get Ajax return before
    if (!Object.isString(content)) {
      this._runAjaxRequest(content, this.options, Dialog.alert);
      return;
    }

    var input = new Element('input', {type: 'button', value: this.options.okLabel, className: this.options.buttonClass + ' ok_button'});
    input.onclick = this.okCallback.bindAsEventListener(this);

    this.content = new Element('div', {className: this.options.className + '_message'}).insert(content).insert(new Element('div', {className: this.options.className + '_buttons'}).insert(input));
    
    return this._openDialog(this.content, this.options)
  },
  
  info: function(content, parameters) {   
    content = content || '';
	this.init(parameters);

    // Get Ajax return before
    if (!Object.isString(content)) {
      this._runAjaxRequest(content, this.options, Dialog.info);
      return;
    }
    
    this.content = new Element('div', {id: 'modal_dialog_message', className: this.options.className + '_message'}).insert(content);
/*
    parameters.ok = null;
    parameters.cancel = null;
*/    
    return this._openDialog(this.content, this.options);
  },
  
  setInfoMessage: function(message) {
    $('modal_dialog_message').update(message);
  },
  
  closeInfo: function() {
    Windows.close(this.dialogId);
  },
  
  _openDialog: function(content, parameters) {
    if (!parameters.height && !parameters.width) {
      parameters.width = Windows.getPageSize(parameters.parent).pageWidth / 2;
    }
    
    if (parameters.id)
      this.dialogId = parameters.id;
    else { 
      var time = new Date().getTime();
      this.dialogId = 'modal_dialog_' + time;
      parameters.id = this.dialogId;
    }

    //parameters.effectOptions = parameters.effectOptions;
    
    var win = new Window(parameters);
    win.getContent().update(content);

    if (this.options.showProgress)
      win.getContent().insert(new Element('div', {id: 'modal_dialog_progress', className: this.options.className + '_progress'}));
    
    win.showCenter(true, parameters.top, parameters.left);  
    win.setDestroyOnClose();
    
    win.cancelCallback = parameters.onCancel || parameters.cancel; 
    win.okCallback = parameters.onOk || parameters.ok;
    
    return win;    
  },
  
  _getAjaxContent: function(originalRequest)  {
      Dialog.callFunc(originalRequest.responseText, Dialog.parameters)
  },
  
  _runAjaxRequest: function(message, parameters, callFunc) {
    if (message.options == null)
      message.options = {}  
    Dialog.onCompleteFunc = message.options.onComplete;
    Dialog.parameters = parameters;
    Dialog.callFunc = callFunc;
    
    message.options.onComplete = Dialog._getAjaxContent;
    new Ajax.Request(message.url, message.options);
  },
  
  okCallback: function() {
    var win = Windows.focusedWindow;
    if (!win.okCallback || win.okCallback(win)) {
      // Remove onclick on button
      $$('#' + win.id + ' input').each(function(element) {element.onclick=null;})
      win.close();
    }
  },

  cancelCallback: function() {
    var win = Windows.focusedWindow;
    // Remove onclick on button
    $$('#' + win.id + ' input').each(function(element) {element.onclick=null})
    win.close();
    if (win.cancelCallback)
      win.cancelCallback(win);
  }
};
