// miriorama.js - interactive reordering of a series of image tiles
// Copyright (C) 2007 Cesar Rincon
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// version 2.1, as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// To contact the author, please send email to <crincon@et.com.mx>, or
// conventional mail to: Enlaces Tecnologicos, att. Cesar Rincon,
// Av. Patria 1347 oficina 14, col. Mirador del Sol, 45054 Zapopan,
// Jalisco, Mexico.
//
// To obtain a copy of the GNU Lesser General Public License, visit
// the home page of this software at
// http://mengambrea.org/miriorama/ or write to the author, or
// write to the Free Software Foundation, Inc., 51 Franklin Street,
// Fifth Floor, Boston, MA 02110-1301 USA


// The following function defines an object document.mgb_miriorama,
// which contains our stuff.  This is in fact a global variable,
// precluding the use of two mirioramas in the same document, so it's
// ugly, but it makes our life significantly easier.  When this stupid
// JavaScript language is ditched, and we can script in Ruby with real
// closures for e.g.  handling events, call me and I'll fix this.

function mgb_miriorama_init(target_element_id) {

  // Check for W3C DOM, stop if not found.
  var W3CDOM = document.createElement && document.getElementsByTagName;
  if(!W3CDOM) return;

  // Get the target element, stop the script if not found.
  var element = document.getElementById(target_element_id);
  if(!element) return;

  // Find the images in the element.  Add each 'img' to our images array.
  var images = new Array();

  for(var i = 0; i < element.childNodes.length; i++) {
    var node = element.childNodes.item(i);
    if(node.nodeType == 1 &&
       node.nodeName.toLowerCase() == "img") {
      images[images.length] = node;
    }
  }

  // If no images were found, stop.
  if(images.length == 0) return;

  // Now do setup stuff.
  var m = new Object();

  m.images = images;
  m.element = element;
  m.highlighted = null;
  m.draggee = null;

  for(var i = 0; i < images.length; i++) {
    var img = images[i];
    mgb_setup_node(img, i, 0);
  }

  document.mgb_miriorama = m;
}

function mgb_reset_highlight() {
  var m = document.mgb_miriorama;

  if(m.highlighted) {
    m.highlighted.style.left = 0;
    m.highlighted.style.top = 0;
    m.highlighted.style.zIndex = 0;

    m.element.onmousemove = null;
    m.element.onmouseover = mgb_miriorama_mouseover;
    m.highlighted = null;
    m.draggee = null;
  }
}

function mgb_miriorama_mouseover(e) {
  if(!e) var e = window.event;
  var target = e.target ? e.target : e.srcElement;
  var m = document.mgb_miriorama;

  if(!m.draggee) {
    mgb_reset_highlight();
    target.onmouseover = null;
    target.style.top = 4;
    target.style.zIndex = 1;
    m.highlighted = target;
  }

  return false;
}

function mgb_miriorama_mouseout(e) {
  var m = document.mgb_miriorama;

  if(m.highlighted && !m.draggee) {
    mgb_reset_highlight();
  }

  return false;
}

function mgb_miriorama_mousedown(e) {
  if(!e) var e = window.event;
  var m = document.mgb_miriorama;

  if(m.highlighted && !m.draggee) {
    m.draggee = m.highlighted;
    m.element.onmousemove = mgb_miriorama_mousemove;
    m.drag_start_x = e.screenX;
  }

  return false;
}

function mgb_miriorama_mouseup(e) {
  if(!e) var e = window.event;
  var m = document.mgb_miriorama;

  if(m.draggee) {
    mgb_reset_highlight();
    mgb_miriorama_mouseover(e);
  }
}

function mgb_miriorama_mousemove(e) {
  if(!e) var e = window.event;
  var m = document.mgb_miriorama;

  if(m.draggee) {
    var offset = e.screenX - m.drag_start_x;
    var swap = null;

    if(Math.abs(offset) > 0.75 * m.draggee.width) {
      // Ok, we swap this image with the next or previous one
      if(offset > 0) {
        if(m.draggee.mgb_index + 1 < m.images.length) {
          // swap with the next image
          swap = m.images[m.draggee.mgb_index + 1];
          offset = -0.25 * m.draggee.width;
        }
      }
      else {
        if(m.draggee.mgb_index > 0) {
          // swap with the previous image
          swap = m.images[m.draggee.mgb_index - 1];
          offset = 0.25 * m.draggee.width;
        }
      }
    }

    if(swap) {
      // compute new offset.
      m.drag_start_x = e.screenX - offset;

      var draggee_clone = m.draggee.cloneNode(true);
      var swap_clone = swap.cloneNode(true);

      mgb_setup_node(draggee_clone, swap.mgb_index, offset);
      mgb_setup_node(swap_clone, m.draggee.mgb_index, 0);

      m.images[draggee_clone.mgb_index] = draggee_clone;
      m.images[swap_clone.mgb_index] = swap_clone;

      m.element.replaceChild(draggee_clone, swap);
      m.element.replaceChild(swap_clone, m.draggee);

      m.draggee = draggee_clone;
      m.highlighted = draggee_clone;
    }
    else {
      m.draggee.style.left = offset;
    }
  }

  return false;
}

function mgb_setup_node(node, index, offset) {
  node.mgb_index = index;
  node.style.left = offset;
  if(offset == 0) {
    node.style.top = 0;
    node.zIndex = 0;
  }
  else {
    node.style.top = 4;
    node.zIndex = 1;
  }
  node.style.position = "relative";
  node.onmouseover = mgb_miriorama_mouseover;
  node.onmouseout = mgb_miriorama_mouseout;
  node.onmousedown = mgb_miriorama_mousedown;
  node.onmouseup = mgb_miriorama_mouseup;
}

function mgb_miriorama_shuffle() {
  var m = document.mgb_miriorama;
  if(!m) {
    alert("error: miriorama no usable");
    return;
  }

  // Randomize stuff.  We swap each element in the images array with
  //some other random element.

  for(var i = 0; i < m.images.length; i++) {
    var swapindex = Math.floor(Math.random() * m.images.length);
    if(swapindex != i) {
      var a = m.images[i];
      var b = m.images[swapindex];
      a.mgb_index = swapindex;
      b.mgb_index = i;
      m.images[i] = b;
      m.images[swapindex] = a;
    }
  }

  // Now, we reflect the new order in the document.
  var current = 0;
  for(var i = 0; i < m.element.childNodes.length; i++) {
    var node = m.element.childNodes.item(i);
    if(node.nodeType == 1 &&
       node.nodeName.toLowerCase() == "img") {
      var clone = m.images[current].cloneNode(true);
      mgb_setup_node(clone, current, 0);
      m.element.replaceChild(clone, node);
      m.images[current] = clone;
      current++;
    }
  }

  // Finally, reset any operation in course.
  m.highlighted = null;
  m.draggee = null;
}
