/*
Tooltips (c)2006 Matthias.Platzer@knallgrau.at, MIT-sytle LICENSE
depends on script.acolo.us (http://script.aculo.us).
v0.3 23.01.2006
*/

window.tooltips = [];
TooltipFactory = Class.create();
TooltipFactory.prototype = {
	initialize: function(selector, options) {
		this.selector = selector;
		this.elements = [];
		this.tooltips = [];
		this.options = options||{};
	},

	areSupported: function() {
		return (document.getElementById!=null);
	},

	activate: function(elements, options) {
		this.elements = document.getElementsBySelector(this.selector);
		Object.extend(this.options, options||{});
		for (var i=0; i<this.elements.length; i++){
			var tooltip = new Tooltip(this.elements[i], this);
			if (tooltip) {
				this.tooltips.push(tooltip);
			}
		}
	},

	activateOnLoad: function(options) {
		if (this.areSupported()) {
			Object.extend(this.options, options||{});
			Event.observe(window, "load", this.activate.bind(this));
		}
	}
}

Tooltip = Class.create();
Tooltip.prototype = {
	initialize: function(trigger, options) {
		this.trigger = trigger;
		if (options instanceof TooltipFactory) {
			this.factory = options;
			options = options.options;
		}
		this.setOptions(options);
		if (this.options.getPopUp) {
			this.popUp = this.options.getPopUp.bind(this)(event);
		} else if (!this.popUp && this.options.popUp) {
			this.popUp = $(this.options.popUp);
		} else if (this.trigger.href && this.trigger.href.indexOf("#") != -1) {
			this.popUp = $(this.trigger.href.split("#").pop());
		}
		if (!this.popUp && this.trigger.id) this.popUp = $(this.trigger.id + "PopUp");
		if (!this.popUp && this.trigger.title) {
			this.popUp = document.createElement("div");
			document.body.appendChild(this.popUp);
			this.popUp.className = this.options.popUpClassName;
			this.popUp.innerHTML = this.trigger.title;
			this.trigger.removeAttribute("title");
		}
		if (!this.popUp) return;

		window.tooltips.push(this);
		this.runningEffect = null;
		if (this.options.openingEvent==this.options.closingEvent) {
			Event.observe(this.trigger, this.options.openingEvent||'click', this.toggle.bind(this));
		} else {
			Event.observe(this.trigger, this.options.openingEvent||'mouseover', this.open.bind(this));
			Event.observe(this.trigger, this.options.closingEvent||'mouseout', this.close.bind(this));
		}
		if (this.options.onInitialize) this.options.onInitialize.bind(this)();
	},

	setOptions: function(options) {
		this.options = {
			queue: { 
				position: "end", 
				scope: "tooltip"+window.tooltips.length, 
				limit: 1
			}
		};
		Object.extend(this.options, Tooltips.options);
		Object.extend(this.options, options || {});
		if (this.setInitialPosition) this.setInitialPosition = this.options.setInitialPosition.bind(this);
		if (this.getPosition) this.getPosition = this.options.getPosition.bind(this);
		// opera can't handle opacity effects
		if (window.opera && this.options.effect.toLowerCase() == "appear") {
			this.options.duration = 0;
		}
	},

	open: function(event) {
		var duration = this.options.duration;
		if (this.runningEffect) {
			var queue = Effect.Queues.get(this.options.queue.scope);
			duration = Math.min(new Date().getTime() - this.runningEffect.startOn, duration);
			queue.remove(this.runningEffect);
		}
		if (this.options.setPosition) this.options.setPosition.bind(this)(event);
		this.runningEffect = Effect[Effect.PAIRS[this.options.effect][0]](this.popUp, Object.extend(Object.extend(
			Object.extend({}, this.options), {duration: duration}), this.options.open||{}
		));
		if (this.options.onOpen) this.options.onOpen.bind(this)(event);
	},

	close: function(event) {		
		var duration = this.options.duration;
		if (this.runningEffect) {
			var queue = Effect.Queues.get(this.options.queue.scope);
			duration = Math.min(new Date().getTime() - this.runningEffect.startOn, duration);
			queue.remove(this.runningEffect);
		}
		this.runningEffect = Effect[Effect.PAIRS[this.options.effect][1]](this.popUp, Object.extend(Object.extend(
			Object.extend({}, this.options), {duration: duration}), this.options.close||{}
		));
		if (this.options.onClose) this.options.onClose.bind(this)(event);
	},

	toggle: function(event) {
		Element.visible(this.popUp) ? this.close(event) : this.open(event);
	}
};

