/**********************************************************
    CSS Iterator

	Copyright 2007
	Adam Laughlin

	CSS Iterator is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    CSS Iterator 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 General Public License for more details at:
    http://www.gnu.org/licenses/
 *********************************************************/

/*Notes:
I created this script to make a flexible calendar with behavior that
modifiable by a graphic designer with limited CSS skills.

It operates on the logic that any animation must have a start and an
end point, so if we can create a script to carry dom elements through
a series of steps, we can leave all the presentation in the CSS. It has
been easier to debug since there is no javascript debugging involved,
and much easier for newbies to learn.

Also, Letting the CSS engine do all the work is much faster than using
javascript, and a single action can trigger things all over the page
by only changing a single class name or ID.

If you really want to see something cool, create a table and try
removing random rows, cells, columns, etc.  Don't change the CSS
or the JavaScript.

I am aware the script has issues.  It could be shorter and more elegant.
I'd love to have the time to change the iteration box recognition
from id to class, but I don't have the time right now.  Adding an
option for one container to set the stats on another container on the
fly is another, as well as the ability to stop the iteration mid-way
(for things like slideshows.)  I probably won't have time to work on it
in the near future, so feel free run with it if you'd like.
*/

// Adapted from Dean Edwards' addEvent - http://dean.edwards.name/ for more info. - Creative Commons License
function cssiAddEvent(element, type, handler) {if (!handler.$$guid) handler.$$guid = cssiAddEvent.guid++;if (!element.events) element.events = {};var handlers = element.events[type];if (!handlers) {handlers = element.events[type] = {};if (element["on" + type]) {handlers[0] = element["on" + type];}}handlers[handler.$$guid] = handler;element["on" + type] = cssiHandleEvent;};cssiAddEvent.guid = 1;
function cssiRemoveEvent(element, type, handler) {if (element.events && element.events[type]) {delete element.events[type][handler.$$guid];}};
function cssiHandleEvent(event) {var returnValue = true;event = event || cssiFixEvent(window.event);var handlers = this.events[event.type];for (var i in handlers) {this.$$cssiHandleEvent = handlers[i];if (this.$$cssiHandleEvent(event) === false) {returnValue = false;}}return returnValue;};
function cssiFixEvent(event) {event.preventDefault = cssiFixEvent.preventDefault;event.stopPropagation = cssiFixEvent.stopPropagation;return event;};
cssiFixEvent.preventDefault = function() {this.returnValue = false;};
cssiFixEvent.stopPropagation = function() {	this.cancelBubble = true;};

