
// The code following is the property of TableBrain.  It may not be used or duplicated
// for any purpose without the consent of TableBrain.


if (window.addProgress) window.addProgress(5);

Prototype.Browser.iPhone = Prototype.Browser.MobileSafari;

var BrainInfo = {
    Browser: {
	iPhone: Prototype.Browser.iPhone
    },
    BrowserIs: Prototype.Browser.IE ? 'IE' :
               Prototype.Browser.Opera  ? 'Opera' :
               Prototype.Browser.iPhone ? 'iPhone' :
               Prototype.Browser.WebKit  ? 'WebKit' :
               Prototype.Browser.Gecko  ? 'Gecko' :
               'unknown',
    _complete_: true
};
  

if ( ! TableBrain) var TableBrain = {};

TableBrain.version = 1.0037;

TableBrain.init = function() {
    if (window.addProgress) window.addProgress(5);
};


var BrainEffect = {
    effects: [],
    timer: null,
    last: null,
    afterAll: 0,
    accumulator: null,

    timerHandler: function() {
	//logger.info("here");

	BrainEffect.timer = null;

	var now = new Date().getTime();
	var effects = BrainEffect.effects;
	var effectsLen = effects.length;
	var i = 0;
	var delayed = false;
	
	while (i < effectsLen) {
	    var effect = effects[i];

	    if (now >= effect.start) {
		if (effect.delayedStart) {
		    effect.delayedStart(effect);
		    effect.delayedStart = null;
		}
		if (effect.doStart) {
		    if (BrainEffect.start(effect)) delayed = true;
		}

		if (now >= effect.finish) {
		    // at end - do clean up
		    if (effect.trash) {
			effects.splice(i, 1);
			effectsLen = effects.length;
			BrainEffect.effects = effects;
		    } else {
			if (effect.extra.again && effect.extra.again(effect)) {
			    effect.start = effect.finish;
			    effect.finish = effect.finish + effect.extra.duration * 1000;
			} else BrainEffect.finish(effect);
		    }

		} else {
		    if (effect.changeHandler) {
			// in the middle
			var delta = now - effect.start;
			delta = delta * effect.timeScaling;
			delta = effect.transition(delta);
			effect.changeHandler(effect, delta);
		    }
		    ++i;
		}

	    } else {
		++i;
	    }
	}

	if (effectsLen > 0) {
	    // kick off the next timer
	    BrainEffect.timer = setTimeout(function() { BrainEffect.timerHandler(); }, delayed ? 1 : 25);
	    //if (delayed) log('BrainEffect() delayed');
	}
    }
};

BrainEffect.Base = function(el, handlers, extra) {
    var now = new Date().getTime();

    var extra = Object.extend({
	    duration:   1.0,   // seconds
	    delay:      0.0,   // seconds
	    delayFrom:  null,
	    from:       0.0,
	    to:         1.0,
	    transition: Effect.Transitions.sinoidal,
	    accelerate: true
	}, extra);

    var start = now;
    var altStart;

    if (extra.delayFrom) {
	switch (typeof extra.delayFrom) {
	case 'string':
	    switch (extra.delayFrom) {
	    case 'now':
		break;
	    case 'afterAll':
		altStart = BrainEffect.afterAll;
		if (altStart && start < altStart) start = altStart;
		break;
	    case 'lastStart':
		altStart = BrainEffect.last;
		if (altStart && start < altStart.start) start = altStart.start;
		break;
	    case 'lastFinish':
		altStart = BrainEffect.last;
		if (altStart && start < altStart.finish) start = altStart.finish;
		break;
	    }
	}
    }
    if (extra.delay) start += extra.delay * 1000;

    extra.fromToDelta = extra.to - extra.from;

    /*
    if(!el.length) {
    	this.el = $(el);
    } else {
    	this.el = el;
    }
    */
    this.el = $(el);

    this.extra = extra;
    this.transition = extra.transition;
    this.start = start;
    this.finish = start + (extra.duration * 1000);
    this.timeScaling = (0.001 / extra.duration) * (extra.times ? extra.times : 1.0);
    this.startHandler = handlers[0];
    this.changeHandler = handlers[1];
    this.finishHandler = handlers[2];
    this.doStart = handlers[0] || extra.atStart ? true : false;

    BrainEffect.last = this;
    if (BrainEffect.afterAll < this.finish) BrainEffect.afterAll = this.finish;

    // add it to the queue
    BrainEffect.effects.push(this);

    // start the timer if necessary
    if ( ! BrainEffect.timer) {
	BrainEffect.timer = setTimeout(function() { BrainEffect.timerHandler(); }, 1);
    }
};

