/***********************************************************

Dialogs
Copyright B-Lex Information Technologies 2010-2011

easy to use, fully stylable popup which makes the CSS autoposition the popup

- forces CSS to do all the positioning
  so the dialog will be centered even when resizing the browser window

- the popup can be anywhere in the HTML, including within a <form>.
  Therefore the fields in the popup can be a hidden part of the form
  (for example for an 'change address' button in an order process.

- dialogs element outside the document can also be used.
  When used these will always be placed in the document.body.


How to use:

  Opening a dialog:
    - dialogobject = showElementAsDialog("elementid", settings)
    - dialogobject = showURLInDialog("url", settings)

    - safe to use variables
        dialogobject.viewport -> topnode of the dialog
        dialogobject.body     -> refers to the element which was specified to use as dialog content

  Templates
    - when using a template, the element with the templatebodyid will be replaced with the element
      specified in showElementAsDialog.

    - if contentverticalalign is set to true the body (element you specified)
      will be wrapped in other elements to do the vertical alignment

    - if no wrappers were created (to for example vertical align)
      an contentminheight will be done by setting min-height on the body element

  Styling:
    How to add corners and a closebutton:
    - Option 1: include them in the HTML of the element you use in showElementAsDialog
    - Option 2: use setDialogTemplate()

    - the close button in a dialog can use the closeCurrentDialog() function to close.
      (note that this doesn't work if you show multiple popups at once)


Current issues:

  - side effect of setting min-height to the body element is that
    the resulting min-height will be the specified height + padding of the body

  - iphone/ipad treat fixed positioning somewhat like absolute positioning.
    To support iphone/ipad correctly you always need to use Javascript
    to reposition dialogs while scrolling.

  - only proper support at the moment for one open dialog at a time

  - Internet Explorer issues:
      - <table style="width: 100%;">

  - Internet Explorer with showURLInDialog:
      - the following can case the dialog to use the full width
          - <table style="width: 100%;">
          - "margin: 0 auto;" on a element within the iframe (in IE < 8 only?)


FIXME:
  - create a default popupsettings object
  - upon initialization the dialog override the default popupsettings with settings passed
  - properly document all settings

***********************************************************/

window.ws_dialogs = [];
window.modaldialog = null;
window.ws_dialogs_globalsettings =
      { pagewidth:       null // set to null if content on the page is centered, otherwise(content is at the left side of the page) specify the width of the content

      , template:        ""
      , templatebodyid:  ""   // id of the element in the template to replace with the dialog content element

      , contentminheight:     200
      , contentverticalalign: true

      , infopopuptemplate: ""
      , infopopuptemplatebodyid: ""

      , keepdomposition: false // false - dialog will be in the body, prevents z-index issues
                               // true  - dialog will occupy the same position in the DOM as the specified content element,
                               //         if the dialog is part of a form, the form will keep on working
      };



// to fix IE7 or IE8 specific problems ---------------------------------------------------------
var toddUserAgent = navigator.userAgent.toLowerCase();
var toddUserAgentIsOpera = (toddUserAgent.indexOf('opera') != -1);
var toddUserAgentIsIE = (typeof document.attachEvent != 'undefined') && (!toddUserAgentIsOpera);
if (toddUserAgentIsIE)
  var toddUserAgentIEVersion = toddGetIEVersion();
var toddUserAgentIsIE7 = toddUserAgentIsIE && toddUserAgentIEVersion == 7;
var toddUserAgentIsIE8 = toddUserAgentIsIE && toddUserAgentIEVersion == 8;
// --------------------------------------------------------------------------------------------


function setDialogTemplate(templatehtml, templatebodyid)
{
  window.ws_dialogs_globalsettings.template = templatehtml;
  window.ws_dialogs_globalsettings.templatebodyid = templatebodyid;
}

/** @param element
    @param settings
*/
function showElementAsDialog(element, settings)
{
  if (typeof element == "string")
    element = document.getElementById(element);

  // lookup if the given element was already changed into a dialog
  for(var tel=0; tel<window.ws_dialogs.length; tel++)
  {
    if (window.ws_dialogs[tel].body == element)
    {
      window.modaldialog = window.ws_dialogs[tel];
      window.ws_dialogs[tel].show();
      return window.ws_dialogs[tel];
    }
  }

  if(typeof settings == "undefined")
    settings = {};

  // convert the element to an dialog
  var newdialog = new wsdialog(element, settings);
  window.modaldialog = newdialog;

  window.ws_dialogs.push(newdialog);
  $(newdialog.body).fireEvent('whpopup-show'); //ADDME invoke show() ?
  return newdialog;
}

