/*
 *  popover dialogs © 2008-2012, Horus Web Engineering Ltd
 *
 *  $Id: popover.js,v 1.49 2012-01-26 15:17:26 horus Exp $
 *
 *  licensed under the terms of the GNU Lesser General Public License:
 *    http://www.opensource.org/licenses/lgpl-license.php
 *
 *  needs horus.js, dom.js
 *  uses xmlhttp.js, status.js if go/nogo are used
 *
 */

horus.script.load('dom', 'call');


horus.popover=
  function ( popover, options ) {
    if (this instanceof horus.popover) {
      this.$popover=horus.getElement(popover);
      this.$id=this.$popover.id;
      this.$style=this.$popover.style;

      this.$baseopt=
        horus.options.get('popover', popover || this.$id, horus.popover.$options);

      if (this.$baseopt.level==null)
	this.$baseopt.level=horus.hasClass(this.$popover, 'alert') ? 1 : 0;

      horus.options.save(this, this.$baseopt, horus.popover.$options);
      this.$data=horus.hash(this.$baseopt.data);
      this.$enbiggened=false;
      this.$popover.$popover=this;
      return;
    }

    return horus.popover.get(popover).show(options);
  };


horus.popover.$options=
  { reference:     null,
    y:             20,
    x:             80,
    height:        null,
    width:         null,
    maxHeight:     '40%',
    maxWidth:      '75%',
    hideMenu:      false,
    level:         null,
    moveable:      false,
    focus:         true,
    escape:        true,
//  data:	   null, // set/merged explicitly
    title:         null,
    titleReplace:  null,
    contentMethod: null,
    contentAction: null,
    contentArgs:   null,
    method:        null,
    action:        null,
    callback:      null,
    params:        false,
    formparams:    false,
    ajaxoptions:   null };


horus.popover.prototype.popover   = function () { return this.$popover };
horus.popover.prototype.id        = function () { return this.$id };
horus.popover.prototype.form      = function () { return this.$form };
horus.popover.prototype.reference = function () { return this.$reference };
horus.popover.prototype.maximised = function () { return this.$enbiggened };
horus.popover.prototype.params    = function () { return this.$params };


horus.popover.prototype.options=
  function ( options ) {
    if (options!=null)
      switch (typeof options) {

      case 'string':  this.$reference = options; break;
      case 'number':  this.$y         = options; break;
      case 'boolean': this.$moveable  = options; break;

      case 'object':
        if (horus.isElement(options))
	  this.$reference=options;
	else if (options.control && typeof options.control=='function')
	  this.$reference=options.control();
	else {
	  horus.options.save(this, options, horus.popover.$options);
	  if ('data' in options) this.$data.merge(options.data);
	}
      }

    if (this.$reference)
      this.$reference=horus.tick && this.$reference instanceof horus.tick ?
	this.$reference.link() : horus.getElement(this.$reference);

  };