BrainEffect.start = function(effect) {
    if (effect.extra.atStart) effect.extra.atStart(effect);  // user defined pre-effect
    if (effect.startHandler) effect.delayedStart = effect.startHandler(effect);
    effect.doStart = false;

    //log('BrainEffect.start() done');
    return effect.delayedStart;
};

BrainEffect.finish = function(effect) {
    if (effect.changeHandler) effect.changeHandler(effect, 1.0);
    if (effect.finishHandler) effect.finishHandler(effect);
    if (effect.extra.atFinish) effect.extra.atFinish(effect);  // user defined after effect
    effect.trash = true;

    //log('BrainEffect.finish() done');
};


/*
BrainEffect.show = function(el) {
    el.style.display = el._displayAs;
};

BrainEffect.hide = function(el) {
    el.style.display = 'none';
};
*/


BrainEffect.Move_start = function(e) {
    var el = e.el;
    var style = el.style;
    var extra = e.extra;
    var xStart = 0, yStart = 0, wStart = 0, hStart = 0;
    var xFinish = 0, yFinish = 0, wFinish = 0, hFinish = 0;
    var delayed = null;
    var sizingToo = extra.sizingToo;
    if (sizingToo) {
	//wStart = style.width ? parseInt(style.width) : el.offsetWidth;
	//hStart = style.height ? parseInt(style.height) : el.offsetHeight;
	//wStart = el.offsetWidth;
	//hStart = el.offsetHeight;
	wStart = el.clientWidth;
	hStart = el.clientHeight;
    }

    if (extra && extra.startAt) {
	var startAt = extra.startAt;
	xStart = startAt.x;
	yStart = startAt.y;
	if (startAt.w) wStart = startAt.w;
	if (startAt.h) hStart = startAt.h;
    } else {
	if (style.position == 'absolute' && style.left && style.top) {
	    //console.log('got it all');
	    xStart = parseInt(style.left);
	    yStart = parseInt(style.top);
	} else {
	    //console.log('missing something');
	    //var xy = el.viewportOffset();
	    //xStart = xy[0];
	    //yStart = xy[1];
	    xStart = el.offsetLeft;
	    yStart = el.offsetTop;
	    //style.position = 'absolute';
	}
    }

    var endEl = e.endEl;
    var endStyle = endEl.style;
    if (endStyle) {
	if (endStyle.position == 'absolute' && endStyle.left && endStyle.top) {
	    xFinish = parseInt(endStyle.left);
	    yFinish = parseInt(endStyle.top);
	    if (sizingToo) {
		wFinish = endStyle.width ? parseInt(endStyle.width) : endEl.offsetWidth;
		hFinish = endStyle.height ? parseInt(endStyle.height) : endEl.offsetHeight;
	    }
	} else {
	    var offEl = el.getOffsetParent();
	    var xy = endEl.viewportOffset();
	    //console.log(endEl.id, xy, "  ", offEl, offEl.offsetLeft, offEl.offsetTop);
	    xFinish = xy[0] - (offEl.offsetLeft || 0);
	    yFinish = xy[1] - (offEl.offsetTop || 0);
	    if (sizingToo) {
		wFinish = endEl.offsetWidth;
		hFinish = endEl.offsetHeight;		
	    }
	}
    } else {
	xFinish = endEl.x;
	yFinish = endEl.y;
	wFinish = endEl.w;
	hFinish = endEl.h;
    }

    e.xStart = xStart;
    e.yStart = yStart;
    e.xFinish = xFinish;
    e.yFinish = yFinish;
    e.xDelta = e.xFinish - e.xStart;
    e.yDelta = e.yFinish - e.yStart;
    if (sizingToo) {
	e.wStart = wStart;
	e.hStart = hStart;
	e.wFinish = wFinish;
	e.hFinish = hFinish;
	e.wDelta = e.wFinish - e.wStart;
	e.hDelta = e.hFinish - e.hStart;
	//if (el === $('info')) alert(hStart);
	//logger.innerHTML = '';
	//log('hStart=' + hStart + ', hFinish=' + hFinish + ', e.hDelta=' + e.hDelta);
    } else {
	e.hDetla = 0;
	e.wDelta = 0;
    }
    /*
    if (Prototype.Browser.iPhone) {
	var duration = (extra.duration * 1000) + 'ms';
	style.webkitTransition = 'left ' + duration + ' ease-in-out, top ' + duration + ' ease-in-out' +
	    (sizingToo ? ', width ' + duration + ' ease-in-out, height ' + duration + ' ease-in-out': '');
	//log('webkitTransition = ' + style.webkitTransition);
	delayed = BrainEffect.Move_startDelayed;
    }
    */
    //log('BrainEffect.Move_start() done');
    return delayed;
};