// Begin CSS Iterator - GPL v3 license
var cssi = {
    className: document.all ? 'className' : 'class',
    containers: [], // create a namespace to store the iteration containers in
    getTarget: function(t){var tt = t || window.event;return tt.target || tt.srcElement},
    init: function(iterators){
	//Gets the targets in the page based on the global 'iterators' variable.
	//Creates event delegation containers from them, and sets their arams
	//based on the same iterators string, includig their events.
        for (var kk = 0; kk < 30 ; kk++){
            if (!iterators[kk])return;
            var p = currentContainerParams = iterators[kk].split(" ");
            var co = this.containers[kk] = document.getElementById(p[0]);
            co.steps = p[1];
            co.expansionRate = p[2];
            co.resetting = p[3];
            co.defaultNode = p[5];
            co.defaultClass = document.getElementById(p[6])||p[6];
            co.delay = p[7];
            co.locked = false;
            co.moving = 0;
            co.i = 1;
            co.cssMinStep = 1;
            co.boxContainerId = p[0];
            co.expandedBoxContainerId = 'ex_' + p[0];
            if(p[8]){  // If any subscribers are present, set them
                cssi.setSubscribers.call(cssi.containers[kk],p[8]);
            };
            cssiAddEvent(co,p[4],cssi.iterateTargets);
        };
    },
    setSubscribers: function(args){
    //I used the word subscribers because I've been debating whether to trigger other containers via the observer pattern.
		this.subscribersArray = [];
        this.args = args.split(","); //convert the string args to an array of container names
        for(var i = 0;i<this.args.length;i++){
            var oneSubscriber = document.getElementById(this.args[i]);  // get each container from the name and push it into subscribersArray
            this.subscribersArray.push(oneSubscriber);
        };
    },
    iterateTargets: function(t){
        //Controls which containers iterate, and when.
		//Closures are our friends!
		// ******TODO***** REMOVE EVENTS HERE??
        this.t = cssi.getTarget(t);
        var temp = this;
        setTimeout(function(){var foo = function(){return cssi.iterateOneContainer.call(temp,temp.t)};return foo();},temp.delay);
        if(temp.subscribersArray){
            for (var i=0;i<temp.subscribersArray.length;i++){
                var tempS = temp.subscribersArray[i];
                tempS.isSubscriber = true;
                setTimeout(function(){var foo = function(){cssi.iterateOneContainer.call(tempS,tempS.defaultClass)};return foo();},tempS.delay);
            };
        };
    },
    iterateOneContainer: function(target){
		//controls how any containter iterates, then hands off to expand/change/contract
        this.target = target;
        if ((this.moving != null) && (this.moving != 0)) return; // Make sure the box isn't in the process of iterating
        for (var n=0;n<20;n++){ // explore up through the parent nodes to find the defaultNode
            if (this.defaultNode != this.target.nodeName){
                this.target = this.target.parentNode;
				if (this.target.nodeName == "BODY") return;
            }else{
                break;
            };
        };
        if ((this.isSubscriber) && (this.target != this.defaultClass)){ //check to see that the element they're clicking on is the target node.
            return;
        };
        if (this.id == (this.boxContainerId + this.steps)) { // Tests to see if the container is expanded.
        //If expanded...
            if ((this.isSubscriber && this.target == this.defaultClass) || (!this.isSubscriber && this.target == this.box_aObj)){
                cssi.contract.call(this);
            }
            else if (this.defaultNode == this.target.nodeName){
                this.setAttribute('id',(this.expandedBoxContainerId + this.i));
                cssi.change.call(this);
            }
            else{
                console.log('container is expanded, but all tests failed on 125... returning');
                return;
            };
        };
        if (this.id == (this.boxContainerId)) {
            this.box_aObj = this.target;
            cssi.expand.call(this);
        };//*****TODO***** CHECK FOR SWITCH HERE?
    },
	//expand/iterUp, change/iterChange, and contract/iterDown each work together
	//to start then control the iteration of the delegation container ID.
	//The best way to see this in action is to use firebug to watch the table ID and
	//target TD class name as they change. (set breakpoints on  lines191 & 207)
	//The names make the most sense if you think of them in the context of animating
	//boxes in a calendar, which was the first project this was designed for.
    expand: function(){  //Preps elements for expansion and starts iterup.
		this.prevClasses = this.box_aObj.getAttribute(cssi.className);
        this.box_aObj.setAttribute(cssi.className,'box_a' + ' ' + (this.prevClasses || ""));
        var temp = this;
        temp.moving = setInterval(function(){cssi.iterUp.call(temp)}, temp.expansionRate);
    },
    change: function(){ //Preps elements for expansion/contraction and starts iterchange.
        this.box_bObj = this.target;
        this.box_bObj.prevClasses = this.box_bObj.getAttribute(cssi.className);
		this.box_bObj.setAttribute(cssi.className,'locked_closed' + ' ' + (this.box_bObj.prevClasses || ""));
        this.box_aObj.setAttribute(cssi.className,'locked_open' + ' ' + (this.prevClasses || ""));
        this.i = this.cssMinStep;
		this.setAttribute('id',this.expandedBoxContainerId + this.i);
		this.box_bObj.setAttribute(cssi.className,'box_a' + ' ' + (this.box_bObj.prevClasses || ""));
        this.box_aObj.setAttribute(cssi.className,'box_b' + ' ' + (this.prevClasses || ""));
        var temp = this;
        temp.moving = setInterval(function(){cssi.iterChange.call(temp)}, temp.expansionRate);
    },
    contract: function(){ ////Preps elements for contraction and starts iterdown.
        this.box_aObj.setAttribute(cssi.className,'box_a' + ' ' + (this.prevClasses || ""));
        var temp = this;
        temp.moving = setInterval(function(){cssi.iterDown.call(temp)}, temp.expansionRate);
    },
	iterUp: function (){
		if (this.i<this.steps){
    	    this.i++;
			this.setAttribute('id',this.boxContainerId + this.i);
			if (this.locked == 2) {
                return;
            };
    	}else{
			clearInterval(this.moving);
			this.box_aObj.setAttribute(cssi.className,'locked_open' + ' ' + (this.prevClasses || ""));
			this.moving = null;
			if (this.resetting == 'oneway'){
			    this.locked = true;
		    };
			if (this.resetting == 'reset') {
			    this.i = 0;this.iterDown();
		    };
			return;
		};
	},
    iterChange: function(){
        if (this.i<this.steps){
			this.setAttribute('id',this.expandedBoxContainerId + this.i);
            this.i++;
        }else{
            clearInterval(this.moving);
            this.box_bObj.setAttribute(cssi.className,'locked_open' + ' ' + (this.box_bObj.prevClasses || ""));
            this.box_aObj.setAttribute(cssi.className,'locked_closed' + ' ' + (this.prevClasses || ""));
            this.setAttribute('id',this.boxContainerId + this.steps);
            this.box_aObj.setAttribute(cssi.className,this.prevClasses || "");
            this.prevClasses = this.box_bObj.prevClasses;
            this.box_aObj = this.box_bObj;
            this.moving = null;
        };
    },
    iterDown: function(){
        if (this.i>0){
			this.setAttribute('id',this.boxContainerId + this.i);
            this.i--;
        }
        else{
            clearInterval(this.moving);
            this.i = this.cssMinStep;
            this.box_aObj.setAttribute(cssi.className,this.prevClasses || "");
            this.setAttribute('id',this.boxContainerId);
            this.prevClasses = null;
            this.moving = null;
			return;
        };
    }
    //*uncomment this line for easy setting of params on the fly*    setParams: function(paramsandValuesArray){for (var i = 0;i<(paramsandValuesArray.length * 2);i++){co[arguments[i]] = arguments[i+1];i++;};}
};
window.onload = function(){
	// Set all the containers on the page by that have the same IDs as the first word in the 'iterators' variable.
    cssi.init(iteratorList);
};