//**************************************************
//
// FishEye.js
// (C) 2007
// Authors: Antonio Cisternino, Beatrice Rapisarda
// ver. 1.0
//
// Known problems:
//
// - Docking works only for left and 
//   bottom
//
//**************************************************

 RegisterScript('FishEye', 1, 'the Fish Eye menu');


// Resource location

var FishEyeEmptyIcon = '/resources/EmptyIcon.png';
var FishEyeLeftArrowIcon = '/resources/arrow_left.png';
var FishEyeRightArrowIcon = '/resources/arrow_right.png';
var FishEyeTopArrowIcon = '/resources/arrow_top.png';
var FishEyeBottomArrowIcon = '/resources/arrow_bottom.png';

// Print utility: you must have a DIV with id == 'debug'
function printDebug(s) {
  var d = document.getElementById('debugwnd');
  if (!d) {
      d = document.createElement('div');
      d.setAttribute('id', 'debugwnd');
      d.style.position = 'absolute';
      d.style.right = '0px';
      d.style.top = '0px';
      d.style.width = '50%';
      d.style.height = '60%';
      document.body.appendChild(d);      
  }
  d.innerHTML = s;
}

// Dump an object using printDebug
function dumpObj(o) {
  var s = "";
  
  for (v in o) {
    s += v + ': ' + o[v] + '<br/>';
  }
  printDebug(s);
}

// Possible locations in case of docking
var FishEyeDockStyle = { 'Top': 0, 'Left': 1, 'Right': 2, 'Bottom': 3 };

//**********************************************************************
// ImageTile object stores the information relative to each icon that
// shall be displayed
// Parameter d may contain additional data
function ImageTile(u, iu, t, d) {
  this.url = u;
  this.imgurl = iu;
  this.text = t;
  this.data = d;
  this.selected = false;
}

//**********************************************************************
// Global state (for animation purpose)

var ActiveFishEyeObject = null;

function ScrollAnimate(time, step) {
  if (ActiveFishEyeObject != null) {
    ActiveFishEyeObject.Scroll(step);
    setTimeout("ScrollAnimate("+time+", "+step+")", time);
  }
}

function IconHost(container, size) {
  this.Container = container;
  this.Size = size;
  
  this.Icon = function() {
    return this.Container.firstChild;
  }
  
  this.IsSelected = function() {
    return this.Container.childNodes.length > 1;
  }
  
  this.SetSelected = function(s) {
    if (s && !this.IsSelected()) {
      var img = document.createElement('img');
      img.setAttribute('src', '/resources/left_sel.png');
      img.setAttribute('alt', '');
      img.setAttribute('border', 0);
      img.style.position = 'relative';
      img.style.left = '-'+this.Size+'px';
      img.style.top = '0px';
      this.Container.appendChild(img);
      
      var fe = this.FishEye();
      fe[fe.FindId(this)].selected = s;
    } else if (!s && this.IsSelected()) {
      this.Container.removeChild(this.Container.childNodes[1]); 
      var fe = this.FishEye();
      fe[fe.FindId(this)].selected = s;
    }
  }
  
  this.SetHref = function(url) {
    this.Container.href = url;
  }
  
  this.SetText = function(txt) {
    this.Container.nextSibling.innerHTML = txt;
  }
  
  this.SetAction = function (fun) {
    this.Container.onclick = fun;    
  }
  
  this.Magnify = function(dir, m) {
    var sz = Math.floor(m * this.Size);
    this.Icon()[dir] = sz;
    if (this.IsSelected()) {
      this.Container.childNodes[1].style.left = '-'+sz+'px'; // FIXME!!!
      this.Container.childNodes[1].style.top =  '0px';
    }
  }
  
  this.Reset = function (sz) {
    this.Icon()[sz] = this.Size;
    if (this.IsSelected()) {
      this.Container.childNodes[1].style.left = '-'+this.Size+'px'; // FIXME!!!
      this.Container.childNodes[1].style.top = '0px';
    }
  }
  
  this.FishEye = function () {
    return this.Container.parentNode.FishEyeObject;
  }
  
  this.GetData = function() {
    var fe = this.FishEye();
    return fe[fe.FindId(this)];
  }
}

function FishEyeMouseOverIcon(obj) {
  obj.parentNode.nextSibling.style.display = 'inline';
}

function FishEyeMouseOutIcon(obj) {
  obj.parentNode.nextSibling.style.display = 'none';
}