BrainEffect.Move_startDelayed = function(e) {
    var style = e.el.style;
    style.left = e.xFinish + 'px';
    style.top = e.yFinish + 'px';
    if (e.extra.sizingToo) {
	style.width = e.wFinish + 'px';
	style.height = e.hFinish + 'px';
    }
};

BrainEffect.Move_change = function(e, delta) {
    var style = e.el.style;
    /*
    style.left = (e.xStart + (e.xDelta * delta)) + 'px';
    style.top = (e.yStart + (e.yDelta * delta)) + 'px';
    if (e.extra.sizingToo) {
	style.width = (e.wStart + (e.wDelta * delta)) + 'px';
	style.height = (e.hStart + (e.hDelta * delta)) + 'px';
        //log('height = (' + e.hStart + ' + (' + e.hDelta + ' * ' + delta + ')) + px');
    }
    */
    if (e.xDelta) style.left = (e.xStart + (e.xDelta * delta)) + 'px';
    if (e.yDelta) style.top = (e.yStart + (e.yDelta * delta)) + 'px';
    if (e.wDelta) style.width = (e.wStart + (e.wDelta * delta)) + 'px';
    if (e.hDelta) style.height = (e.hStart + (e.hDelta * delta)) + 'px';
};

BrainEffect.Move_finish = function(e) {
    var extra = e.extra;
    /*
    if (Prototype.Browser.iPhone) {
	var style = e.el.style;
	style.webkitTransition = 'left 0, top 0' + (extra.sizingToo ? ', width 0, height 0' : '');
	//log('BrainEffect.Move_finish() cleared webkitTransition');
    }
    */

    //log('BrainEffect.Move_finish() done');
};

BrainEffect.Move = function(el, endEl, extra) {
    var base = new BrainEffect.Base(el, [BrainEffect.Move_start,
					 //					 Prototype.Browser.iPhone ? null : BrainEffect.Move_change,
					 BrainEffect.Move_change,
					 BrainEffect.Move_finish], extra);
    base.endEl = endEl;
    return base;
};


BrainEffect.Resize_start = function(e) {
    var extra = e.extra;
    var style = e.el.style;

    var wS = parseInt(style.width);
    var hS = parseInt(style.height);

    var endEl = e.endEl;
    var wF, hF;
    if (endEl.style) {
	wF = endEl.offsetWidth;
	hF = endEl.offsetHeight;
    } else {
	wF = endEl.w;
	hF = endEl.h;
    }

    e.wStart = wS;
    e.hStart = hS;
    e.wFinish = wF;
    e.hFinish = hF;
    e.wDelta = wF - wS;
    e.hDelta = hF - hS;

    if (Prototype.Browser.iPhone) {
	var duration = (extra.duration * 1000) + 'ms';
	style.webkitTransition = 'width ' + duration + ' ease-in-out, height ' + duration + ' ease-in-out';
	//log('webkitTransition = ' + style.webkitTransition);
	delayed = BrainEffect.Resize_startDelayed;
    }

    //log('BrainEffect.Move_start() done');
    return delayed;
};

BrainEffect.Resize_startDelayed = function(e) {
    var style = e.el.style;
    style.width = e.wFinish + 'px';
    style.height = e.hFinish + 'px';
};

BrainEffect.Resize_finish = function(e) {
    if (Prototype.Browser.iPhone) {
	var style = e.el.style;
	style.webkitTransition = 'width 0, height 0';
    }
};

BrainEffect.Resize_change = function(e, delta) {
    var style = e.el.style;
    style.width = (e.wStart + (e.wDelta * delta)) + 'px';
    style.height = (e.hStart + (e.hDelta * delta)) + 'px';
};

BrainEffect.Resize = function(el, endEl, extra) {
    var base = new BrainEffect.Base(el, [BrainEffect.Resize_start,
					 Prototype.Browser.iPhone ? null : BrainEffect.Resize_change,
					 BrainEffect.Resize_finish], extra);

    base.endEl = endEl;
    return base;
};


BrainEffect.Ellipse_start = function(e) {
    var extra = e.extra;
    e.xCenter = extra.x ? extra.x : 0;
    e.yCenter = extra.y ? extra.y : 0;
    e.width = extra.w ? extra.w : 0;
    e.height = extra.h ? extra.h : 0;
    e.angle0 = extra.a0 ? extra.a0 * 1.0 : 0.0;
    e.angle1 = (extra.a1 === undefined) ? extra.a0 + 360.0 : extra.a1
    e.angleDelta = e.angle1 - e.angle0;
    e.PI180 = Math.PI / 180;
    var beta = -e.angle0 * e.PI180;
    e.sinBeta = Math.sin(beta);
    e.cosBeta = Math.cos(beta);
    return null;
};