////// IMPLEMENTATIONS of TooltipFactory ///////
/// extend this as you need it

// element hover tooltips
Tooltips = new TooltipFactory(".tooltipTrigger", {
	popUpClassName: "tooltip",
	offsetLeft: 20,
	offsetTop: -30,
	effect: "appear",
	duration: 0.4,
	openingEvent: "mouseover",
	closingEvent: "mouseout",
	onInitialize: function() {
		this.popUp.style.position = "absolute";
		this.popUp.style.left = "0";
		this.popUp.style.top = "0";
		Element.hide(this.popUp);
	},
	onOpen: function(event) {
		var x = Event.pointerX(event) + this.options.offsetLeft;
		var triggerY = Position.cumulativeOffset(this.trigger)[1];
		var y = triggerY + this.trigger.offsetHeight + this.options.offsetTop;
		Position.prepare();
		var popUp = Element.getDimensions(this.popUp);
		if (x + popUp.width + this.options.offsetLeft > (Position.deltaX+Position.visibleWidth)) 
			x = Math.max( this.options.offsetLeft, ((Position.deltaX+Position.visibleWidth) - popUp.width - this.options.offsetLeft));
		if (y + popUp.height + this.options.offsetTop > (Position.deltaY+Position.visibleHeight)) {
			y = triggerY - popUp.height - this.options.offsetTop;
		}
		this.popUp.style.left = x+"px";
		this.popUp.style.top = y+"px";
	}
});


// button/link info: appears after klicking something
ActionHints = new TooltipFactory(".actionHintTrigger", {
	popUpClassName: "tooltip",
	offsetLeft: 20,
	offsetTop: 20,
	effect: "appear",
	openingEvent: "click",
	closingEvent: "mouseout",
	open: {
		duration: 0.0
	},
	close: {
		delay: 1.0,
		duration: 1.0
	},
	onInitialize: function() {
		this.popUp.style.position = "absolute";
		this.popUp.style.left = "0";
		this.popUp.style.top = "0";
		Element.hide(this.popUp);
	},
	onOpen: function(event) {
		var triggerPos = Position.cumulativeOffset(this.trigger);
		var x = triggerPos[0] + this.options.offsetLeft;
		var y = triggerPos[1] - this.options.offsetTop - this.trigger.offsetHeight;
		Position.prepare();
		var popUp = Element.getDimensions(this.popUp);
		if (x + popUp.width + this.options.offsetLeft > (Position.deltaX+Position.visibleWidth)) 
			x = Math.max( this.options.offsetLeft, ((Position.deltaX+Position.visibleWidth) - popUp.width - this.options.offsetLeft));
		this.popUp.style.left = x+"px";
		this.popUp.style.top = y+"px";
	}
});

// trigger for showing/hidding a section in your page
SectionTriggers = new TooltipFactory(".sectionTrigger", {
	effect: "appear",
	openingEvent: "click",
	closingEvent: "click",
	text: {
		show: "show",
		hide: "hide"
	},
	duration: 1.0,
	onInitialize: function() {
		// Element.hide(this.popUp);
	},
	onOpen: function(event) {
		this.trigger.innerHTML = this.trigger.getAttribute("hide")||this.options.text.hide;
		Event.stop(event);
	},
	onClose: function(event) {
		this.trigger.innerHTML = this.trigger.getAttribute("show")||this.options.text.show;
		Event.stop(event);
	}
});