function DefaultIconMarkup(sz, size) {
  var vertical = sz == "width";
  var label = "";
  var align = '';
  if (vertical) {
    label = '<span class="FishEyeLabel" style="display: none; position: relative; top: 0.5em"></span>';
    align = 'align="middle"';
  } else {
    label = '<span  class="FishEyeLabel" style="display: none; position: relative; left: -' +size+'px; top: -'+(size + 20)+'px;"></span>';
  }
  return '<a href="#" class="FishEyeLink"><img class="FishEyeImg" src="'+FishEyeEmptyIcon+'" '+sz+'="'+size+'" alt="" border="0" onmouseover="FishEyeMouseOverIcon(this)" onmouseout="FishEyeMouseOutIcon(this)"'+align+'/></a>'+label;
}

function FishEyeSelectIcon(obj) {
  var fisheye = obj.parentNode.FishEyeObject;
  return fisheye.SelectItem(obj);
}

function FishEyeUpdate(obj) {
  var fisheye = obj.parentNode.FishEyeObject;
  return fisheye.Update();
}

function FishEyeGetIconData(obj) {
  var fisheye = obj.parentNode.FishEyeObject;
  var ih = new IconHost(obj, fisheye.Size);
  return fisheye[fisheye.FindId(ih)];
}

function FishEyeSelectedItems(obj) {
  var fisheye = obj.parentNode.FishEyeObject, i;
  var ret = new Array();
  for (i = 0; i < fisheye.length; i++) {
    if (fisheye[i].selected)
      ret.push(fisheye[i]);
  }
  return ret;
}

function FishEyeNothing() {
}