BrainEffect.Ellipse_change = function(e, delta) {
    //alert('Ellipse_change');
    var style = e.el.style;
    var angle = e.angle0 + e.angleDelta * delta;
    //console.log(angle);
    var alpha = angle * e.PI180;
    var bSinAlpha = e.height * Math.sin(alpha);
    var aCosAlpha = e.width * Math.cos(alpha);
    var x = e.xCenter + (aCosAlpha * e.cosBeta - bSinAlpha * e.sinBeta);
    var y = e.yCenter + (aCosAlpha * e.sinBeta - bSinAlpha * e.cosBeta);
    style.marginLeft = x + 'px';
    style.marginTop = y + 'px';
};

BrainEffect.Ellipse = function(el, extra) {
    // extra should include x, y, w, h, a0, a1 (each otherwise defaulting to zero)
    var base = new BrainEffect.Base(el, [BrainEffect.Ellipse_start,
					 BrainEffect.Ellipse_change,
					 null], extra);
    return base;
};


BrainEffect.BackgroundColor_start = function(e) {
    var extra = e.extra;
    var style = e.el.style;

    var sColor = style.backgroundColor;
    var color = 0;

    e.wStart = wS;
    e.hStart = hS;
    e.wFinish = wF;
    e.hFinish = hF;
    e.wDelta = wF - wS;
    e.hDelta = hF - hS;

    var duration = (extra.duration * 1000) + 'ms';
    style.webkitTransition = 'width ' + duration + ' ease-in-out, height ' + duration + ' ease-in-out';
    style.width = e.wFinish + 'px';
    style.height = e.hFinish + 'px';

    return delayed;
};

BrainEffect.BackgroundColor_change = function(e, delta) {
    var style = e.el.style;
    style.width = (e.wStart + (e.wDelta * delta)) + 'px';
    style.height = (e.hStart + (e.hDelta * delta)) + 'px';
};

BrainEffect.BackgroundColor_finish = function(e) {
    var style = e.el.style;
    style.webkitTransition = 'width 0, height 0';
};

BrainEffect.BackgroundColor = function(el, endEl, extra) {
    var base = null;
    if (Prototype.Browser.iPhone) {
	base = new BrainEffect.Base(el, [BrainEffect.BackgroundColor_start,
					 BrainEffect.BackgroundColor_change,
					 BrainEffect.BackgroundColor_finish], extra);

	base.endEl = endEl;
    }
    return base;
};


if (window.addProgress) window.addProgress(5);


BrainEffect.MarginLeft_start = function(e) {
    var style = e.el.style;
    var finishAt = e.extra.finishAt;
    if (typeof finishAt == 'object') {
	finishAt = finishAt.style.left || finishAt.offsetLeft || 0;
    }
    e.xStart = e.extra.startAt || parseInt(style.marginLeft) || 0;
    e.xFinish = parseInt(finishAt) || 0;
    e.xDelta = e.xFinish - e.xStart;
};

BrainEffect.MarginLeft_change = function(e, delta) {
    var style = e.el.style;
    style.marginLeft = (e.xStart + (e.xDelta * delta)) + 'px';
};

BrainEffect.MarginLeft = function(el, extra) {
    var base = new BrainEffect.Base(el, [BrainEffect.MarginLeft_start, BrainEffect.MarginLeft_change, null], extra);
    return base;
};


BrainEffect.MarginTop_start = function(e) {
    var style = e.el.style;
    var finishAt = e.extra.finishAt;
    if (typeof finishAt == 'object') {
	finishAt = finishAt.style.top || finishAt.offsetTop || 0;
    }
    e.xStart = e.extra.startAt || parseInt(style.marginTop) || 0;
    e.xFinish = parseInt(finishAt) || 0;
    e.xDelta = e.xFinish - e.xStart;
};

BrainEffect.MarginTop_change = function(e, delta) {
    var style = e.el.style;
    style.marginTop = (e.xStart + (e.xDelta * delta)) + 'px';
};

BrainEffect.MarginTop = function(el, extra) {
    var base = new BrainEffect.Base(el, [BrainEffect.MarginTop_start, BrainEffect.MarginTop_change, null], extra);
    return base;
};


BrainEffect.Rotate_change = function(e, delta) {
    var extra = e.extra;
    var value = (extra.fromToDelta * delta) + extra.from;
    if (Prototype.Browser.WebKit) e.el.style.webkitTransform = 'rotate(' + value + 'deg)';
};

BrainEffect.Rotate = function(el, extra) {
    return new BrainEffect.Base(el, [null, BrainEffect.Rotate_change, null], extra);
};


BrainEffect.Opacity_change = function(e, delta) {
    var extra = e.extra;
    var value = (extra.fromToDelta * delta) + extra.from;
    //logger.info("Opacity_change = " + value);
    e.el.setOpacity(value);
};