/*
   Stripped from Behaviour v1.1 by Ben Nolan, June 2005.
   The following code is Copyright (C) Simon Willison 2004.
*/

function getAllChildren(e) {
  // Returns all children of element. Workaround required for IE5/Windows. Ugh.
  return e.all ? e.all : e.getElementsByTagName('*');
}

$S = document.getElementsBySelector = function(selector, parentElement) {
  // Attempt to fail gracefully in lesser browsers
  if (!document.getElementsByTagName) {
    return new Array();
  }
  // Split selector in to tokens
  var tokens = selector.split(' ');
  var currentContext = (parentElement) ? new Array($(parentElement)) : new Array(document);
  for (var i = 0; i < tokens.length; i++) {
    token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
    if (token.indexOf('#') > -1) {
      // Token is an ID selector
      var bits = token.split('#');
      var tagName = bits[0];
      var id = bits[1];
      var element = document.getElementById(id);
      if (tagName && element.nodeName.toLowerCase() != tagName) {
        // tag with that ID not found, return false
        return new Array();
      }
      // Set currentContext to contain just this element
      currentContext = new Array(element);
      continue; // Skip to next token
    }
    if (token.indexOf('.') > -1) {
      // Token contains a class selector
      var bits = token.split('.');
      var tagName = bits[0];
      var className = bits[1];
      if (!tagName) {
        tagName = '*';
      }
      // Get elements matching tag, filter them for class selector
      var found = new Array;
      var foundCount = 0;
      for (var h = 0; h < currentContext.length; h++) {
        var elements;
        if (tagName == '*') {
            elements = getAllChildren(currentContext[h]);
        } else {
            elements = currentContext[h].getElementsByTagName(tagName);
        }
        for (var j = 0; j < elements.length; j++) {
          found[foundCount++] = elements[j];
        }
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      for (var k = 0; k < found.length; k++) {
        if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
          currentContext[currentContextIndex++] = found[k];
        }
      }
      continue; // Skip to next token
    }
    // Code to deal with attribute selectors
    if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
      var tagName = RegExp.$1;
      var attrName = RegExp.$2;
      var attrOperator = RegExp.$3;
      var attrValue = RegExp.$4;
      if (!tagName) {
        tagName = '*';
      }
      // Grab all of the tagName elements within current context
      var found = new Array;
      var foundCount = 0;
      for (var h = 0; h < currentContext.length; h++) {
        var elements;
        if (tagName == '*') {
            elements = getAllChildren(currentContext[h]);
        } else {
            elements = currentContext[h].getElementsByTagName(tagName);
        }
        for (var j = 0; j < elements.length; j++) {
          found[foundCount++] = elements[j];
        }
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      var checkFunction; // This function will be used to filter the elements
      switch (attrOperator) {
        case '=': // Equality
          checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
          break;
        case '~': // Match one of space seperated words 
          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
          break;
        case '|': // Match start with value followed by optional hyphen
          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
          break;
        case '^': // Match starts with value
          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
          break;
        case '$': // Match ends with value - fails with "Warning" in Opera 7
          checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
          break;
        case '*': // Match ends with value
          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
          break;
        default :
          // Just test for existence of attribute
          checkFunction = function(e) { return e.getAttribute(attrName); };
      }
      currentContext = new Array;
      var currentContextIndex = 0;
      for (var k = 0; k < found.length; k++) {
        if (checkFunction(found[k])) {
          currentContext[currentContextIndex++] = found[k];
        }
      }
      // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
      continue; // Skip to next token
    }
    
    if (!currentContext[0]){
    	return;
    }
    
    // If we get here, token is JUST an element (not a class or ID selector)
    tagName = token;
    var found = new Array;
    var foundCount = 0;
    for (var h = 0; h < currentContext.length; h++) {
      var elements = currentContext[h].getElementsByTagName(tagName);
      for (var j = 0; j < elements.length; j++) {
        found[foundCount++] = elements[j];
      }
    }
    currentContext = found;
  }
  return currentContext;
}