//**********************************************************************
// FishEye component. It displays a number of icon out of a possibly
// large list of icons (urls images and links)
// Parameters:
//  - cont: The container to be used, if null a new div is created and
//          docked to bottom.
//  - count: Number of visible icons
//  - size: The size in pixel of icons (sets height or width of the image)
//  - magnify: The magnification factor (-1, i.e. use 1 to indicate 2x)
//  - fishfisheyerange: an integer number from 1 to n, it is the exponent used to
//                      mix magnify and distance (use 1-5)
//
// Example:
//  var d = document.getElementById("foo");
//  f = new FishEye(d, 6, 32, 3, 5);
//  f.SetLocation(FishEyeDockStyle.Left);
//  f.AddElement('##', 'resources/notepad.png', 'Foo');
//  f.AddElement('##', 'resources/video.png', 'Foo');
//
// Example:
//  var f = new FishEye(null, 6, 32, 3, 5);
//  f.SetDock(FishEyeDockStyle.Bottom);
//  f.AddElement('##', 'resources/notepad.png', 'Foo');
//  f.AddElement('##', 'resources/video.png', 'Foo');
//
function FishEye(cont, count, size, magnify, fisheyerange) {
  var container = cont == null ? document.createElement("div") : cont;
  
  if (cont == null)
    container.style.textAlign = 'center';
  
  container.FishEyeObject = this;

  // public: number of items stored in the control
  this.length = 0;
  // public: size in pixels of unsolicited icons
  this.Size = size;
  // public: Number of visible icons
  this.Count = count;
  // private: offset in the array of items to be displayed
  this.StartIndex = 0;
  
  // private: Start offset in the div (either height or width)
  this.startOffset = 0;
  // private: End offset in the div (either height or width)
  this.endOffset = 0;
  // public: Magnification amount
  this.Magnify = magnify;
  // public: Influence of perturbation factor.
  this.FishEyeRange = fisheyerange;

  this.IsVertical = function () {
    return (this.Location == FishEyeDockStyle.Left || this.Location == FishEyeDockStyle.Right);
  }

  // private: used to enumerate a nodes within the div
  this.EnumerateIcons = function (fun) {
    var children = container.childNodes;
    var i, icon = 0;
    for (i = 0; i < children.length; i++) {
      var n = children[i];
      if (n.className == 'FishEyeLink') {
        fun(new IconHost(n, this.Size), icon);
        icon++;
      }
    }
  }
  
  this.FindId = function (iconhost) {
    var idx = -1;
    var fun = function (h, icon) {
      if (h.Container == iconhost.Container)
        idx = obj.StartIndex + icon;
    }
    this.EnumerateIcons(fun);
    return idx;
  }
  
  this.SelectItem = function(link) {
    var h = new IconHost(link, this.Size);
    h.SetSelected(!h.IsSelected());
    return h.IsSelected();
  }

  // private: reset all images to the original height or width
  this.reset = function () {
    var sz = obj.IsVertical() ? "width" : "height";

    var f = function (node, idx) {
      node.Reset(sz);
    }
    obj.EnumerateIcons(f);
  }

  var obj = this;
  var sizeChanged = false;

  var updateRange = function () {
    var start, end, off;
    var vertical = obj.IsVertical(); 
    var f = function (node, idx) {
      if (vertical) {
        if (idx == 0)
          start = node.Icon().offsetTop;
        else
          end = node.Icon().offsetTop + node.Icon().offsetHeight;
      } else {
        if (idx == 0)
          start = node.Icon().offsetLeft;
        else
          end = node.Icon().offsetLeft + node.Icon().offsetWidth;
      }
    }
    off = vertical ? container.offsetTop : container.offsetLeft;
    obj.EnumerateIcons(f);
    obj.startOffset = start + off;
    obj.endOffset = end + off;
  }

  container.onmousemove = function (evt) {
    updateRange();
    var x, y;
    if (evt) {
      x = evt.clientX;
      y = evt.clientY;
    } else {
      x = event.clientX;
      y = event.clientY;
    }
    var vertical = obj.IsVertical();
    var w = obj.endOffset - obj.startOffset;
    if ((vertical ? y : x) > obj.startOffset && (vertical ? y : x) < obj.endOffset) {
      var sz = vertical ? "width" : "height";

      var f = function (node, idx) {
        var img = node.Icon();
        var distance = vertical ? Math.abs(img.offsetTop + container.offsetTop + (img.clientHeight / 2) - y) : Math.abs(img.offsetLeft + container.offsetLeft  + (img.clientWidth / 2) - x);
        distance = 1 - (distance / w);
        var i, r = 1;
        for (i = 1; i < obj.FishEyeRange; i++)
          r *= distance;
        node.Magnify(sz, 1 + obj.Magnify * r);
      }
      obj.EnumerateIcons(f);
      sizeChanged = true;
    } else if (sizeChanged) {
      obj.reset();
    }
  }    

  container.onmouseout = function (evt) {
    var x, y;
    if (evt) {
      x = evt.clientX;
      y = evt.clientY;
    } else {
      x = event.clientX;
      y = event.clientY;
    }
    
    if ((x > container.offsetLeft) && 
        (x < (container.offsetLeft + container.offsetWidth)) &&
        (y > container.offsetTop) &&
        (y < (container.offsetTop + container.offsetHeight)))
      return;

    obj.reset();    
  } 


  // Add 
  this.AddElement = function (url, imgurl, alttxt, data) {
    this[this.length++] = new ImageTile(url, imgurl, alttxt, data);
    this.Update();
  }
  
  this.UpdateArrows = function () {
    var children = container.childNodes;
    var i, icon = 0;
    for (i = 0; i < children.length; i++) {
      var n = children[i];
      if (n.className == 'FishEyeRightArrow') {
        n.style.visibility = (this.StartIndex + this.Count >= this.length || this.length == 0) ? 'hidden' : 'visible';
      }
      if (n.className == 'FishEyeLeftArrow') {
        n.style.visibility = (this.StartIndex == 0 || this.length == 0) ? 'hidden' : 'visible';
      }
    }
  }
  
  this.Update = function () {
    var sz = "height";
    var obj = this;
    if (this.IsVertical())
      sz = "width";
    var f = function (node, idx) {
      var img = node.Icon();
      img[sz] = obj.Size;
      var id = obj.StartIndex + idx;
      if (id < obj.length) {
        img.src = obj[id].imgurl;
        img.alt = obj[id].text;
        if (typeof(obj[id].url) == 'string') {
          node.SetHref(obj[id].url);
          node.SetText(obj[id].text);
          node.SetAction(null);
        } else {
          node.SetHref("javascript:FishEyeNothing()");
          node.SetText(obj[id].text);
          node.SetAction(obj[id].url);
        }
          
        node.SetSelected(obj[id].selected);
      } else {
        img.src = FishEyeEmptyIcon;
        img.alt = "";
        node.SetHref("#");
        node.SetAction(null);
        node.SetSelected(false);
      }
    }
    
    this.EnumerateIcons(f);    
    this.UpdateArrows();
  }

  this.UpdateLayout = function () {
    var sep = "", sz = "height";
    var vertical = this.IsVertical();
    if (vertical) {
      sep = "<br/>\n";
      sz = "width";
    }
    var txt = DefaultIconMarkup(sz, this.Size)+sep;
    var content = "", i;
    for (i = 0; i < this.Count; i++)
      content += txt;
    container.innerHTML = '<img src="'+(vertical ? FishEyeTopArrowIcon : FishEyeLeftArrowIcon) +'" class="FishEyeLeftArrow" onmouseup="this.parentNode.FishEyeObject.Animate(0, 0)" onmousedown="this.parentNode.FishEyeObject.Animate(1000, -1)" alt="" />' + sep + content + '<img src="'+(vertical ? FishEyeBottomArrowIcon : FishEyeRightArrowIcon)+'" class="FishEyeRightArrow"  onmouseup="this.parentNode.FishEyeObject.Animate(0, 0)" onmousedown="this.parentNode.FishEyeObject.Animate(1000, 1)" alt="" />';    
    this.Update();
  }
  
  this.Animate = function (steptime, amount) {
    if (steptime == 0) {
      ActiveFishEyeObject = null;
    } else if (ActiveFishEyeObject == null) {
      ActiveFishEyeObject = this;
      //setTimeout("ScrollAnimate("+steptime+", "+amount+")", steptime);
      ScrollAnimate(steptime, amount);
    }
  }
  
  this.Scroll = function (amount) {
    var idx = this.StartIndex + amount;
    if (idx >= 0 && idx < this.length - this.Count + 1) {
      this.StartIndex = idx;
      this.Update();
    }
  }
  
  this.SetLocation = function (dock) {
    this.Location = dock;
    this.UpdateLayout();
  }
  
  this.SetDock = function (dock) {
    this.Location = dock;
    switch (dock) {
    case FishEyeDockStyle.Top:
      container.style.position = 'absolute';
      container.style.left = '0px';
      container.style.right = '0px';
      container.style.top = '0px';
      //container.style.height = this.Size + 'px';
      break;
    case FishEyeDockStyle.Left:
      container.style.position = 'absolute';
      container.style.left = '0px';
      container.style.top = '0px';
      container.style.bottom = '0px';
      container.style.width = this.Size + 'px';
      break;
    case FishEyeDockStyle.Right:
      container.style.position = 'absolute';
      container.style.right = '0px';
      container.style.top = '0px';
      container.style.bottom = '0px';
      container.style.width = this.Size + 'px';
      break;
    case FishEyeDockStyle.Bottom:
      container.style.position = 'absolute';
      container.style.left = '0px';
      container.style.right = '0px';
      container.style.bottom = '0px';
      //container.style.height = this.Size + 'px';
      break;
    } 
    this.UpdateLayout();
  }

  this.SetSize = function (sz) {
    this.Size = sz;
    this.Update();
  }
  
  this.Clear = function () {
    var i = 0;
    for (i = 0; i < this.length; i++)
      this[i] = null;
    this.length = 0;
  }
  
  if (cont == null) {
    this.SetDock(FishEyeDockStyle.Bottom);  
  
    document.body.appendChild(container);
  } else {
    this.SetLocation(FishEyeDockStyle.Bottom);  
  }
}

    function IconChange(icon) {      
	  var s = FishEyeSelectedItems(icon);
	  if (s.length) {
	    var filter = new Array(), i;
	    for (i = 0; i < s.length; i++)
	      filter.push(s[i].data);
	    for (n in map_animations) {
	      map_animations[n].setTypeFilter(map_maps[n], filter);
	      map_animations[n].centerShapes(map_maps[n]);
	      break;
	    }
	  } else {
	    for (n in map_animations) {
	      map_animations[n].clearTypeFilter(map_maps[n]);
	      map_animations[n].centerShapes(map_maps[n]);
	      break;
	    }
	  }
	  UpdateSelectedIconsBar(icon);
	  FishEyeUpdate(icon);
    }
    
    function IconDeselect() {
      v = FishEyeGetIconData(this);
      v.data.data.selected = false;
      IconChange(v.data.icon);
    }

	function UpdateSelectedIconsBar(icon) {
      var s = FishEyeSelectedItems(icon)
	  var d = document.getElementById('SelectedIcons');
	  if (s.length) {
	    var f = new FishEye(d, 16, 18, 0, 5);
  	    f.SetLocation(FishEyeDockStyle.Bottom);
	    for (i = 0; i < s.length; i++) {
	      var n = s[i].data;
	      if (n.indexOf('spiaggia') != -1) n = 'spiaggiasolomare';
	      f.AddElement(IconDeselect, '/images/small_icon/'  + n + '.png', '', { 'icon' : icon, 'data' : s[i] });
	    }
	  } else {
	    d.innerHTML = '';
	  }
	}

	function IconSelected() {
	  FishEyeSelectIcon(this);
	  IconChange(this);
	}
	
	function SetColorLabel(s) {
	  var d = document.getElementById('ColorLabel');
	  if (s == null) {
	    d.style.display = 'none';
	    d.innerHTML = "";
	  } else {
	    d.style.display = 'block';
	    d.innerHTML = s;
	  }
	}