BrainEffect.Opacity = function(el, extra) {
    return new BrainEffect.Base(el, [null, BrainEffect.Opacity_change, null],
				Object.extend({
					atStart: function (e) { e.el.show(); },
					atFinish: function (e) { if (e.extra.to == 0) e.el.hide(); }
				    }, extra));
};


// cop out
BrainEffect.Fade = function(el, extra) {
    return BrainEffect.Opacity(el, Object.extend( { from: 1, to: 0 }, extra));
};

// cop out
BrainEffect.Appear = function(el, extra) {
    return BrainEffect.Opacity(el, Object.extend( { from: 0, to: 1 }, extra));
};

// same as Fade (for now)
BrainEffect.Shrink = function(el, extra) {
    return BrainEffect.Opacity(el, Object.extend( { from: 1, to: 0 }, extra));
};

// same as Appear (for now)
BrainEffect.Grow = function(el, extra) {
    return BrainEffect.Opacity(el, Object.extend( { from: 0, to: 1 }, extra));
};


BrainEffect.Pulsate_change = function(e, delta) {
    var extra = e.extra;
    var value = (extra.fromToDelta * delta) + extra.from;
    e.el.setOpacity(value);
};

BrainEffect.Pulsate = function(el, extra) {
    return new BrainEffect.Base(el, [null, BrainEffect.Pulsate_change, null], extra);
};


function BrainCookies(options) {
    var defaults = {
	duration: 'session',
	expires:  '',
	domain:   '',
	path:     '',
	secure:   ''
    };
    Object.extend(defaults, options || {});
    
    if (defaults.duration != 'session') {
	var date = new Date();
	date = new Date(date.getTime() + (defaults.duration * 1000));
	defaults.expires = '; expires=' + date.toGMTString();
    }
    if (defaults.domain != '') {
	defaults.domain = '; domain=' + escape(defaults.domain);
    }
    if (defaults.path != '') {
	defaults.path = '; path=' + escape(defaults.path);
    }
    if (defaults.secure != '') {
	defaults.secure = '; secure';
    }
    this.options = defaults;
};

BrainCookies.prototype.set = function(name, value, duration) {
    var options = this.options;
    var cookie = escape(name) + "=" + escape(value);
    var myExpires = options.expires;
    if (duration && duration !== options.duration) {
	if (duration == 'session') {
	    myExpires = '';
	} else {
	    var date = new Date();
	    date = new Date(date.getTime() + (duration * 1000));
	    myExpires = '; expires=' + date.toGMTString();	    
	}
    }
    try {
	var cookieValue = cookie + myExpires + options.path + options.domain + options.secure;
	document.cookie = cookieValue;
    } catch (e) {
	return false;
    }
    return true;
};

BrainCookies.prototype.getAll = function() {
    var cookieHash = {};
    var cookies = document.cookie.split('; ');
    var cookieCount = cookies.length;
    for (var i = 0; i < cookieCount; ++i) {
	var parts = cookies[i].split('=');
	var name = unescape(parts[0]);
	var value = parts[1];
	if (name && value != null) cookieHash[name] = unescape(value);
    }    
    return cookieHash;
};

BrainCookies.prototype.get = function(name) {
    var cookies = document.cookie.split('; ');
    var cookieCount = cookies.length;
    for (var i = 0; i < cookieCount; ++i) {
	var parts = cookies[i].split('=');
	var cookieName = unescape(parts[0]);
	var cookieValue = parts[1];
	if (cookieName == name && cookieValue != null) return unescape(cookieValue);
    }
    return null;
};

BrainCookies.prototype.discard = function(name) {
    var options = this.options;
    try {
	document.cookie = escape(name) + '=; expires=Sat, 01-Jan-2000 00:00:00 GMT' + options.path + options.domain + options.secure;
    } catch (e) {
	return false;
    }
    return true;    
};