horus.popover.prototype.show=
  function ( options ) {
    this.options(options);

    if (this.$title!=null || this.$titleReplace) {
      if (!this.$titlebar) this.$titlebar=horus.getElement(this.$id+'title');

      if (this.$titlebar) {
	var title=this.$title;
	if (title==null) title=this.$title=horus.childText(this.$titlebar);

	if (this.$titleReplace)
	  for (var tag in this.$titleReplace)
	    title=title.replace(tag, this.$titleReplace[tag]);

	horus.childText(this.$titlebar, title);
      }
    }

    if (this.$hideMenu) themenu.hide();
    if (this.$popover.parentNode!=document.body) horus.insertChild(this.$popover);

    if (this.$moveable && !this.$mover) {
      this.$mover=horus.getTags.find(this.$popover, '.titlebar');
      this.$mover=this.$mover.isEmpty() ? this.$popover : this.$mover[0];
      this.$mover.onmousedown=horus.popover.mover;
    }

    if (this.$form==null) {
      this.$form=horus.firstTag(this.$popover, 'form');
      if (!this.$form) this.$form=false;
    }

    if (this.$content==null) {
      this.$content=horus.searchTag(this.$popover, false, '.content');
      if (!this.$content) this.$content=false;
    }

    if (this.$contentMethod) {
      if (this.$refresh==null) {
	if (!this.$contentAction) this.$contentAction=this.$action;
	this.$contentTarget=this.$content || this.$form || this.$popover;
	this.$refresh=true;
      }

      if (this.$refresh) {
	var response=horus.ajax
          (this.$contentAction, this.$contentMethod, this.$contentArgs);

	if (response) {
	  var content=response.selectSingleNode('horus/content');
	  if (content) horus.replaceImport(this.$contentTarget, content);
	  this.$refresh=response.getAttribute(content, 'refresh', false);
	}
      }
    }

    if (this.$height)
      if (this.$content)
	this.$content.style.height=horus.sizeValue(this.$height);
      else
	this.$style.height=horus.sizeValue(this.$height);

    if (this.$width) {
      this.$style.width=horus.sizeValue(this.$width);
      var adjust=this.$popover.offsetWidth-this.$width;
      if (adjust) this.$style.width=horus.sizeValue(this.$width-adjust);
    } else if (horus.brokenDOM)
      this.$style.float='left';

    if (this.$content)
      this.$content.style.maxHeight=horus.sizeValue(this.$maxHeight, 'height');

    var windowsize=horus.windowPos();
    var maxtop=windowsize.bottom-this.$popover.offsetHeight-4;
    var maxleft=windowsize.right-this.$popover.offsetWidth-4;
    if (maxleft<0) maxleft=0; // TODO: proper fix for browsers getting first shot width wrong

    var position=this.$reference ?
      horus.getPosition(this.$reference) :
      { top: horus.scrollv(), left: horus.scrollh() };

    position.top+=this.$y;
    position.left+=this.$x;
    if (position.top>maxtop) position.top=maxtop;
    if (position.left>maxleft) position.left=maxleft;
    this.$style.top=position.top+'px';
    this.$style.left=position.left+'px';
    horus.addClass(this.$popover, 'visible');
    this.$style.maxWidth=horus.sizeValue(this.$maxWidth);
    this.$popover.onkeyup=horus.popover.key;
    this.$popover.onmouseover=horus.popover.raise;

    if (horus.brokenDOM && this.$form) {
      if (horus.ieold) this.$form.style.width=this.$form.offsetWidth;
      this.$form.style.height='100%';
    }

    if (this.$focus) {
      if (typeof this.$focus=='boolean') {
	this.$focus=
	  horus.searchTag(this.$popover, false, horus.getTags.input, 'button.action');

	if (!this.$focus)
	  this.$focus=horus.searchTag(this.$popover, false, horus.getTags.input);

      } else if (typeof this.$focus=='string')
	this.$focus=document.getElementById(this.$focus);

      if (this.$focus) horus.focus(this.$focus);
    }

    this.$width=this.$popover.offsetWidth;
    horus.popover.$pending[this.$level]=this;
    horus.popover.restack();
    return this;
  };


horus.popover.prototype.hide=
  function () {
    horus.removeClass(this.$popover, 'visible');
    if (this.$reference) horus.focus(this.$reference);
    return this.$params;
  };


horus.popover.prototype.data=
  function ( tag ) {
    var result=this.$data[tag];
    if (arguments.length>1) this.$data[tag]=arguments[1];
    return result;
  };


horus.popover.prototype.title=
  function () {
    if (arguments.length) horus.replaceContentWith(this.$titlebar, arguments)
    return this.$titlebar;
  };


horus.popover.prototype.content=
  function () {
    if (this.$content==null) {
      this.$content=horus.searchTag(this.$popover, false, '.content');
      if (!this.$content) this.$content=false;
    }

    return this.$content;
  };


horus.popover.prototype.raise=
  function () {
    if (horus.popover.$pending[this.$level]!=this) {
      horus.popover.$pending[this.$level]=this;
      setTimeout(horus.popover.restack, 400);
    }
  };