/** settings.allowresize allow resize by the user
    settings.iframewidth
    settings.iframeheight (if not set, the dialog will be resized to the size of the iframe's content and scrollbars will be disabled if possible)
*/
function showURLInDialog(url, settings)
{
  if (!settings)
    settings = {};

  if (typeof settings.visible == "undefined")
    settings.visible = false;

  var content = document.createElement("div");
  content.style.overflow = "auto";

  if (settings.allowresize)
    content.style.resize = "both";

  var frame = document.createElement('iframe');
  frame.style.cssText = 'resize: none; border: 0;';

  // note: setting height to 100% will make the dialog the full window height in Safari
  if (settings.iframewidth)
    frame.style.width = settings.iframewidth+"px";

  if (settings.iframeheight)
    frame.style.height = settings.iframeheight+"px";

  if (!settings.iframeheight)
    frame.style.overflowY = "hidden";

  frame.setAttribute('frameBorder', '0'); // IE ignores the border:0; style for iframs
  frame.setAttribute('noresize', 'noresize');
  frame.setAttribute('allowTransparency', 'true'); // IE shows an opaque background in the iframe without this
  content.appendChild(frame);

  settings.delayshow = true;
  var dialog = showElementAsDialog(content, settings);
  dialog.body.overflow = "hidden"; // let the iframe scroll, NOT the dialog body in case of showing a website
  dialog.frame = frame;
  dialog.closed = false;

  // FIXME: upon domcontentloaded and onload events explicitly also resize?
  dialog.prevsize = null;

  /*
  frame.addEvent("domready", function()
        { console.log("DOMREADY");
          dialog.handleIframeReady();
        });
  */

  dialog.body.addEvent("whpopup-beforehide", function()
    {
      //console.log("hide() while currently "+(this.visible?"visible":"hidden"));
      if (dialog.resizetimer)
        clearInterval(dialog.resizetimer);

      if(dialog.forceshowtimeout)
        clearTimeout(dialog.forceshowtimeout);

      if (toddUserAgentIsIE)
        dialog.frame.document.execCommand('Stop');
      else
        dialog.frame.contentWindow.stop();

      dialog.close();
    });

  frame.onload = function()
        {
          if (dialog.closed)
            return;
          //console.log("Onload");
          dialog.handleIframeReady();
        };

  frame.onreadystatechange = function()
        {
          if (dialog.closed)
            return;

          //console.log("readystatechange to "+frame.readyState);
          if (frame.readyState == "complete")
            dialog.handleIframeReady();
        };

  dialog.forceshowtimeout = setTimeout(function()
        {
          //console.log("show forced");
          dialog.handleIframeReady();
        }, 4000);

  frame.src = url;

  return dialog;
}

wsdialog.prototype.handleIframeReady = function()
{
  clearTimeout(this.forceshowtimeout);

  if (!this.settings.iframeheight)
    this.resizeToIframe();

  this.show();
}


function closeCurrentDialog()
{
  if (window.modaldialog)
  {
    window.modaldialog.hide();
    window.modaldialog = null;
  }
  else // if there isn't a dialog in this document, check if we ourselve are part of a dialog (in a iframe)
  {
    var frameelem = window.frameElement;
    if (!frameelem)
      return; // we don't seem to be running in an iframe

    // close the current dialog of our parent frame
    //var parentwin = window.frameElement.ownerDocument.window;
    //if (parentwin.closeCurrentDialog)
    //  parentwin.closeCurrentDialog();

    var parentwin = parent.document.window;
    var dialogobj = parentwin.getDialogByFrameRef(this.frameElement);
    dialogobj.hide();

    if (parentwin.modaldialog = dialogobj)
      parentwin.modaldialog = null;
  }
}

// FIXME: cannot be used within a iframe yet
function closeDialog(node)
{
  // lookup the dialog by the dialognode
  var dialoginstance = getDialogInstanceContainingNode(node);
  if (!dialoginstance)
    return;

  dialoginstance.hide();

  if (window.modaldialog = dialoginstance)
    window.modaldialog = null;
}