var BrainInput = {
    lastTap: null,
    touch0: null,
    touchCount: 0,
    monitorCount: 0,
    tapCount: 1,
    touchRecent: null,
    holdTimer: null,
    holdTimeout: 300,
    doubleTapTimeout: 400,
    slideFingerAllowance: 10,
    slideMouseAllowance: 3,
    dropZone: null,
    touchSession: 0,

    // obj methods that could be called:
    //   tap:       mouse click / single finger single tap (will preceed doubleTap)
    //              (examine t0.tapCount to see if this is a double tap (or higher))
    //              (on multi taps t0.firstTap will be a reference to the first t0
    //                in the tap sequence (preserving any info you attached))
    //   hold:      mouse / finger(s) starting to hold in place
    //   holdMore:  fingers (more than one) are holding in place
    //   held:      mouse / finger(s) let go after holding
    //   slide:     mouse / finger(s) moving while in contact
    //              (examine t0.holding to see if being held before sliding)
    //   slid:      mouse / finger(s) finished moving and let go
    //   down:      mouse down / first finger makes contact
    //   downMore: (2nd(+) finger makes contact)
    //   up:        mouse up / only finger looses contact
    //   upMore:   (2nd(+) finger looses contact)
    // surrogateClass defaults to 'none', if a value is passed in for surrogateClass then
    //     we create an element that registers for the user input and passes it to el
    //   the surrogate gets defined with the same ID as el with "_surrogate" appended
    //   the surrogate gets defined with the classes that you specify in "surrogateClass",
    //     which could be simply the value True
    //   the surrogate gets an additional class of "brainInputSurrogate"
    //   make sure you give the _surrogate class a sufficiently high z-index to sit
    //     above the elements that would otherwise interfere with mouse / touch detection
    monitor: function(el, obj, id, surrogateElementClass) {
	el = $(el);
	var myEl = el;

	if (surrogateElementClass) {
	    if (surrogateElementClass === true) surrogateElementClass = '';
	    var surrogateElName = el.id + '_surrogate';
	    var surrogateEl = document.createElement('div');
	    surrogateEl.setAttribute('id', surrogateElName);
	    surrogateEl.className = surrogateElementClass + ' brainInputSurrogate';
	    surrogateEl.underlyingEl = el;
	    document.body.appendChild(surrogateEl);
	    myEl = $(surrogateElName);
	    el.brainInputSurrogate = myEl;
	}
	myEl.brainInputObj = obj;
	myEl.brainInputID = (id === undefined) ? null : id;

	if ( ! obj.context) obj.context = obj;

	if (BrainInfo.Browser.iPhone) {
	    myEl.addEventListener('touchstart', BrainInputTouchStart, false);
	    myEl.addEventListener('touchmove',  BrainInputTouchMove,  false);
	    myEl.addEventListener('touchend',   BrainInputTouchEnd,   false);
	} else {
	    myEl.observe('mousedown',  BrainInputTouchStart);
	    if (Prototype.Browser.IE)
		myEl.observe('dblclick', BrainInputDoubleClick);
	    if (this.monitorCount == 0) {
		try {
		    document.captureEvents(Event.MOUSEMOVE);
		    document.captureEvents(Event.MOUSEUP);
		} catch(e) { }
		document.observe('mousemove', BrainInputTouchMove);
		document.observe('mouseup',   BrainInputTouchEnd);
	    }
	}
	this.monitorCount++;
    },

    // kills the surrogate if one is present
    stopMonitoring: function(el, obj, id) {
	if ( ! id) id = null;
	if (el) {
	    var myEl = el;
	    var surrogateEl = el.brainInputSurrogate;
	    if (surrogateEl) myEl = el.brainInputSurrogate;
	    
	    if (( ! obj) || (myEl.brainInputObj === obj && myEl.brainInputID === id)) {
		this.monitorCount--;

		myEl.brainInputObj = null;
		if (BrainInfo.Browser.iPhone) {
		    myEl.removeEventListener('touchstart', BrainInputTouchStart, false);
		    myEl.removeEventListener('touchmove',  BrainInputTouchMove,  false);
		    myEl.removeEventListener('touchend',   BrainInputTouchEnd,   false);
		} else {
		    myEl.stopObserving('mousedown',  BrainInputTouchStart);
		    if (Prototype.Browser.IE)
			myEl.stopObserving('dblclick', BrainInputDoubleClick);
		    if (this.monitorCount == 0) {
			try {
			    document.releaseEvents(Event.MOUSEMOVE);
			    document.releaseEvents(Event.MOUSEUP);
			} catch(e) { }
			document.stopObserving('mousemove', BrainInputTouchMove);
			document.stopObserving('mouseup',   BrainInputTouchEnd);
		    }
		}
		if (surrogateEl) {
		    surrogateEl.underlyingEl = null;
		    el.brainInputSurrogate = null;
		    document.body.removeChild(surrogateEl);
		}
	    }
	}
    },

    findDropZone: function(dropZones, x, y) {
	var dropZoneCount = dropZones.length;
	for (var i = 0; i < dropZoneCount; ++i) {
	    var dropZone = dropZones[i];
	    if (dropZone.x1 === undefined) {
		el = $(dropZone.el);
		var x1 = 0, y1 = 0, w = el.offsetWidth, h = el.offsetHeight;
		do {
		    x1 += el.offsetLeft || 0;
		    y1 += el.offsetTop  || 0;
		    el = el.offsetParent;
		} while (el);
		dropZone.x1 = x1;
		dropZone.y1 = y1;
		dropZone.x2 = x1 + w;
		dropZone.y2 = y1 + h;
	    }
	    if (dropZone.x1 <= x && x <= dropZone.x2 && dropZone.y1 <= y && y <= dropZone.y2) {
		return dropZone.name ? dropZone.name : i;
	    }
	}
	return null;
    }
};