horus.popover.prototype.maximise=
  function () {
    if (this.$enbiggened) {
      this.$style.position='absolute';
      this.$style.top=this.$top;
      this.$style.left=this.$left;
      this.$style.width=this.$width;
      this.$style.height=this.$height;
      this.$enbiggened=false;
    } else {
      this.$top=this.$style.top;
      this.$left=this.$style.left;
      this.$width=this.$style.width;
      this.$height=this.$style.height;
      var win=horus.windowPos();
      this.$style.position='fixed';
      this.$style.top=(win.top+10)+'px';
      this.$style.left=(win.left+10)+'px';
      this.$style.width=(win.width-40)+'px';
      this.$enbiggened=true;
    }

    if (this.$focus) horus.focus(this.$focus);
    return this.$enbiggened;
  };


horus.popover.prototype.go=
  function () {
    var action=this.$action;
    var method=this.$method;
    var params=this.$params;

    var offset, response;

    if (this.$form && this.$formparams) {
      if (params)
	params=horus.hash.copy(params);
      else
	params={};

      for (var i=0; i<this.$form.elements.length; i++) {
	var field=this.$form.elements[i];
	if (field.name) params[field.name]=horus.getvar(field);
      }
    }

    callback=this.$callback;

    if (callback) {
      if (callback instanceof Array)
	callback=callback.clone();
      else
	callback=[ callback ];

      offset=callback.length;
      callback.push('go', this.$popover, params);
    }

    if (callback) {
      response=horus.call.as(this, callback, null, null);
      if (response!=null && !response) return false;
    }

    if (method) {
      var async;

      if (typeof method=='function' || action && typeof action=='object') {
	if (!action) action=this;
	if (typeof method!='function') method=action[method];
	response=method.call(action, params, this);
      } else {
	if (callback) {
	  async=true;
	  callback[offset]='callback';
	  callback.pop(); // horus.ajax will put the params back
	  var options=this.$ajaxoptions;

	  if (options && typeof options=='object')
	    options.as=this;
	  else
	    options={ debug: options, as: this };

	  horus.ajax(action, method, params, callback, options);
	} else
	  response=horus.ajax(action, method, params, null, this.$ajaxoptions);

      }

      if (!async)
	if (callback)
	  horus.call.as(this, callback, response, null);
	else if (response || response==null)
	  this.hide();

    } else
      this.hide();

    return false;
  };


horus.popover.prototype.nogo=
  function ( popover ) {
    var callback=this.$callback;

    if (callback) 
      horus.call.as
	(this, callback, null,
	 [ 'nogo', this.$popover, this.$params ]);

    else {
      this.hide();
      horus.status.clear();
    }
  };


horus.popover.skip      = function ()       { return false };

horus.popover.hide      = function ( p )    { return horus.popover.get(p).hide() };
horus.popover.options   = function ( p, o ) { horus.popover.get(p).options(o) };
horus.popover.content   = function ( p )    { return horus.popover.get(p).content() };
horus.popover.maximise  = function ( p )    { return horus.popover.get(p).maximise() };
horus.popover.maximised = function ( p )    { return horus.popover.get(p).maximised() };
horus.popover.raise     = function ( p )    { p=horus.popover.get(p); if (p) p.raise() };
horus.popover.go        = function ( p )    { return horus.popover.get(p).go() };
horus.popover.nogo      = function ( p )    { return horus.popover.get(p).nogo() };


horus.popover.get=
  function ( popover ) {
    if (!(popover && popover instanceof horus.popover)) {
      if (!horus.isElement(popover))
	if (horus.isString(popover))
	  popover=horus.getElement(popover);
	else
	  popover=horus.event(popover).target;

      if (popover && !horus.hasClass(popover, 'popover'))
	popover=horus.parentTag(popover, 'div.popover');

      if (popover) popover=popover.$popover || new horus.popover(popover);
    }

    return popover;
  };