/**
  settings.canclose true/false if true, clicking outside the dialog will close the dialog
  settings.visible  whether the dialog must be visible initially
*/
function wsdialog(element, settings)
{
  /*
  this.viewport -> all HTML for the dialog (including positioning)
  this.lineheightstretcher -> dummy element needed for vertical alignment within the browser window
  this.dialog -> all HTML needed to make the dialog itself (including corners)
  this.body   -> the element specified to be the content of the dialog
  */

  if (!settings)
    settings = {};

  this.isdialog = true;
  this.visible = typeof settings.visible != "undefined" ? settings.visible : true;
  //this.reuse   = typeof settings.reuse   != "undefined" ? settings.reuse   : false; // upon hide() destroy the dialog
  this.forceshowtimeout = null;

  this.settings = settings;

  if (typeof element == "string")
    element = document.body.getElementById(element);

  if (!element)
  {
    console.error("Cannot open dialog, specified element does not exist. ("+element+")");
    return;
  }

  // create wrapper elements to vertical-align content if needed
  if (window.ws_dialogs_globalsettings.contentverticalalign)
  {
    // create elements to vertical align dialog content within the skin
    var dminheight = document.createElement("span");
    dminheight.style.display = "inline-block";
    dminheight.style.height = window.ws_dialogs_globalsettings.contentminheight+"px";
    dminheight.style.verticalAlign = "middle";

    var dalign = document.createElement("span");
    dalign.style.display = "inline-block";
    dalign.style.verticalAlign = "middle";

    var frag = document.createDocumentFragment();
    frag.appendChild(dminheight);
    frag.appendChild(dalign);
    dalign.appendChild(element);
  }
  else if (window.ws_dialogs_globalsettings.contentminheight)
    element.style.minHeight = window.ws_dialogs_globalsettings.contentminheight + "px";

  settings.canclose = (typeof settings.canclose != "undefined") ? settings.canclose : true;

  var elemparent  = element.parentNode;
  var elemsibling = element.nextSibling;

  var ismodal = !settings.isaffirmation;

  this.viewport = document.createElement("span");
  this.viewport.className = "ws_dialog_poscontainer";
  this.viewport.style.cssText = "position:fixed;z-index:50000;top:0;bottom:0;left:0;right:0;text-align:center;height:100%;";//white-space:nowrap;";
  if (!this.visible)
    this.viewport.style.visibility = "hidden";

  if (ismodal)
  {
    this.greyoutnode = document.createElement("div");
    this.greyoutnode.id = "ws_greyout";
    this.greyoutnode.style.cssText = "position:fixed !important;top:0;left:0;width:100%;height:100%;background-color:#000000 !important;opacity: 0.4; filter:alpha(opacity=40);z-index:40;";

    var self = this;
    if (settings.canclose)
    {
      this.greyoutnode.onclick = function()
                                 {
                                   self.hide();
                                 };
      this.viewport.onclick = function(event)
              {
                if(!event)
                  event = window.event;

                var target = event.target ? event.target : event.srcElement;

                if (getDialogInstanceContainingNode(target)) // don't react to the dialog itself, only the viewport
                  return;

                self.hide();
              };
    }
  }

  if (!window.ws_dialogs_globalsettings.pagewidth)
  {
    this.viewport.style.right = "0";
    this.viewport.style.width = "auto";
  }
  else
  {
    this.viewport.style.right = "auto";
    this.viewport.style.width = window.ws_dialogs_globalsettings.pagewidth+"px";
  }

  this.lineheightstretcher = document.createElement("span");
  this.lineheightstretcher.className = "ws_stretch";
  this.lineheightstretcher.style.cssText = "display:inline-block;vertical-align:middle;width:0px;height:100%;";

  this.body = element;

  var usetemplate = window.ws_dialogs_globalsettings.template != "";
  if (usetemplate)
  {
    this.dialog = document.createElement("span");
    this.dialog.innerHTML = window.ws_dialogs_globalsettings.template;
  }
  else
  {
    this.dialog = element;
  }

  addClass(this.dialog, "ws_dialog");
  this.dialog.style.display = "inline-block";
  this.dialog.style.verticalAlign = "middle";
  this.dialog.style.textAlign = "left";
  this.dialog.style.position = "relative";

  this.viewport.appendChild(this.dialog);
  this.viewport.appendChild(this.lineheightstretcher);


  // FIXME: experimental default closebutton
  /*
  if (settings.canclose)
  {
    var closebtn = document.createElement("div");
    closebtn.className = "ws_closebutton";
    closebtn.innerHTML = "x";
    closebtn.onclick = function() { closeDialog(); };
    this.dialog.appendChild(closebtn);
  }
  */


  if (window.ws_dialogs_globalsettings.keepdomposition && elemparent)
  {
    // place the dialog at the position in the DOM of the original element,
    // so forms keep working.
    elemparent.insertBefore(this.viewport, elemsibling);

    // also insert the greyout in the same level in the DOM,
    // because IE will have problems with the z-index
    if (ismodal)
      elemparent.insertBefore(this.greyoutnode, elemsibling);
  }
  else
  {
    // if we don't want to keep the DOM position
    // or when the dialog was outside the DOM (there's no elemparent)
    // attach the dialog to the body
    document.body.appendChild(this.viewport);

    if (ismodal)
      document.body.appendChild(this.greyoutnode);
  }

  if (usetemplate)
  {
    // replace the placeholder node within the popup with the proper node
    var dialogbodynode = document.getElementById(window.ws_dialogs_globalsettings.templatebodyid);

    if (window.ws_dialogs_globalsettings.contentverticalalign)
      dialogbodynode.parentNode.replaceChild(frag, dialogbodynode);
    else
      dialogbodynode.parentNode.replaceChild(element, dialogbodynode);
  }

  element.style.display = "inline-block";

  if (settings.width)
    element.style.width = settings.width+"px";

  if (settings.height)
    element.style.height = settings.height+"px";

  //this.show();
}