function BrainInputTouchStart(event) {
    var setHoldTimer = false;
    var touches = event.touches;
    var touchCount = touches ? touches.length : 1;
    var touch0 = BrainInput.touch0;
    if (touch0 && ! touches) {
	BrainInputTouchEnd(event);
	touch0 = null;
    }
    if ( ! touch0) {
	var contactEl = event.target;
	var el = contactEl;
	var obj = null;
	while (el) {
	    if (obj = el.brainInputObj) break;
	    el = el.parentNode;
	}
	if (obj) {
	    var touch = touches ? touches[0] : event;
	    var offset = el.viewportOffset();
	    var x = touch.clientX;
	    var y = touch.clientY;
	    var underlyingEl = el.underlyingEl;

	    var touch0 = {
		contactEl: contactEl, captureEl: el, el: (underlyingEl ? underlyingEl : el),
		obj: obj, tID: el.brainInputID, n: (new Date()).getTime(),
		x: x, y: y, offsetX: x - offset[0], offsetY: y - offset[1],
		viewportOffsetX: offset[0], viewportOffsetY: offset[1],
		holding: 0, sliding: 0, touchSession: ++BrainInput.touchSession,
		maxFingers: touches ? 1 : (event.button < 3 ? event.button : 3)
	    };
	    BrainInput.touch0 = touch0;
	    BrainInput.touchRecent = null;
	    setHoldTimer = true;
	    if (obj.down) {
		var changedTouches = event.changesTouches;
		if ( ! touches) {
		    touches = [ touch0 ];
		    changesTouches = touches;
		}
		obj.down.call(obj.context, touch0, touches, changedTouches);
	    }
	    BrainInput.touchCount = touch0.maxFingers;
	    if (el.setCapture) el.setCapture();
	}
    }
    if (touchCount > 1) {
	var touch0 = BrainInput.touch0;
	if (touch0) {
	    var obj = BrainInput.touch0.obj;
	    if (touch0.holding) setHoldTimer = true;
	    if (obj.downMore) obj.downMore.call(obj.context, touch0, touches, event.changedTouches);
	    if (touch0.maxFingers < touchCount) touch0.maxFingers = touchCount;
	}
	BrainInput.touchCount = touchCount;
    }
    if (setHoldTimer) {
	if (BrainInput.holdTimer) clearTimeout(BrainInput.holdTimer);
	BrainInput.holdTimer = setTimeout(BrainInputTouchHold, BrainInput.holdTimeout);
    }
    if (event.preventDefault) event.preventDefault(); else event.returnValue = false;
    return false;
}

function BrainInputTouchNoHold() {
    if (BrainInput.holdTimer) {
	clearTimeout(BrainInput.holdTimer);
	BrainInput.holdTimer = null;
    }
}

function BrainInputTouchHold() {
    if (BrainInput.holdTimer) {
	BrainInputTouchNoHold();
	var touch0 = BrainInput.touch0;
	touch0.holding = BrainInfo.Browser.iPhone ? BrainInput.touchCount : touch0.maxFingers;
	var obj = touch0.obj;
	if (obj) {
	    if (obj.hold) {
		obj.hold.call(obj.context, touch0);
	    }
	    if (obj.holdMore) {
		//obj.holdMore.call(obj.context, touch0);
	    }
	}
    }
}

function BrainInputTouchMove(event) {
    var touch0 = BrainInput.touch0;
    if (touch0) {
	var touches = event.touches;
	var obj = touch0.obj;
	var touch = touches ? event.touches[0] : event;
	var x = touch.clientX;
	var y = touch.clientY;
	var touch1 = { x: x, y: y };
	if ( ! touch0.sliding) {
	    var allow = touches ? BrainInput.slideFingerAllowance : BrainInput.slideMouseAllowance;
	    if (Math.abs(touch0.x - x) > allow || Math.abs(touch0.y - y) > allow) {
		BrainInputTouchNoHold();
		if (touches) {
		    if (touch0.sliding < touches.length) touch0.sliding = touches.length;
		} else {
		    if (touch0.sliding < touch0.maxFingers) touch0.sliding = touch0.maxFingers;
		}
		if (obj.slide)
		    obj.slide.call(obj.context, touch0, touches, event.changedTouches);
	    }
	}
	if (touch0.sliding && obj.sliding) {
	    var dropZones = touch0.dropZones;
	    if (dropZones) {
		var dropZone = BrainInput.findDropZone(dropZones, x, y);
		var dropChange = undefined;
		if (BrainInput.dropZone !== dropZone) {
		    if (dropZone === null) {
			dropChange = 'leave';
		    } else if (BrainInput.dropZone === null) {
			dropChange = 'enter';
		    } else {
			dropChange = 'transition';
		    }
		    BrainInput.dropZone = dropZone;
		}
		touch1.dropZone = dropZone;
		touch1.dropChange = dropChange;
	    }
	    obj.sliding.call(obj.context, touch0, touch1, touches, event.changedTouches);
	    touch0.lastDropZone = dropZone;
	}
	BrainInput.touchRecent = touch1;
    }
    if (event.preventDefault) event.preventDefault(); else event.returnValue = false;
    return false;
}