horus.popover.create=
  function ( id, createoptions, displayoptions ) {
    var popover;

    if (id)
      popover=horus.popover.get(id);
    else
      id='popoverid'+document.forms.length;

    if (!popover) {
      if (!createoptions) createoptions={};
      var formargv=createoptions.formname || id+'form';
      var uploader=createoptions.upload;
      var title=createoptions.title || 'Please Confirm';
      var content=createoptions.content || '';

      var yesclass=
	'yesclass' in createoptions ? createoptions.yesclass : uploader ? 'save' : 'go';

      var yeslabel=createoptions.yeslabel || (uploader ? 'upload' : 'yes');

      var noclass=
	'noclass' in createoptions ? createoptions.noclass : uploader ? 'reset' : 'nogo';

      var nolabel=createoptions.nolabel || (uploader ? 'cancel' : 'no');
      var contentclass=[ 'content' ];

      if (createoptions.contentclass)
	if (createoptions.contentclass instanceof Array)
	  contentclass.addList(createoptions.contentclass);
	else if (createoptions.contentclass.left(1)=='.')
	  contentclass.push(createoptions.contentclass.right(-1));
	else
	  contentclass.push(createoptions.contentclass);

      var popoverclass=[ 'popover' ];
      if (createoptions.alert) popoverclass.push('alert');
      if (createoptions.popoverclass) popoverclass.push(createoptions.popoverclass);

      var node=horus.insertChild
	(document.body,
	 [ 'div', { id: id, className: popoverclass, width: createoptions.width } ]);

      popover=new horus.popover(node);
      formargv={ name: formargv };

      if (uploader) {
	formargv.method='post';
	formargv.action=horus.isString(uploader) ? uploader : '/fckconnector.cfm';
	formargv.enctype='multipart/form-data';
      }

      popover.$form=horus.appendChild(node, [ 'form', formargv ]);
      popover.$titlebar=horus.appendChild(popover.$form, [ 'h2', '.titlebar', title ]);
      popover.$content=horus.appendChild(popover.$form, [ 'div', contentclass, '*' ]);

      if (content)
	if (content instanceof Array)
	  horus.replaceContentWith(popover.$content, content);
	else
	  horus.replaceContent(popover.$content, content);

      horus.appendChild
	(popover.$form,
         [ 'div', '.endbutton',
	   [ 'input',
	     { type: 'submit', classname: yesclass+' small button', value: yeslabel,
	       onclick: horus.popover.go } ], ' ',
	   [ 'input',
	     { type: 'button', classname: noclass+' small button', value: nolabel,
	       onclick: horus.popover.nogo } ] ]);

    }

    if (displayoptions!=null) popover.show(displayoptions);
    return popover;
  };


horus.popover.$pending=[];
horus.popover.$raised=[];
horus.popover.$restacking=0;


horus.popover.restack=
  function () {
    if (++horus.popover.$restacking==1)
      for (var level=0; level<horus.popover.$pending.length; level++) {
	var target=horus.popover.$pending[level];
	horus.popover.$pending[level]=null;

	if (target && horus.popover.$raised[level]!=target) {
	  if (horus.popover.$raised[level]) {
	    if (target.$over) target.$over.$under=target.$under;

	    if (target.$under) {
	      target.$under.$over=target.$over;

	      while (target.$under) {
		target.$under.$style.zIndex=Number(target.$under.$style.zIndex)-1;
		target.$under=target.$under.$under;
	      }
	    }

	    horus.popover.$raised[level].$under=target;
	    target.$over=horus.popover.$raised[level];
	    target.$style.zIndex=Number(horus.popover.$raised[level].$style.zIndex)+1;
	  } else
	    target.$style.zIndex=(level+2)*20000+1;

	  horus.popover.$raised[level]=target;
	}
      }

    horus.popover.$restacking--;

    for (level=0; level<horus.popover.$pending.length; level++)
      if (horus.popover.$pending[level]) {
	setTimeout(horus.popover.restack, 100);
	break;
      }

  };