wsdialog.prototype.hide = function()
{
  this.body.fireEvent('whpopup-beforehide');

  if (this.greyoutnode)
    this.greyoutnode.style.display = "none";

  if (!this.visible)
    return;

  if (this.viewport != null) // in case we used .close in the whpopup-beforehide callback
    this.viewport.style.display = "none";

  this.visible = false;
  this.body.fireEvent('whpopup-hide');
}

wsdialog.prototype.close = function()
{
  this.viewport.parentNode.removeChild(this.viewport);
  this.viewport = null;
  this.closed = true;
}

wsdialog.prototype.show = function()
{
  if (this.visible)
    return;

  this.viewport.style.display = "";
  this.viewport.style.visibility = "";

  if (this.greyoutnode)
    this.greyoutnode.style.display = "";

  this.visible = true;
  this.body.fireEvent('whpopup-show');
}

wsdialog.prototype.setbodysize = function(width, height)
{
  this.body.style.width = width+"px";
  this.body.style.height = height+"px";
}

/////////////////////////////////////////////////////////////////
//
//  Auto resize to iframe content
//

// hopefully one day whe'll see this appear: https://bugzilla.mozilla.org/show_bug.cgi?id=80713
wsdialog.prototype.activateAutoresizeToIframe = function()
{
  var self = this;
  this.resizetimer = setInterval(function() { self.resizeToIframe(); }, 500);
}