function BrainInputTouchEnd(event) {
    BrainInputTouchNoHold();
    var touch0 = BrainInput.touch0;

    if (touch0) {
	var touches = event.touches;
	var touchCount = touches ? touches.length : 0;
	var cTouches = event.changedTouches;
	var obj = touch0.obj;

	if (touchCount == 0) {
	    var touch1 = BrainInput.touchRecent;

	    if (touch0.captureEl.releaseCapture) touch0.captureEl.releaseCapture();
	    if (obj.up) obj.up.call(obj.context, touch0, touches, cTouches);
	    if (touch0.sliding) {
		if (obj.slid) obj.slid.call(obj.context, touch0, touch1, touches, cTouches);
		BrainInput.lastTap = null;
	    }
	    if (touch0.holding) {
		if (obj.held) obj.held.call(obj.context, touch0, touches, cTouches);
		BrainInput.lastTap = null;
	    }
	    if ( ! touch0.sliding && (obj.tap || obj.doubleTap)) {
		var now = (new Date()).getTime();
		var touchL = BrainInput.lastTap;
		var allow = touches ?
		    BrainInput.slideFingerAllowance : BrainInput.slideMouseAllowance;
		if (touchL && (now - touchL.n < BrainInput.doubleTapTimeout) &&
		    (Math.abs(touch0.x - touchL.x) < allow) &&
		    (Math.abs(touch0.y - touchL.y) < allow))
		{
		    BrainInput.tapCount++;
		    touch0.firstTap = touchL.firstTap ? touchL.firstTap : touchL;
		} else {
		    BrainInput.tapCount = 1;
		}
		touch0.tapCount = BrainInput.tapCount;
		if (obj.tap)
		    obj.tap.call(obj.context, touch0, touches, cTouches);
		if (touch0.tapCount == 2 && obj.doubleTap)
		    obj.doubleTap.call(obj.context, touch0, touches, cTouches);
		BrainInput.lastTap = touch0;
	    }
	    if (obj.click) {
		var doAction = true;
		if (touch1) {
		    doAction = false;
		    var x = touch1.x; y = touch1.y;
		    var offX = touch0.viewportOffsetX, offY = touch0.viewportOffsetY;
		    var offW = touch0.captureEl.offsetWidth, offH = touch0.captureEl.offsetHeight;
		    if (offX <= x && x <= (offX + offW) && offY <= y && y <= (offY + offH)) doAction = true;
		}
		if (doAction) obj.click.call(obj.context, touch0);
	    }

	    BrainInput.touch0 = null;
	    
	} else if (touches) {
	    if (obj.upMore) obj.upMore.call(obj.context, touch0, touches, cTouches);
	}
    }
    BrainInput.touchCount = touchCount;
    if (event.preventDefault) event.preventDefault(); else event.returnValue = false;
    return false;
}


function BrainInputDoubleClick(event) {
    var touchL = BrainInput.lastTap;
    if (touchL) {
	var obj = touchL.obj;
	var touch0 = Object.clone(touchL);
	touch0.n = (new Date()).getTime();
	touch0.faked = true;
	if (obj.down) obj.down.call(obj.context, touch0, null, null);
	if (obj.up) obj.up.call(obj.context, touch0, null, null);
	BrainInput.tapCount++;
	touch0.firstTap = touchL.firstTap ? touchL.firstTap : touchL;
	touch0.tapCount = BrainInput.tapCount;
	if (obj.tap)
	    obj.tap.call(obj.context, touch0, null, null);
	if (touch0.tapCount == 2 && obj.doubleTap)
	    obj.doubleTap.call(obj.context, touch0, null, null);
	BrainInput.lastTap = touch0;
    }
    if (event.preventDefault) event.preventDefault(); else event.returnValue = false;
    return false;
};


if (window.addProgress) window.addProgress(5);

TableBrain.init();