horus.popover.key=
  function ( event ) {
    event=horus.event(event);
    var target=event.target;

    switch (event.keyCode) {

    case 27:
      var popover=horus.popover.get(target);
      var onescape=popover.$escape;

      if (onescape)
	switch (typeof onescape) {

	case 'boolean':  horus.popover.nogo(event); break;
	case 'function': onescape(event); break;
	case 'string':   document.getElementById(onescape).onclick(event); break;
	case 'object':   onescape.onclick(event); break;

	}

      break;

    case 37:
      if (horus.checkTag(target, 'input:type=button=submit,button')) {
	target=horus.previousTag(event.target, horus.getTags.input);
	if (target) horus.focus(target);
      }

      break;

    case 39:
      if (horus.checkTag(target, 'input:type=button=submit,button')) {
	target=horus.nextTag(event.target, horus.getTags.input);
	if (target) horus.focus(target);
      }

      break;

    }

    return true;
  };


horus.popover.mover=
  function ( event ) {
    event=horus.event(event);
    var popover=horus.popover.get(event);
    if (!popover.$moveable) return;
    popover.$mtop=popover.$popover.offsetTop;
    popover.$mleft=popover.$popover.offsetLeft;
//    if (!popover.$width) popover.$width=popover.$popover.offsetWidth;
    popover.$ey=event.y;
    popover.$ex=event.x;
    if (horus.popover.$moving) horus.popover.endmove();
    horus.popover.$moving=popover;
    horus.eventListener(document.body, 'mousemove', horus.popover.move);
    horus.eventListener(document.body, 'mouseup', horus.popover.endmove);

    if (horus.iewin) {
      horus.eventListener(document.body, 'selectstart', horus.popover.skip);
      horus.eventListener(document.body, 'dragstart', horus.popover.skip);
    }

    popover.$mover.style.cursor='move';
    return false;
  };


horus.popover.move=
  function ( event ) {
    var popover=horus.popover.$moving;
    if (!popover) return;
    event=horus.event(event);
    var dy=event.y-popover.$ey;
    var dx=event.x-popover.$ex;
    if (dy*dy<9 && dx*dx<9) return;
    popover.$style.top=(popover.$mtop+=dy)+'px';
    var left=popover.$mleft+dx;
    var width=popover.$popover.offsetWidth;

    if (left<0) {
      popover.$style.width=(width+left)+'px';
      popover.$mleft=popover.$style.left=0;
    } else if (left+popover.$width>horus.$width) {
      popover.$style.width=(horus.$width-left)+'px';
      popover.$style.left=(popover.$mleft=left)+'px';
    } else if (width>=popover.$width) {
      popover.$style.left=(popover.$mleft=left)+'px';
    } else if (popover.$mleft>0) {
      width=horus.$width-left;
      popover.$style.width=(width>popover.$width ? popover.$width : width)+'px';
    } else {
      width+=left;

      if (width>=popover.$width) {
	popover.$style.width=popover.$width+'px';
	popover.$style.left=(popover.$mleft=left)+'px';
      } else
	popover.$style.width=(width+left)+'px';

    }

    popover.$ey=event.y;
    popover.$ex=event.x;
    return false;
  };


horus.popover.endmove=
  function () {
    var popover=horus.popover.$moving;
    if (!popover) return;
    popover.$mover.style.cursor=null;

    if (horus.iewin) {
      horus.removeListener(document.body, 'dragstart', horus.popover.skip);
      horus.removeListener(document.body, 'selectstart', horus.popover.skip);
    }

    horus.removeListener(document.body, 'mouseup', horus.popover.endmove);
    horus.removeListener(document.body, 'mousemove', horus.popover.move);
    horus.popover.$moving=false;
  };


horus.popover.contentdata=
  function ( substitute, store, section ) {
    if (section=='html') horus.appendImport(null, store, substitute, true);
  };


horus.script.loaded('popover');