wsdialog.prototype.resizeToIframe = function()
{
  /*
  if (typeof window.count == "undefined")
    window.count = 0;
  else
    window.count++;
  */

  var framedoc = toddGetIFrameDocument(this.frame);
  if (!framedoc)
   return;

  // give the iframe content room so it flows nicely
  // (doesn't keep resizing to the iframe, since we change the iframe size)
  var htmlelem = framedoc.documentElement;
  if (!htmlelem) // IE: is there an actual page loaded in this div?
    return;

  var framebody = framedoc.body;
  if (!framebody) // FF: is there an actual page loaded in this div?
    return;

  var prevsize = window.prevsize;
  var ownerviewportwidth = document.documentElement.clientWidth;
  var ownerviewportheight = document.documentElement.clientHeight;
  var keepfromedge = { left: 35, right: 35, top: 35, bottom: 35 };
  var maxdialogwidth = ownerviewportwidth - keepfromedge.left - keepfromedge.right;
  var maxdialogheight = ownerviewportheight - keepfromedge.top - keepfromedge.bottom;

  htmlelem.style.width = (ownerviewportwidth-keepfromedge.left-keepfromedge.right)+"px";
  htmlelem.style.overflow = "hidden";
  framebody.style.overflow = "hidden";

  var mywidth, myheight;

  if (toddUserAgentIsIE && toddUserAgentIsIE7)
  {
    framebody.style.position = "static"; // !! downside to static is element within with width:100% will use the size of <html>
    framebody.style.display = "inline"; // don't grow to fit <HTML>, just use what is needed
  }
  else
  {
    framebody.style.display = "inline-block"; // don't grow to fit <HTML>, just use what is needed
  }

  // document.body.scrollWidth/scrollHeight would return the full available size instead of rect of used space
  var bounds_body = framedoc.body.getBoundingClientRect();
  mywidth = bounds_body.right - bounds_body.left;
  myheight = bounds_body.bottom - bounds_body.top;

  // create a scrolling viewport for the iframe in case the content of the iframe
  // is to big to stay within the viewport of the page
  this.body.style.maxWidth = maxdialogwidth+"px";
  this.body.style.maxHeight = maxdialogheight+"px";
  this.body.style.overflow = "auto";

  // keep the dialog from twitching
  // (due to animations or css hover effects making the document a few pixels bigger or smaller)
  var twitchtreshold = 5;

  if (this.prevsize && Math.abs(this.prevsize.width - mywidth) <= twitchtreshold && Math.abs(this.prevsize.height - myheight) <= twitchtreshold)
    return;

  mywidth  += twitchtreshold;
  myheight += twitchtreshold;

  this.prevsize = { width: mywidth, height: myheight };
  this.frame.style.width = mywidth+"px";
  this.frame.style.height = myheight+"px";

  // test to help IE 8 (prevent dialog not recentering upon initial resizing of the dialog)
  if (toddUserAgentIsIE8)
    this.dialog.style.zoom = "1";
}


/////////////////////////////////////////////////////////////////
//
//  Helper functions
//

function hasClass(ele,cls)
{
  return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}

function addClass(ele,cls)
{
  if (!this.hasClass(ele,cls)) ele.className += " "+cls;
}

function removeClass(ele,cls)
{
//if (hasClass(ele,cls)) {
  var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
  ele.className=ele.className.replace(reg,' ');
//}
}

function toddGetIFrameDocument(iframe)
{
  // if we try to read contentWindow before the iframe has been inserted into a document,
  // IE will throw an 'Unspecified error'
  if (typeof iframe.contentWindow == 'unknown')
    return null;

  // contentDocument is supported by NS6, Firefox, Opera, IE8
  if (iframe.contentDocument)
    return iframe.contentDocument;

  // FF3/SF3.2.1/OP9.64/CHR1 will properly return null (typeof=='object') if not initialized
  if (iframe.contentWindow == null)
    return null;

  //if (iframe.document) // For IE5
  //  return iframe.document;

  // if we have a contentwindow we can safely ask for it's document
  // (IE5.5 and IE6)
  return iframe.contentWindow.document;
}

function toddGetIEVersion()
{
  // See: http://support.microsoft.com/kb/167820
  var ua = window.navigator.userAgent
  var msie = ua.indexOf ( "MSIE " )

  if ( msie > 0 )      // If Internet Explorer, return version number
    return parseInt (ua.substring (msie+5, ua.indexOf (".", msie )))
  else                 // If another browser, return 0
    return 0
}

// for internal use
function getDialogByFrameRef(frame)
{
  for(var tel=0; tel<window.ws_dialogs.length; tel++)
  {
    if (window.ws_dialogs[tel].frame == frame)
      return window.ws_dialogs[tel];
  }
  return null;
}

function getDialogByPropertyValue(propertyname, value)
{
  for(var tel=0; tel<window.ws_dialogs.length; tel++)
  {
    if (window.ws_dialogs[tel][propertyname] == value)
      return window.ws_dialogs[tel];
  }
  return null;
}

function getDialogInstanceContainingNode(node)
{
  // lookup for the div with class ws_dialog, this is the dialoginstance.dialog
  while(node && node.nodeType == 1 && !hasClass(node, "ws_dialog"))
    node = node.parentNode;

  if (!node)
    return null;

  // lookup the dialog by the dialognode
  var dialoginstance = getDialogByPropertyValue("dialog", node);
  return dialoginstance;
}
