/*-------------------------------------
Aggregate Student Scripts

FIXME: This is a temporary solution for js consolidation. The following
files are included in proper order...

	 common.js, plp_feedback.js, modal.js, 
	 ebooks.js, student.js

NOTE: several functions below do not exist in
other files.

-------------------------------------*/
// Flag to indicate if browser is IE
window.IE = /*@cc_on!@*/false;


var shiftPressed = false;
window.addEvent('keydown', function(e) {
    if( e.code == 16 ) {
        shiftPressed = true;
    }
} );

window.addEvent('keyup', function(e) {
    if( e.code == 16 ) {
        shiftPressed = false;
    }
} );

function follow_link(struct, depId) {
    if ( !sac_on ) {
        click_link( struct );
    }
    else if( can_click_link('/web/Student/Assignment-Responses/last?dep=' + depId) ) { 
        window.location = '/web/Student/Assignment-Responses/last?dep=' + depId;
    }
}

// Add a javascript function to the load event of a window
function addLoadEvent(func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			oldonload();
			func();
		}
	}
}


addLoadEvent(function() {
	var inputs = document.getElementsByTagName('input');
	var inputsLength = inputs.length;
	for(var i = 0; i < inputsLength; i++) {
		var inputType = inputs[i].type;
		inputType = inputType.toLowerCase();
		
		if(inputType == 'submit') {
			var saveButton = inputs[i];
			if(saveButton && saveButton.id) {
				saveButton.onmousedown = registerButtonPress;
				saveButton.onmouseup = registerButtonRelease;
			}
		}
	}
});

function bPress(obj) {
	var buttonStyle = getStyleObject(obj.id);
	if(buttonStyle) {
		buttonStyle.background = '#ccc';
	}
}

function bRelease(obj) {
	var buttonStyle = getStyleObject(obj.id);
	if(buttonStyle) {
		buttonStyle.background = 'url(/wastatic/common/img/sButtonBackground.gif)';
	}
}

function registerButtonPress(e) {
	var targ;
	if (!e) var e = window.event;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;
	if (targ.nodeType == 3) // defeat Safari bug
		targ = targ.parentNode;

	var buttonObject = document.getElementById(targ.id);
	bPress(buttonObject);
}

function registerButtonRelease(e) {
	var targ;
	if (!e) var e = window.event;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;
	if (targ.nodeType == 3) // defeat Safari bug
		targ = targ.parentNode;

	var buttonObject = document.getElementById(targ.id);	
	bRelease(buttonObject);
}

function setCourse(course) {
	document.getElementById('course').value = course;
}

function feedback_modal( asset_id, show_checkbox ) {
    //need to move this to another js file
    var closeButton = 'Close';

    //get content via ajax call to feedback rating component
    var url = '/wx/ra/tm';
    var upass = $('UserPass').value;
    var fc = new Request({
        url:url,
        onComplete: function(html) {
            var modal = new feedbackModal( html, 'n', closeButton, asset_id );
            modal.show();
        }
    });
    fc.send('UserPass=' + upass + '&show_checkbox=' + show_checkbox);
}

function set_no_modal_prefs() {
    var url = '/wx/ra/nm';
    var upass = $('UserPass').value;

    var rq = new Request({
        url:url,
        onComplete: function(html) {
            //reload opener
            reload_teachme();
        }
    });
    rq.send('UserPass=' + upass);
}

function submit_asset_feedback( asset_id ) {
    //send asset id and rec to ajax comp to store rating
    var url = '/wx/ra/st';
    var upass = $('UserPass').value;
    var rq = new Request({
        url:url,
        onComplete: function(html) {
            //reload opener
            reload_teachme();
        }
    });

    /* capture ranking value -- 0 = no rating, 1 = helpful, 2 = not helpful */
    var ranking = 0;
    if ( $('ranking_y').checked ) {
        ranking = 1;
    } else if ( $('ranking_n').checked ) {
        ranking = 2;
    }

    //see if checkbox exists and get checked attr
    if ( $('dont_show_box_Y') ) {
        var chkbox_val = $('dont_show_box_Y').checked;
        rq.send('UserPass=' + upass + '&asset_id=' + asset_id + '&dont_show_box=' + chkbox_val
            + '&ranking=' + ranking + '&comment=' + $('comment').value);
    } else {
        rq.send('UserPass=' + upass + '&asset_id=' + asset_id + '&ranking=' + ranking
            + '&comment=' + $('comment').value);
    }
}

function reload_teachme() {
    document.forms[0].struct.value = document.forms[0].savedStruct.value;
    document.forms[0].submit(this);
}

/*	
 *	updated and modified by Ian Quattlebaum
 *  to only do modal screens and work with 
 *  the latest version of mootools 1.2
*/
var debugFlag = false;
function debug(input) {
	if(debugFlag) {
		debug(input);
	}
}

var AscDialog = new Class({
	options: {
		closeButtonText: 'Save Assignment',
		speed: 500,
		maskOpacity: 0.3,
		maskColor: '#000000',
		isModal: false,
		useArrows: false,
		addCloseBtn: true,
		submitjs: false,
		popOpacity: 1,
		cornerRadius: 10,
		classPrefix: 'Asc',
        containerClass: false,
		boxColor: 'red',
		alignChoice: 'center',
		msgAlign: { 'left': 'pLeft', 'center': 'pCenter', 'right': 'pRight' },
		colorStyles : {
			'red' : { nw: 'nw', n: 'north', ne: 'ne', sw: 'sw', s: 's', se: 'se' },
			'blue' : { nw: 'bnw', n: 'bnorth', ne: 'bne', sw: 'bsw', s: 'bs', se: 'bse' },
			'grey' : { nw: 'gnw', n: 'gnorth', ne: 'gne', sw: 'gsw', s: 'gs', se: 'gse' }
		},
		place: {
			'ss': { target:'window', io:1, align:'n', offset:0, margin:0 }, // show start
			'se': { trans:'fly', target:'window', io:-1, align:'c', offset:0, margin:0 }, // show end
			'he': { trans:'fly', target:'window', io:1, align:'n', offset:0, margin:0 } // hide end
		},
		onHide: $empty,
		onShow: $empty,
		transition: Fx.Transitions.Quad.easeInOut
	},
	initialize: function(options){
		this.setOptions(options);
		this.isShowing = false;
		this.mask = false;
		this.pop = false;
		this.event = window.event;

		this.isIE = /MSIE (5.5|6.)/.test(navigator.userAgent);
		if (this.isIE) {
			this.options.useArrows = false;
		}

		this.fx_dir = 0; // track whether showing/hiding
		this.fx_in_process = false;
			
		window.addEvent('resize', function(e){ 
			this.update(e);
			if(this.isShowing){
				this.isShowing = false;
				this.show();
			}

		}.bind(this));			
		window.addEvent('scroll', function(e){
			this.update(e);
			/* fixes side scrolling, but a little buggy*/
			if(this.isShowing){
				this.isShowing = false;
				this.show();
			}
			
		}.bind(this));		

		this.init();
	},
	init: function(){

		if (this.pop) {
			//this.pop.remove();
		}
		this.add_pop();
		var fxels = [this.pop];

		if (this.options.isModal) {
			this.add_mask();
			fxels[1] = this.mask;
		} else if (this.isIE) {
			this.options.maskColor = '#FFF';
			this.add_mask();
		}

		this.fx = new Fx.Elements(fxels, {
			wait: false, 
			duration: this.options.speed, 
			transition: this.options.transition,
			onStart: function() {
				this.fx_in_process = true;
			}.bind(this), 
			onComplete: function() {
				switch (this.fx_dir) {
					case 1:
						this.isShowing = true;
						this.pop.focus(); // to activate pop close by ESC, the ESC keydown for window doesn't work in IE
						break;
					case 0:
						this.isShowing = false;
						this.pop.setStyles({
							'visibility':'hidden',
							'display': 'none'
						});
						if (this.options.isModal) {
							this.mask.setStyle('display', 'none');
						}
						if (!this.options.isModal && this.isIE) {
							this.mask.setOpacity(0);
						}
						break;				
				}
				this.fx_in_process = false;
			}.bind(this)
		});
	},
	add_mask: function(){
		if (!this.mask)	{
			if (this.isIE){
				// need to use IFRAME for IE in order to cover SELECT elements
				this.mask = new Element('iframe', {
					'class':this.options.classPrefix+'Mask',
					'src':"about:blank",
					'frameborder':0,
					'src':"about:blank"
				}).inject(document.body);
			} else {
				// make mask a div for other browsers
				this.mask = new Element('div', {
					'class':this.options.classPrefix+'Mask'
				}).inject(document.body);
			}
			this.mask.setStyles({
				'position':'absolute',
				'top': 0,
				'left': 0,
				'opacity': 0,
				'z-index': 9999,
				'background-color':this.options.maskColor,
				'display': 'none'
			});
		}
	},
	add_pop: function(){
        classes = this.options.containerClass 
                ? this.options.containerClass+' '+this.options.classPrefix+'Pop'
                : this.options.classPrefix+'Pop'

		this.pop = new Element('div', {
			'class':classes,
			'styles':{
				'position': 'absolute',
				'visibility': 'hidden',
				'top': -1000,
				'left': 0,
				'z-index': 10000,
				'display': 'none'
			}
		}).inject(document.body);

		this.pop.addEvent('keydown', function(e){ 
			this.esc(e);
		}.bind(this));


		// add table for pop with border graphics
		this.poptbl = new Element('table',{ 'class':'grid ' + this.options.msgAlign[this.options.alignChoice] }).injectInside(this.pop);
		this.poptbody = new Element('tbody').injectInside(this.poptbl);

/*
		boxColor: 'red',
		colorStyles : {
			'red' : { nw: 'nw', n: 'north', ne: 'ne', sw: 'sw', s: 's', se: 'se' },
			'blue' : { nw: 'bnw', n: 'bnorth', ne: 'bne', sw: 'bsw', s: 'bs', se: 'bse' }
		},
*/
		var color = this.options.boxColor;
		
		[[this.options.colorStyles[color].nw, 
		  this.options.colorStyles[color].n, 
		  this.options.colorStyles[color].ne],
		 [this.options.colorStyles[color].sw, 
			this.options.colorStyles[color].s, 
			this.options.colorStyles[color].se]].each(function(tds) {
			this.insertPopTblRow(tds);
		}.bind(this));


		// assign td class "north" as contents block of pop
		this.popc = this.poptbl.getElement('td[class=' + this.options.colorStyles[color].n + ']');

		if (this.options.useArrows)	{
			this.addPopArrows();
		}
		
		// close button
		if (this.options.addCloseBtn) {
			this.close = new Element('div',{
				'class':this.options.classPrefix+'Close'
			}).injectInside(this.pop);
			var close_button = new Element('button', {
				'name':'closeButton',
				'class':'sButton',
				'styles':{
					'position': 'relative',
					'display': 'inline',
					'float': 'none',
					'border-width': '2px'
				},
				'events':{
					'click':this.hide.bindWithEvent(this)
				}
			}).injectInside(this.close);
			close_button.set('html', this.options.closeButtonText);
			close_button.addEvent('click', function(e){ 
				if(e) e = new Event(e).stop();
				this.hide();
			}.bind(this));			
		}
	},
	insertPopTblRow: function(tds){
		var tr = new Element('tr').injectInside(this.poptbody);
		tds.each(function(cls) {
			var td = new Element('td',{ 'class':cls }).injectInside(tr);
		});
	},
	set_contents: function(msg, cls) {
		var color = this.options.boxColor;

		if (($type(cls)=='undefined') || (cls=='')) {
			cls = 'n';
		}
		if (this.popc) {
			this.popc.className = this.options.colorStyles[color].n + ' ' + cls;
		}
		if (this.popc) {
			this.popc.empty();

			switch($type(msg)) {
				case 'element':
					var msg_cl = msg.clone().cloneEvents(msg).inject(this.popc);
					break;
				case 'string':
					this.popc.set('html', msg);
					break;
			}
		}

		// determine the width/height of the pop after adding new content to pop
		var was_dn = false;
		if (this.pop.getStyle('display') == 'none') {
			was_dn = true;
			this.pop.setStyle('display', 'block');
		}
		this.popsize = this.pop.getSize();
		if (was_dn) {
			this.pop.setStyle('display', 'none');
		}
	},
	show: function() {
		if(!this.isShowing) {

			// set the starting position of the pop
			var start = {
				'visibility':'visible',
				'display': 'block',
				'opacity': 0
			};

			// both fade and fly trans fades in
			var fx = {
				'0': { 
					'opacity': this.options.popOpacity
				}
			};

			var se = this.options.place.se;
			var ss = this.options.place.ss;

			var end_xy = this.coord(se.target, se.io, se.align, se.offset, true);

			// just fade into the end coords
			start.top = end_xy.top;
			start.left = end_xy.left;
		
			this.pop.setStyles(start);

			// show fx for pop
			if (this.options.isModal) {
				this.add_mask(); // only adds one if it doesn't exist
				this.mask.setStyles({
					'height': window.getScrollHeight(),
					'width': window.getScrollWidth(),
					'display': 'block'
				});
				// fx for mask
				fx['1'] = { 'opacity': this.options.maskOpacity };
			} else {
				if (!this.options.isModal && this.isIE) {
					this.mask.setStyles({
						'height': this.popsize.y,
						'width': this.popsize.x,
						'display': 'block',
						'visibility':'visible',
						'top': end_xy.top,
						'left':end_xy.left
					});
				}
			}
			this.fx_dir = 1;
			this.fx.start(fx);
			
			// jsut thought this would be nice if the trigger was
			// a hyperlink so it won't follow the href path
			return false;
		}
	},
	hide: function(e) {
		if(!this.isShowing) {
			return false;
		}

		this.fx_dir = 0;
		// fx for pop
		var fx = {
			'0': { 
				'opacity': 0
			}
		};
		var he = this.options.place.he;

		// fx for mask
		fx['1'] = { 'opacity': 0 };	

		if(this.options.submitjs) {
			if($chk($('modal'))) {
				$('modal').value = '1';
			}
			eval($('submitJS').value);
			this.fx.start(fx);
		} else {
			this.fx.start(fx);
		}
	},
	update: function(e) {
		if (this.isShowing) {
			if (this.options.isModal) {
				// resize the mask to the new size of the window
				var size = window.getSize();
				var scroll = this.pop.getScroll();
				
				this.mask.setStyles({
					'height': '10000',
					'width': size.x
				});
			}
			
			var se = this.options.place.se;
			
			if ((se.target == 'window') && (se.io==-1)) {
				var coord = this.coord('window', -1, se.align, se.offset, false);

				// move pop to the center of visible screen
				this.fx.start({
					'0': { 
						'top': coord.top,
						'left': coord.left,
						'margin': se.margin
					}
				});
			}	
		}
	},
	coord: function(target, io, align, offset, arr_mode) {

		var top = 0;
		var left = 0;
		var tdim = 0;

		if (target == 'window') {
			top = window.getScrollTop();
			left = window.getScrollLeft();
			tdim = { 'x':window.getWidth(), 'y':window.getHeight() };
		} else {
			if ($type(target)=='string') {
				var t = $(target);
			} else {
				var t = target;
			}
			if (t) {
				if (t.getStyle('display')=='inline') {
					var tpos = this.cursor_pos(this.event);
					top = tpos.y;
					left = tpos.x;
					tdim = { 'x':1, 'y':1 };
				} else {
					var tpos = t.getPosition();
					if (tpos) {
						top = tpos.y;
						left = tpos.x;
						tdim = { 'x':t.offsetWidth, 'y':t.offsetHeight };
					}
				}
				if (!$defined(align)) {
					align = this.auto_align(t,'auto');
				}
			}
		}

		if (tdim) {
			if ((arr_mode===true) && this.options.useArrows) {
				var pa = this.show_arrow(io, align);
			}

			var nesw = align.substr(0,1);
			switch (io) {
				case 1:
					// outside 
					
					switch (nesw) {
						case 'n':
							top -= (this.popsize.y + offset);
							break;
						case 'e':
							left += (tdim.x + offset);
							break;
						case 's':
							top += (tdim.y + offset);
							break;
						case 'w':
							left -= (this.popsize.x + offset);
							break;					
					}

					// move pop if the size of pop is bigger than the target
					switch (align) {
						case 'nw':
						case 'sw':
							if ((tdim.x < this.popsize.x) && pa) {
								left -= pa.a.x/2;
							}
							break;
						case 'ne':
						case 'se':
							if ((tdim.x < this.popsize.x) && pa) {
								left += pa.a.x/2;
							}
							break;
					}

					switch (align) {
						case 'n': // above target, centered
							left += (tdim.x/2 - this.popsize.x/2);
							break;
						case 'ne': // above target, right aligned
							left += (tdim.x - this.popsize.x);
							break;
						case 'w': // left of target, middle aligned
							top += (tdim.y/2 - this.popsize.y/2);
							break;
						case 'ws': // left of target, bottom aligned
							top += (tdim.y - this.popsize.y);
							break;
						case 'e': // right of target, middle aligned
							top += (tdim.y/2 - this.popsize.y/2);
							break;
						case 'es': // right of target, bottom aligned
							top += (tdim.y - this.popsize.y);
							break;
						case 's': // below target, middle aligned
							left += (tdim.x/2 - this.popsize.x/2);
							break;				
						case 'se': // below target, right aligned
							left += (tdim.x - this.popsize.x);
							break;				
					}
					break;
				case -1:

					// inside 
					switch (nesw) {
						case 'n':
							top += offset;
							break;
						case 's':
							top += (tdim.y - this.popsize.y - offset);
							break;
					}

					switch (align) {
						case 'nw':
							left += offset;
							break;
						case 'n':
							left += (tdim.x/2 - this.popsize.x/2);
							break;
						case 'ne':
							left += (tdim.x - this.popsize.x - offset);
							break;
						case 'w':
							top += (tdim.y/2 - this.popsize.y/2);
							left += offset;
							break;
						case 'c':
							top += (tdim.y/2 - this.popsize.y/2);
							left += (tdim.x/2 - this.popsize.x/2);
							break;
						case 'e':
							top += (tdim.y/2 - this.popsize.y/2);
							left += (tdim.x - this.popsize.x - offset);
							break;
						case 'sw':
							left += offset;
							break;				
						case 's':
							left += (tdim.x/2 - this.popsize.x/2);
							break;				
						case 'se':
							left += (tdim.x - this.popsize.x - offset);
							break;				
					}

					break;
			}

			if (pa) {
				top += pa.p.top;
				left += pa.p.left;
			}

			return { 'top': top, 'left': left };
		}

		return false
	},
	destroy: function() {
		if (this.mask) {
			this.mask.remove();
		}
		this.pop.remove();
	}
});

//
AscDialog.implement(new Options, new Events);

var AscModal = new Class({
	Extends: AscDialog,
 	options: {
		closeButtonText: 'Save Assignment',
		isModal: true,
		addCloseBtn: true,
		popOpacity: 1.0,
		classPrefix: 'Modal',
		submitjs: true,
		place: {
			'ss': { target:'window', io:1, align:'n'}, // show start
			'se': { trans:'fly', target:'window', io:-1, align:'c'}, // show end
			'he': { trans:'fly', target:'window', io:1, align:'n'} // hide end
		}
	},
	initialize: function(msg, cls, bMsg, options){
		this.options.closeButtonText = bMsg;
        this.parent(options);
		this.set_contents(msg, cls);
    }
});

var memoModal = new Class({
	Extends: AscDialog,
 	options: {
		closeButtonText: 'Save Assignment',
		isModal: true,
		addCloseBtn: true,
		popOpacity: 1.0,
		classPrefix: 'Modal',
		submitjs: true,
		boxColor: 'blue',
		alignChoice: 'left',
		place: {
			'ss': { target:'window', io:1, align:'n'}, // show start
			'se': { trans:'fly', target:'window', io:-1, align:'c'}, // show end
			'he': { trans:'fly', target:'window', io:1, align:'n'} // hide end
		}
	},
	initialize: function(msg, cls, bMsg, options){
		if(bMsg) {
			this.options.closeButtonText = bMsg;
		} else {
			this.options.addCloseBtn = false;
		}

        this.parent(options);
		this.set_contents(msg, cls);
    }
});

var RequirementModal = new Class({
    Extends: memoModal,
    options: {
        closeButtonText: 'Cancel',
        containerClass:'RequirementModal',
        submitjs: false,
        boxColor: 'red'
    }
});

var questionLinksModal = new Class({
    Extends: memoModal,
    options: {
		addCloseBtn: false,
        containerClass:'questionLinksModal',
        submitjs: false,
        boxColor: 'red'
    }
}
);

var ebooksModal = new Class({
    Extends: memoModal,
    options: {
        submitjs: false,
        boxColor: 'blue',
        containerClass:'EbooksModal'
    },
    initialize: function(msg, cls, bMsg, bMsg2, options){
        if(bMsg2) {
            this.options.closeButtonText2 = bMsg2; this.options.addCloseBtn2 = true; this.options.boxColor = 'red';
        }     
        this.parent(msg, cls, bMsg, options);
    },
    hide: function(e){
        clearTimeout(timer_long_handle);
        start_timer_short();
        start_timer_long();
        this.parent(e);
    },
    add_pop : function() {
        this.parent();
        if (this.options.addCloseBtn2) {
            var close_button2 = new Element('button', {
                'name':'closeButton2',
                'class':'sButton',
                'styles':{
                    'position': 'relative',
                    'display': 'inline',
                    'float': 'none',
                    'border-width': '2px'
                },
                'events':{
                    'click':this.hide.bindWithEvent(this)
                }
            }).inject(this.close,'top');
            close_button2.set('html', this.options.closeButtonText2);
            close_button2.addEvent('click', function(e){
                if(e) e = new Event(e).stop();
                this.hide();
                close_ebooks();
            }.bind(this));
        }
   }
});

var feedbackModal = new Class({
    Extends: memoModal,
    options: {
        addCloseBtn: false,
        closeButtonText: 'Cancel',
        containerClass:'feedbackModal',
        submitjs: false,
        boxColor: 'blue',
        asset_id: 0
    },
    /* add initialize function that takes the id of the item to be rated for the submit button */
    initialize: function( msg, cls, bMsg, asset_id ) {
        this.options.asset_id = asset_id;
        this.parent( msg, cls, bMsg, this.options );

        //instead of injecting inside this.pop, get inside div.bnorth
        this.close = new Element('div',{
            'class':this.options.classPrefix+'Close'
        }).injectInside( $('feedbackModalContent') ); 

        var close_button = new Element('button', {
            'name':'closeButton',
            'class':'sButton',
            'styles':{
                'position': 'relative',
                'display': 'inline',
                'float': 'none',
                'border-width': '2px',
                'width': '90px'
            },
            'events':{
                'click':function() {
                    reload_teachme();
                    this.hide.bindWithEvent(this);
                }
            }
        }).injectInside(this.close);
        close_button.set('html', this.options.closeButtonText);
        close_button.addEvent('click', function() {
            //if checkbox is checked, send off request to set prefs
            if ( $('dont_show_box_Y').checked ) { 
                set_no_modal_prefs();
            }
        });
        close_button.addEvent('click', function(e){ 
            if(e) e = new Event(e).stop();
            this.hide();
        }.bind(this));			

        var submit_button = new Element('button', {
            'name':'submitFeedback',
            'class':'sButton',
            'styles':{
                'position': 'relative',
                'display': 'inline',
                'float': 'none',
                'border-width': '2px',
                'width':'90px',
                'margin-left': '15px'
             },
             'events':{
                 'click':this.hide.bindWithEvent(this)
             }
        }).inject(this.close,'bottom');
        submit_button.set('html', 'Send');
        submit_button.addEvent('click', function() {
            submit_asset_feedback( this.options.asset_id );
        }.bind(this));
        submit_button.addEvent('click', function(e){
            if(e) e = new Event(e).stop();
            this.hide();
        }.bind(this));
    },
    add_pop : function() {
        this.parent();
        //table cells are added here...don't add second row?

        classes = this.options.containerClass 
                ? this.options.containerClass+' '+this.options.classPrefix+'Pop'
                : this.options.classPrefix+'Pop'

		this.pop = new Element('div', {
			'class':classes,
			'styles':{
				'position': 'absolute',
				'visibility': 'hidden',
				'top': -1000,
				'left': 0,
				'z-index': 10000,
				'display': 'none'
			}
		}).inject(document.body);

		this.pop.addEvent('keydown', function(e){ 
			this.esc(e);
		}.bind(this));


		// add table for pop with border graphics
		this.poptbl = new Element('table',{ 'class':'grid ' + this.options.msgAlign[this.options.alignChoice] }).injectInside(this.pop);
		this.poptbody = new Element('tbody').injectInside(this.poptbl);

/*
		boxColor: 'red',
		colorStyles : {
			'red' : { nw: 'nw', n: 'north', ne: 'ne', sw: 'sw', s: 's', se: 'se' },
			'blue' : { nw: 'bnw', n: 'bnorth', ne: 'bne', sw: 'bsw', s: 'bs', se: 'bse' }
		},
*/
		var color = this.options.boxColor;
		
		[[this.options.colorStyles[color].nw, 
		  this.options.colorStyles[color].n, 
		  this.options.colorStyles[color].ne]].each(function(tds) {
			this.insertPopTblRow(tds);
		}.bind(this));


		// assign td class "north" as contents block of pop
		this.popc = this.poptbl.getElement('td[class=' + this.options.colorStyles[color].n + ']');

		if (this.options.useArrows)	{
			this.addPopArrows();
		}
		
	}
});


var restrictionsModal = new Class({
    Extends: memoModal,
    options: {
        closeButtonText: 'OK',
        containerClass:'restrictionsModal',
        submitjs: false,
        boxColor: 'grey'
    },
    initialize: function(msg, cls, bMsg, bMsg2, bAction, passwordFieldID, options) {
    	if( bMsg ) {
    		this.options.bAction = bAction;
    	}
    	
        if( bMsg2 ) {
            this.options.closeButtonText2 = bMsg2; 
            this.options.addCloseBtn2 = true;
        }
        if( passwordFieldID ) {
        	this.options.passwordFieldID = passwordFieldID;
        }
        this.parent(msg, cls, bMsg, options);
    },
    show: function() {
    	// reset password fields
        var pass = $(this.options.passwordFieldID);
        if( pass ) {
        	pass.value = '';
        	
       		pass.setStyles({
	       		'border-color':'#7f9db9',
	       		'background':'#fff'
	       	});
	       	
	       	pass.getPrevious().getFirst().setStyles({
	       		'display':'none',
	       		'color':'#d00'
	       	});
	       	
	       	// kill error message
	  		var warningMessage = pass.getParent('div.form').getPrevious('span.warning');
	  		if( warningMessage ) {
		    	warningMessage.destroy();
        	}
        }
        
        // download links
        var downloadLDBLinks = $$('a.downloadLink');
                
        if( downloadLDBLinks ) {
        	var uPass = $('UserPass').value;
        	
        	downloadLDBLinks.addEvent('click',function() {
        		window.open("/v4cgi/ldb/download.tpl?UserPass=" + uPass, 'Download_LDB', 'location=no,menubar=no,toolbar=no,scrollbars=no,resizable=yes,status=no,titlebar=no,width=483,height=400');   		
        		return false;
        	});
        }
        
        
        // load parent method
        this.parent();
 
    },
    add_pop: function() {
        classes = this.options.containerClass 
            ? this.options.containerClass+' '+this.options.classPrefix+'Pop'
            : this.options.classPrefix+'Pop'

		this.pop = new Element('div', {
			'class':classes,
			'styles':{
				'position': 'absolute',
				'visibility': 'hidden',
				'top': -1000,
				'left': 0,
				'z-index': 10000,
				'display': 'none'
			}
		}).inject(document.body);


		// add table for pop with border graphics
		this.poptbl = new Element('table',{ 'class':'grid ' + this.options.msgAlign[this.options.alignChoice] }).injectInside(this.pop);
		this.poptbody = new Element('tbody').injectInside(this.poptbl);

		var color = this.options.boxColor;
		
		[[this.options.colorStyles[color].nw, 
		  this.options.colorStyles[color].n, 
		  this.options.colorStyles[color].ne],
		 [this.options.colorStyles[color].sw, 
			this.options.colorStyles[color].s, 
			this.options.colorStyles[color].se]].each(function(tds) {
			this.insertPopTblRow(tds);
		}.bind(this));


		// assign td class "north" as contents block of pop
		this.popc = this.poptbl.getElement('td[class=' + this.options.colorStyles[color].n + ']');

		if (this.options.useArrows)	{
			this.addPopArrows();
		}
		

		// 1st button
		if (this.options.addCloseBtn) {
			
			this.close = new Element('div',{
				'class':this.options.classPrefix+'Close'
			}).injectInside(this.pop);
			
			var close_button = new Element('button', {
				'name':'closeButton',
				'class':'sButton',
				'styles':{
					'position': 'relative',
					'display': 'inline',
					'float': 'none',
					'border-width': '2px',
                    'width': '70px'
				}
			}).injectInside(this.close);
			
			close_button.set('html', this.options.closeButtonText);
			close_button.addEvent('click', function(){
				this.options.bAction();
			}.bind(this));			
		}

    
 		// 2nd button
        if (this.options.addCloseBtn2) {
            var close_button2 = new Element('button', {
                'name':'closeButton2',
                'class':'sButton',
                'styles':{
                    'position': 'relative',
                    'display': 'inline',
                    'float': 'none',
                    'border-width': '2px',
                    'width': '60px'
                },
                'events':{
                    'click':this.hide.bindWithEvent(this)
                }
            }).inject(this.close,'top');
            close_button2.set('html', this.options.closeButtonText2);
            close_button2.addEvent('click', function(){
                this.hide();
            }.bind(this));
        }
   }
});

var ebook_timer = "restart";
var winArray = [];
var timer_short_handle;
var timer_long_handle;
var modal_short;
var modal_long;

var short_min = 600; //600 mintues (10 hours) change made for TAMU previously 25 minutes in production;  enter number of minutes for short timer to run
var long_min = 605;  //605 mintues (10 hours 5 min) change made for TAMU previously 30 minutes in production;  enter number of minutes for long timer to run

function start_child_touch() {
    setInterval("child_touch()",3000);
}

function child_touch() {
    winArray.each(function(win) {
        var winFlag = 1;
        try { if ( !$defined(win.location.hostname) ) { winFlag = 0; } } catch(err) { winFlag = 0; }
        var patt1=new RegExp("AAA");
        if ( !win.closed && winFlag && !patt1.test(win.name) ) { var win_string = "AAA" + win.name; win.name = win_string; }
    });
}

function close_ebooks() {
    if ( timer_short_handle != undefined ) { clearTimeout(timer_short_handle); }
    if ( timer_long_handle != undefined ) { clearTimeout(timer_long_handle); }
    modal_short.hide();
    winArray.each(function(win) {
        var winFlag = 1;
        try { if ( !$defined(win.location.hostname) ) { winFlag = 0; } } catch(err) { winFlag = 0; }
        if (!win.closed && winFlag ) { win.close(); }
    });    
}

function start_timer_short() {
    if ( timer_short_handle != undefined ) { clearTimeout(timer_short_handle); }
    timer_short_handle=setTimeout("timer_short()",short_min*60*1000);
    ebook_timer = "running"
}

function start_timer_long() {
    if ( timer_long_handle != undefined ) { clearTimeout(timer_long_handle); }
    timer_long_handle=setTimeout("timer_long()",long_min*60*1000);
    ebook_timer = "running"
}

function verify_cred(id) {
    var security = '';
    var upass = $('UserPass').value;
    var url = '/wx/ebooks/security';
    var ebook_security = new Request({
        url:url,
        async:false,
        onSuccess: function(creds) {
            security = creds;
        },
        onRequest: function() {
            //alert('Doing request...');
        }
    });

    ebook_security.send('id=' + id + '&UserPass=' + upass);
    alert("Security:"+security);
    return security;
} 

function openAccountEmailModal(alias) {

    var message   =  "<div class='modalMessage'>Enter your email address below.";
    var form      =  "<div class='form'>"
                  +     "<div class='textField container'>"
                  +         "<label for='textfield'>Email Address:</label>"
                  +         "<input type='textfield' id='user_email' name='user_email' value=''>"
                  +     "</div>"
                  +  "</div>"
                  +  "<hr>";
    message += form;
    var action  = function() {
            var url       = '/' + alias + '/extra/vitalsource/store_email?email=';
            var email     = $('user_email').value;
            email         = encodeURIComponent(email);
            url          += email;
            var saveEmail = new Request.HTML({
                url:url,
                method:'get',
                onSuccess: function(responseTree, responseElements) {
                    location.reload(false);
                }
            }).send();

        };
    modal = new userEmailModal(message, 'n', 'Cancel', 'OK', action, 'user_email', 'modal');
    modal.show();
}

function openVitalSourceModal(alias,uid,publisher_id,win_name,ebook,page) {
    var publisher_id = publisher_id;
    var message =  "<div class='modalMessage'>Enter your VitalSource password below."
                +  "Forgot your password?"
                +  "<a href='http://store.vitalsource.com/forgot'>Visit VitalSource to retrieve it.</a></div>";
    var form    =  "<div class='form'>"
                +     "<div class='textField container'>"
                +         "<label for='textfield'>VitalSource Password:</label>"
                +         "<input type='password' id='vitalsource_password' name='vitalsource_password' value=''>"
                +     "</div>"
                +  "</div>"
                +  "<hr>";
    message += form;
    var action  = function(passwd, publisher_id) {
            var url     = '/' + alias + '/extra/vitalsource/retrieve_guid?password=';
            var passwd  = encodeURIComponent(passwd);
            url        += passwd + '&publisher_id=' + publisher_id;
            var getGuid = new Request.HTML({
                url:url,
                method:'get',
                onSuccess: function(tree, el, html, js) {
                        var pattern = /Error:/;
                        if ( html.match(pattern) ) {
                            var message = html;
                            message    += form;
                            modal.set_contents(message);
                            modal.show();
                        }
                        else {
                            modal.hide();
                            openVitalSourceEbook(alias,uid,publisher_id,win_name,ebook,page); 
                        }
                }
            }).send();

        };
    modal = new vitalsourceModal(message, 'n', 'Cancel', 'OK', action, 'vitalsource_password', 'modal', publisher_id);  
    modal.show();
}

function openVitalSourceEbook(wa_alias,uid,publisher_id,win_name,ebook,page) {
    var ebookwin = new Array();
    var guid;
    if (uid) {
        var query_string = 'publisher_id=' + publisher_id;
        var check_user = new Request.JSON({
            async   : false,
            url     : '/' + wa_alias + '/extra/vitalsource/check_user',
            onSuccess: function(response) {
                if (response.guid) {
                    guid = response.guid;
                }
                else if (response.modal_email == 1) {
                    openAccountEmailModal(wa_alias);
                }
                else if (response.modal_password == 1) {
                    openVitalSourceModal(wa_alias, uid, publisher_id, win_name, ebook, page);
                }
            }
        }).send(query_string);
    }
    if (guid !== undefined) {
        var win_options = 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbar=no,resizable,copyhistory=yes,width=961,height=720,left=25,top=25,screenX=25,screenY=25';
        // Unique ebook window object to allow multiple types of ebooks to open only one of each code(ISBN/VBID etc.)
        if (ebookwin[ebook]) {
            ebookwin[ebook].focus();
        }
        else {
            ebookwin[ebook] = window.open('','_blank',win_options);
            ebookwin[ebook].document.write("<div class='loading' id='message'><img src='/wastatic/common/img/ajaxLoading.gif' alt='Loading Ebook' title='Loading Ebook'>  Loading Ebook: " + win_name + "</div>");
            var win_url = '/' + wa_alias + '/extra/vitalsource/index.tpl';
            var params  = 'guid=' + guid +'&publisher_id=' + publisher_id + '&id=' + ebook + '&name=' + win_name;
            if (page) {
                params += '&page=' + page;
            }
            var req     = new Request.HTML({
                method: 'get',
                url: win_url,
                onSuccess: function(tree, el, html, js) {
                    html = html.replace(/\s+$/g, '');
                    ebookwin[ebook].location.href = html;

                },
                onFailure: function(xhr) {
                    ebookwin[ebook].document.getElementById('message').innerHTML = "Problem loading Ebook.  Please try again at a later time.";
                    ebookwin[ebook].focus();
                }
            }).send(params);
        }
    }
}

function openEbook(server_name,wa_alias,ebookurl,ebookid,secflag) {

    var win_url = ebookurl;
    var win_options = '';
    //var sec = verify_cred(ebookid);
    //alert("Security2:"+sec);
    //var patt1 = new RegExp("on"); patt1.test(sec)
    //var patt2 = new RegExp("invalid");

    if ( secflag === "on" ) {
        var upass = $('UserPass').value;
        win_url = 'http://' + server_name + '/' + wa_alias + '/extra/ebookwrapper/index.tpl?';
        win_url += 'ebook_url=' + ebookurl;
        win_url += '&UserPass=' + upass;
        //start_timer_short();
        //start_timer_long();
        //start_child_touch();
        win_options = 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbar=no,resizable,copyhistory=yes,width=960,height=720,left=25,top=25,screenX=25,screenY=25';
    }
    
    //if ( secflag === "invalid" ) {
    //    win_url = '/' + wa_alias + '/ebooks/forbidden.html';
    //}

    var win_ebook = window.open(win_url, ebookid, win_options);
    win_ebook.focus();
    return false;
}

function timer_short() {
    var ebook_ids = '';
    winArray.each(function(win) {
        var winFlag = 1;
        try { if ( !$defined(win.location.hostname) ) { winFlag = 0; } } catch(err) { winFlag = 0; }
        if (!win.closed && winFlag ) { ebook_ids = ebook_ids + ',' + win.name }
    });
    if ( ebook_ids.length === 0 ) { clearTimeout(timer_long_handle); return; }
    //alert('Timer short getting started from PARENT...'); 
    var url = '/wx/ebooks/html';
    var userPass = $('UserPass').value;
    var ebook_html = new Request({
        url:url,
        onSuccess: function(html) {
            modal_short = new ebooksModal(html,'n','I am still here!','Close eBooks');
            modal_short.show();
        },
        onRequest: function() {
            //alert('Doing request...');
        }
    });
    ebook_html.send('ebook_string=' + ebook_ids + '&UserPass=' + userPass + '&type=text');
}

function timer_long() {
    var ebook_ids = '';
    winArray.each(function(win) {
        var winFlag = 1;
        try { if ( !$defined(win.location.hostname) ) { winFlag = 0; } } catch(err) { winFlag = 0; }
        if (!win.closed && winFlag ) { ebook_ids = ebook_ids + ',' + win.name }
    });
    if ( ebook_ids.length === 0 ) { return; }
    var url = '/wx/ebooks/html';
    var userPass = $('UserPass').value;
    var ebook_html = new Request({
        url:url,
        onSuccess: function(html) {
            modal_short.hide();
            modal_long = new ebooksModal(html,'n','Close this Message');
            modal_long.show();
            close_ebooks();
        },
        onRequest: function() {
            //alert('Doing request...');
        }
    });
    ebook_html.send('ebook_string=' + ebook_ids + '&UserPass=' + userPass + '&type=links');
}


/*-----------------------------------------------------------------------------
WebAssign.Print Class

Extends WebAssign

version:	1.1
author:		iq
libs:		mootools 1.2.4

description: a modular print script that looks for a printAssignment ID in the
			 page. Runs prepare scripts which cleanup inline styling from
			 dynamic content, etc. Then cleans up after print has completed.

notes: Only tested in FF 3.6.3, Safari 4.0.5, IE7/8 builds
-----------------------------------------------------------------------------*/

WebAssign = new Class({});

WebAssign.Print = new Class({
    Extends: WebAssign,
    initialize: function(element, options) {
        //console.log('WebAssign.Print: initialize()');				
    	this.imageSizeMax = 500;
    	this.deBug = false;
    },
    onDomReady : function(e) {
        //console.log('WebAssign.Print: onDomReady()');
        if( this._got_dom_ready ) return;
        this._got_dom_ready = true;
        
        this.click();
    },
    click: function() { 
      	//console.log('WebAssign.Print: click()');
    	if( this._got_dom_ready ) {
    		this.printLink = $('printAssignment');
    		
    		// if no link in the page dont run script
    		if( !this.printLink ) {
    			return;
    		}
    		    		
    	} else {
    		return;
    	}
    
    	this.printLink.addEvent('click', function(e) {
    		e.stop();
    		this.formatContent();
    		
    		window.print();   		
    		this.complete();
            this.logPrint();
            
            // debug check
            if( this.deBug ) {
                console.log('Print process completed!');
            }
    	}.bind(this));
    },
    
    complete: function() {
    	// destroy cloned print versions and remove display 
    	// none from originals 
    	$$('.printedFormat').destroy();
    	
    	// debug check
    	if( this.deBug ) {
    	   if( this.deBug && ($$('.printedFormat').length > 0) ) {
    	   		     console.log('yes there are still print versions in the page!');       			     
           } else {
               console.log('print versions were destroyed!');
           }
    	}
    	
    	// NOTE: erase and removeProperty don't seem to work 
    	// in webkit, etc
    	$$('.accordion, .waExpand').setProperty('style', '');
    	
    	// NOTE: IE isn't obeying the last statement, so since these 'are'
    	//       block level this is alright.
    	$$('.accordion, .waExpand').setStyle('display', 'block');
    	    	
    	// remove classes that reduce large images
    	$$('.qContent img').removeClass('bigImg');
    },
    
    formatContent: function() {
    	
    	var images = $$('.qContent img');
    	
    	// run through images and check for width
    	// add class that reduced size for printing
    	if( images ) {
    		images.each(function(image) {
    			var imageSize = image.getSize();
    			
    			if(imageSize.x > this.imageSizeMax) {
    				image.addClass('bigImg');
    			}
    			
    		}.bind(this));
    	}
    	
        // the expand script hides items to 
    	// which we must reveal as well for print
    	// FIXME: accordion is the prelude to the
    	//        expand script, this should be 
    	//        converted to use expand.js
    	var accordions = $$('.accordion, .waExpand');
    	
    	// accordions use inline height styling
    	// this causes issues when collapsed
    	if( accordions ) {

    		accordions.each(function(accordion) {
    		
    			var clonedAccordion = accordion.clone();
                
                // we only want the first level of unordered list structure
    			var clonedLIs = clonedAccordion.getElements('.waExpand > li, .accordion > li');
    			
    			clonedLIs.each(function(li) {
    				
    				// removing inline styling
    				li.getFirst().getNext().removeProperty('style');
    				
    				// removing inline styling 
    				li.getFirst().getNext().getFirst().removeProperty('style');
    			
					// check for a smw closed expandy
					if( accordion.hasClass('waExpand') && accordion.getParent('div.smw') ) {

						// using accordion reference to find relative question data
						var smwContainer = accordion.getParent('div.smw');
						var qid = smwContainer.getElement('input.qid').value;
						var pos = smwContainer.getElement('input.smwPos').value;
						var smwStorageId = '#smw_' + pos + '_' + qid + '_work';

						var dijitValue = smwContainer.getElement(smwStorageId).value;
						
						// create same div with just data inside
						var printSmwDiv = new Element('div', {
							'class': 'smwTextArea',
							'html': dijitValue
						});
						
						// replace smwTextArea with iFrame etc with just data for print
						printSmwDiv.replaces(li.getElement('.smwTextArea'));
					}
    			});
    		
				// tag clone as print data to be disposed of later	
    			clonedAccordion.addClass('printedFormat'); 
    			
    			// injecting print safe version
    			clonedAccordion.inject(accordion, 'after');	
    			accordion.setStyle('display', 'none');
    			
    			if( this.deBug && ($$('.printedFormat').length > 0) ) {
    			     console.log('yes printed version was created!');
    			}
    		});
    	}
    },
    
    setDebug: function(state) {
        this.deBug = state;
    },
    
    logPrint: function() {
        // get user pass from hidden field
        var up = $('UserPass');
        var up_val = '';
        
        if(up) {
            // get password val
            up_val = up.value;
            
            // make ajax call to log print action
            var req = new Request({
                method: 'post',
                url: '/wx/print/log_print',
                data:{log:log, UserPass:up_val},
                onComplete: function(resp){}
            }).send();
        }
    }    
});

WebAssign.Print.getInstance = function() {
    if( WebAssign.Print.__instance__ == null ) {
        WebAssign.Print.__instance__ = new WebAssign.Print();
    }
    return WebAssign.Print.__instance__;
};

(function() {
    var instance = WebAssign.Print.getInstance();
    window.addEvent('load', instance.onDomReady.bind(instance));  
})();

function setViewState( courseVal, qIndex, otherStruct, state ) {
	if( $('ypos') ) {
		$('ypos').value = setYpos();
	}
	
	if( $chk( $(qIndex) ) ) {
		$(qIndex).value = state;
	}
	
	if( $chk( $('action') ) ) {
		$('action').value = '';
	}
	
	if( $chk( $('course') ) ) {
		$('course').value = courseVal;
	}
	
	if( $chk( $('sid') ) ) {
		$('sid').value = '';
	}
	
	if( $chk( $('struct') ) ) {
		$('struct').value = otherStruct;
	}
	
	if( $chk( $('display_level') ) ) {
		$('display_level').value = 'question';
	}
	
	ta.focusQuestion
	
    // Need to set hidden input on  the page to retrieve the last response for SMW
    var index = qIndex.split('_');
    var smw   = $$('div[id^=smw' + index[1] + '_]');

    if ( smw.length ) {
        var qid   = smw[0].getElement('.qid').get('value');
        var input = new Element('input',{
            'type' : 'hidden',
            'name' : 'lastResponse',
            'value': qid
        });
        input.inject(smw[0]);
    }

    document.forms[0].submit(this);	
}



/*-----------------------------------------------------------------------------
ta Class

version:	1.1
author:		iq, bw
libs:		mootools-core 1.2.4, mootools-more 1.2.2.2
dependency: dojo 1.5 (in order to grab editor value, but shouldn't be a problem 
                      if editors not present)

description: The ta (teaching assistant) class is a script that runs on domready 
			 and watches for unsaved data and tries to help the student 
			 understand whent they may lose their work if they don't save or 
			 submit their answers.

notes: 		 This script will have to be considered everytime a new tool is 
			 created for answer entry or until new solution is implemented.
-----------------------------------------------------------------------------*/
ta = new Class({
    Implements: [Options, Events],
    options: {
        question: '',
        deBug: false 
    },
    initialize: function(options){
        this.setOptions(options);
        this.boxs = $$('.waQBox');
        this.boxIndex = {};                 
        this.container = $(document.body);
        this.buttons = $$('.qButtons');
        this.currentQID;
        this.previousQID;
        this.unsavedQids = [];
        this.unsavedQnums = [];
        this.numSort = function(a, b) { return (a-b) }
        this.oneQuestionAtTime = $('oneQuestionAtTime') ? true : false;
        this.submitEntire = $('submitEntire') ? true : false;
        this.IE = ( Browser.Engine.trident ) ? true : false;
        
        this.graphPadXML = ['<os>', '<fs>'];
        this.emptyCalcPadXML = '';
        this.emptyMathPadXML = '';
        this.emptyPencilPadXML = '<?xml version="1.0"?><cvs v="1.0"></cvs></xml>';
        this.beforeUnloadMsg = null;
        this.deBug = ( this.options.deBug ) ? this.options.deBug : false;
        

		// cleanup javascript calls on hrefs in the page
		// NOTE: these prevent multiple unload events from
		// 		 firing in IE
		this.inlineJsCleanUp( this.leaveWithOutSaving.bind(this) );

		
		/*
			Script should run differently for each assignment submition options
		*/
		
		// if oneQuestionAtTime
        if( this.oneQuestionAtTime ) {
        	// run One Question At A Time Init
        	// 1 question at a time (check unsaved for browser alert)
			this.oneQuestionInit();
        
            // return;
        }


        // if submit entire assignment 
        if( this.submitEntire ) {
        	    
        	// change styles for PAV wrapper once
        	$$('span.pavButtons').setStyles({
        	    'margin':'0',
        	    'padding':'0',
        	    'border':'none'
        	});

            // hide buttons if serving all questions at once
            this.buttons.setStyle('visibility', 'hidden');
            
            this.entireSubmitInit();	
			//return;
        } 
        
        
        // if not either submit entire assignment nor one question at a time mode
        if( !this.oneQuestionAtTime && !this.submitEntire ) {
        	
        	// init
	    	this.init();
        }
    },
    entireSubmitInit: function() {
    	if( this.deBug ) {
        	console.log('entireSubmitInit()');
        }
    	
    	// get our initial data when the page loads
        this.getInitialAnswerData();
    	
    	// Use 'click' for all non-IE browsers because 'mouseup' is not captured when clicking on a flash element
        // Use 'mouseup' for all IE browsers because 'click' is not captured when clicking on a flash element
        this.container.addEvent( this.IE ? 'mouseup' : 'click', function(e) {
            
            this.focusQuestion(e);
                        
        }.bind(this) );
        
        // also activate on keyup for tabbing
        this.container.addEvent( 'keyup', function(e) {
            
            this.focusQuestion(e);
            
        }.bind(this) );
    	
    },
    oneQuestionInit: function() {
    
    	if( this.deBug ) {
        	console.log('oneQuestionInit()');
        }

    	// get our initial data when the page loads
        this.getInitialAnswerData();
    	
    	// Use 'click' for all non-IE browsers because 'mouseup' is not captured when clicking on a flash element
        // Use 'mouseup' for all IE browsers because 'click' is not captured when clicking on a flash element
        this.container.addEvent( this.IE ? 'mouseup' : 'click', function(e) {
            
            this.focusQuestion(e);
                        
        }.bind(this) );
        
        // also activate on keyup for tabbing
        this.container.addEvent( 'keyup', function(e) {
            
            this.focusQuestion(e);
            
        }.bind(this) );
        
    },
    init: function() {
    
    	if( this.deBug ) {
        	console.log('init()');
        }

        // hide buttons if serving all questions at once
        this.buttons.setStyle('visibility', 'hidden');

        // get our initial data when the page loads
        this.getInitialAnswerData();
        
        // Use 'click' for all non-IE browsers because 'mouseup' is not captured when clicking on a flash element
        // Use 'mouseup' for all IE browsers because 'click' is not captured when clicking on a flash element
        this.container.addEvent( this.IE ? 'mouseup' : 'click', function(e) {
            
            this.focusQuestion(e);

        }.bind(this) );
        
        // also activate on keyup for tabbing
        this.container.addEvent( 'keyup', function(e) {
            
            this.focusQuestion(e);
            
        }.bind(this) );
            
    },

        focusQuestion: function(e) {
        var target = $(e.target);
        var qBox = Element.getParent( target, '.waQBox' );

        // set our current question state
       	this.question = qBox;
			
       	// grab some other useful elements out of our current question
        var questionButtons;
        var pavButtons;
        var qContent;

        // if current question set
       	if( this.question ) {
       	    questionButtons = this.question.getElement('.qButtons');
       		pavButtons      = this.question.getElement('.pavButtons');
       		qContent        = this.question.getElement('.qContent');
       		this.currentQID = this.question.getProperty('id');
       	}        
       	
        // debugging, set this.debug to true in the constructor
        if( this.deBug ) {
			console.log('focusQuestion():');
			if( this.oneQuestionAtTime ) { console.log('one question at a time.'); }
			if( this.submitEntire ) { console.log('submit entire assignment.'); }
			if( !this.oneQuestionAtTime && !this.submitEntire ) { console.log('regular assignment.'); }
		}
		
		// all questions displayed
		if( !this.oneQuestionAtTime && !this.submitEntire ) {
        	// lets see if we are in a question box or not
        	if( qBox && !( Element.getParent( target, '.qButtons')  ) &&  !( Element.getParent( target, '.qMessage') ) ) {
                if( this.deBug ) { console.log('focusQuestion(): inside qBox'); }
                
        	    // set enhancement styles 
        	    $$('.qContent').setStyles({
        	        '-moz-border-radius-bottomleft': '4px',
        	        '-webkit-border-radius-bottomleft': '4px',
        	        'border-radius-bottomleft': '4px'
        	    });

				if( !$defined( this.previousQID ) ) {
					// show buttons if there are buttons to show
        	    	if( questionButtons.get('html').clean() != '' ) {
        	    	    questionButtons.setStyle('visibility', 'visible');
        	    	    
        	    	    // set enhancement styles 
                	    $$('.qContent').setStyles({
                	        '-moz-border-radius-bottomleft': '4px',
                	        '-webkit-border-radius-bottomleft': '4px',
                	        'border-radius-bottomleft': '4px'
                	    });
        	    	}
				}
			
        	    // make previous QID point to current if not initialized
        	    this.previousQID = !( $defined( this.previousQID ) ) ? this.currentQID : this.previousQID;
			
        	    // get previous question box
        	   	var prevQuestion = $(this.previousQID);
            
        	    // check to see if we have changed question focus
        	    if( ( this.currentQID != this.previousQID ) ) {
                    // FIXME: handle this with class switching not style manipulation
        	    	// hide them all 
					this.buttons.setStyle('visibility', 'hidden');
					// show buttons if there are buttons to show
        	    	if( questionButtons.get('html').clean() != '' ) {
        	    	    questionButtons.setStyle('visibility', 'visible');
        	    	}
        	    
        	    	if( this.deBug ) {
        	    		console.log('setQuestionAnswerData() is being called.');
        	    		console.log('currentQID: ' + this.currentQID + ' previousQID: ' + this.previousQID);
        	    	}
        	        // set our current Question data state for the current box
                    this.setQuestionAnswerData(prevQuestion);
        	        
        	        // check for unsaved data in our boxIndex object
        	        this.unsavedDataCheck( this.previousQID );

        	    	// set previous QID state
        	    	this.previousQID = this.currentQID;
        	    }
    	    
        	    // set enhancement styles
        	    qContent.setStyles({
        	        '-moz-border-radius-bottomleft' : '0',
        	        '-webkit-border-radius-bottomleft' : '0',
        	        'border-radius-bottomleft' : '0'
        	    });
        	    
        	} else {

        	    // trying to isolate targets that will leave the assignment
	    		this.focusNonQuestion(e);
        	    
        	    $$('.qContent').setStyles({
        	        '-moz-border-radius-bottomleft' : '0',
        	        '-webkit-border-radius-bottomleft' : '0',
        	        'border-radius-bottomleft' : '0'
        	    });
        	}
        }

        // one question at a time mode or submit whole assignment mode
        else if( this.oneQuestionAtTime || this.submitEntire ) { 
        
        	if( this.deBug ) {
				console.log('focusQuestion(): oneQuestionAtTime or submitEntireAssignment');
			}
        
        	// lets see if we are in a question box or not
        	if( qBox && !( Element.getParent( target, '.qButtons') ) && !( Element.getParent( target, '.qMessage') ) ) {

        	    // set our current question state
        	    this.question = qBox;

        	    // currentQID
        	    this.currentQID = this.question.getProperty('id');
				
        	    // get current question box
        	   	var currentQuestion = $(this.currentQID);

        	    // set our current Question data state for the current box
        	    this.setQuestionAnswerData( currentQuestion );
        	             
        	    // check for unsaved data in our boxIndex object
        	    this.unsavedDataCheck( this.currentQID );
        	    
        	    // handle PAV buttons on submit whole assignment
        	    if( this.submitEntire ) {
        	    
        	    	if( ( this.currentQID != this.previousQID ) ) {
        	    		
        	    		// if their is no html for the button, then it doesn't exist
        	    		var pavButtonsExist = ( pavButtons ) ? pavButtons.get('html').clean() : '';
        	    		
        	    		// see if their is a PAV button available
        	    		if( pavButtonsExist != '' ) {
        	    	
                            // hide them all 
							this.buttons.setStyle('visibility', 'hidden');
        	    							
        	    			// set previous QID state
        	    			this.previousQID = this.currentQID;
        	    		}
        	    		
        	    		// if their is no html for the button, then it doesn't exist
        	    		var questionButtonsExist = ( questionButtons ) ? questionButtons.get('html').clean() : '';
        	    		
        	    		// show buttons if there are buttons to show
        	    		if( questionButtonsExist != '' ) {
        	    		    questionButtons.setStyle('visibility', 'visible');
        	    		}
        	    	}
        	    }

        	} else {
        	    // trying to isolate targets that will leave the assignment
	    		this.focusNonQuestion(e);
			}
        }
    },
	leaveWithOutSaving: function() {
	
		if( this.deBug ) {
			console.log('leaveWithOutSaving()');
		}
	
		// check to see if on current question there is unsaved data
		// before leaving
		if( this.previousQuestion ) {
			this.setQuestionAnswerData( this.previousQuestion );
			
			// check for unsaved data in our boxIndex object
        	this.unsavedDataCheck( this.previousQID );
		}
		
		if( $(this.currentQID) ) {
			var currentQuestion = $(this.currentQID);
			this.setQuestionAnswerData( currentQuestion );
			
			// check for unsaved data in our boxIndex object
        	this.unsavedDataCheck( this.currentQID );
		}
		
		if( this.deBug ) {
			console.log( 'unsavedCount:' + this.unsavedQids.length );
			console.log( 'unsavedMessage:' + this.beforeUnloadMsg );
		}
		
		//alert(this.unsavedQids.length);
		// if there are unsaved questions on the page return a msg 
		// which triggers the onbeforeunload window event
		if( this.unsavedQids.length > 0 && this.beforeUnloadMsg ) {
        	return this.beforeUnloadMsg;
        } else {
        	return;
        }
	},
	focusNonQuestion: function(e) {
		var target = $(e.target);
		
		if( this.deBug ) {
			console.log('focusNonQuestion():');
		}
		
		if( target.hasClass('sButton') || target.hasClass('fButton') ) {
			this.beforeUnloadMsg = null;
		} else {
			this.beforeUnloadMsg = 'You have unsaved, unsubmitted answers.';
		}
			
	},
    getInitialAnswerData: function() {  
        this.boxs.each(function(box) {
            var qid = box.getProperty('id');
            var fieldType = '';
            this.boxIndex[qid] = {};
            
            var entryFields = this.getBoxFields(box);
            
            entryFields.each(function(field) {
                var inputId = field.getProperty('id');

                if( inputId ) {
                    
                    if( field.getProperty('type') == 'text' && !( $defined( field.getParent('.matrix') ) ) ) {
                                           
                        currentValue = ( field.value ) ? field.value : '';
                        fieldType = 'textfield';
                        
                    } else if( field.getProperty('type') == 'radio' || field.getProperty('type') == 'checkbox' ) {
                        
                        currentValue = ( field.checked ) ? true : false;
                        fieldType = field.getProperty('type');
                        
                    } else if( field.get('tag') == 'select' ) {
                        
                        currentValue = field.selectedIndex;
                        fieldType = field.get('tag');
                        
                    } else if( field.get('tag') == 'textarea' ) {

                        currentValue = field.value;
                        fieldType = field.get('tag');
                        
                    } else if( field.getProperty('type') == 'hidden' && ( field.getProperty('id').substr(0,2) == 'RG' ) ) { /* graphPad */  

                        currentValue = this.getGraphPadData( field.value );
                        fieldType = 'graphPad';
                        
                    } else if( field.getProperty('type') == 'hidden' && this.padCheck( field, 'init' ) == 'calcpad' ) { /* calcPad */
                                            
                        field.value = ( field.value == '' ) ? this.emptyCalcPadXML : field.value;
                        currentValue = field.value;
                        fieldType = 'calcPad';
                        
                    } else if( field.getProperty('type') == 'hidden' && this.padCheck( field, 'init' ) == 'mathpad' ) { /* mathPad */
                        
                        field.value = ( field.value == '' ) ? this.emptyMathPadXML : field.value;
                        currentValue = field.value;
                        fieldType = 'mathPad';
                        
                    } else if( field.getProperty('type') == 'hidden' && ( field.getProperty('id').substr(0,2) == 'RL' ) ) { /* pencilPad */
                        
                        field.value = ( field.value == '' ) ? this.emptyPencilPadXML : field.value;
                        currentValue = field.value;
                        fieldType = 'pencilPad';
                        
                    } else if( field.getProperty('type') == 'hidden' && ( field.getProperty('id').substr(0,2) == 'RR' ) ) { /* numberLine */
                        
                        // numberLine uses andupdates a hidden field on the fly  (very nice!)                           
                        currentValue = field.value;
                        fieldType = 'numberLine';
                        
                    } else if( field.getProperty('type') == 'hidden' && ( field.getProperty('id').substr(0,2) == 'RN'  ) ) { /* matrix */
                                              
                        currentValue = field.value;
                        fieldType = 'matrix';
                        
                    } else if( field.getProperty('type') == 'hidden' && ( field.getProperty('id').substr(0,2) == 'RJ'  ) 
                                                                     && field.getProperty('class') == 'jmeStorage' ) { /* JME */
                      
                        currentValue = field.value;
                        fieldType = 'jme';
                    } else {
                
                        if( this.deBug ) {
                            console.log('getInitialAnswerData(): Unexpected input in ' + qid);
                            console.log(field);
                            console.log(fieldType);
                        }
                        return;
                
                    }
                    
                    if( currentValue !== null ) {
                        this.boxIndex[qid][inputId] = {
                            original : currentValue
                        }
                    } else {
                    	if( this.deBug ) {
                    		console.log('getInitialAnswerData(): currentValue is null of type - ' + fieldType);
                    	}
                    }
                }
            }.bind(this) );
        }.bind(this) );
    },
	padCheck: function( field, state ) {

		// NOTE: if layout of object changes
		// this method will fail (gracefully) as is
		// we need to standardize input data in the app
		
		if( state ) {
            var prevSib;
            try {
                prevSib = field.getPrevious('span.flashPadWrap');
            }
            catch(e) {}
            
			if( prevSib ) {
				var flashPlaceholder = prevSib.getFirst('span');
				
				if( flashPlaceholder ) {
				
					if( flashPlaceholder.get('id').test('mathpad', 'i') ) {
						return 'mathpad';
					} else if( flashPlaceholder.get('id').test('calcpad', 'i') ) {
						return 'calcpad';
					} else if( flashPlaceholder.get('id').test('physpad', 'i') ) {
						return 'physpad';
					} else {
						return false;
					}
				} else {
					return false;
				}
			} else {
				return false;
			} 
		} else {
			var prevSib;
            try {
                prevSib = field.getPrevious();
            }
            catch(e) {}
			
			if( $defined( prevSib ) ) {
				// looking for object tag
				var objectTag = prevSib.getElement('object');
				if( objectTag ) {				
					var objectTagId = objectTag.getAttribute('id');
					
					if( objectTagId ) {
						if( objectTagId.test('calcpad_obj_') && $defined($('calcpad_obj_' + field.getProperty('id'))) ) {
							return 'calcpad';
						} else if( objectTagId.test('mathpad_obj_') && $defined($('mathpad_obj_' + field.getProperty('id'))) ) {
							return 'mathpad';
						}  else if( objectTagId.test('physpad_obj_') && $defined($('physpad_obj_' + field.getProperty('id'))) ) {
							return 'physpad';
						} else {
							return false;
						}
					} else {
						return false;
					}
				} else {
					return false;
				}
			} else {
				return false;
			}
		}
	},
    setQuestionAnswerData: function( box ) {
        if(box) {
            var qid = box.getProperty('id');     
            var entryFields = this.getBoxFields(box);     
            entryFields.each( this.parseEntryType.bindWithEvent(this, [qid]) );
        }
    },
    getBoxFields : function( box ) {
        var content = box.getElement('.qContent');
        
        var entryFields = [];
        if( content ) {
            var inputs    = content.getElements('input');
            var selects   = content.getElements('select');
            var textareas = content.getElements('textarea');
                
            entryFields.extend(inputs);
            entryFields.extend(selects);
            entryFields.extend(textareas);
        } else {
            if( this.deBug ) {
                console.log('Question ' + qid + ' did not have div with class .qContent');
            }
        }
        
        return entryFields;
    },
    parseEntryType: function( field, qid ) {
        var inputId = field.getProperty('id');
 
        if( inputId ) {
            var currentValue = '';
            
            // need to get the value for previous matrix seperately
            // causes errors in Safari/OSX when not there so we can't just stuff it in an if block
            // see FB 5558
            var prevMatrix = false;
            try {
                if( field.getPrevious('#matrix_' + field.getProperty('id')) ) {
                    prevMatrix = true;
                }
            } catch(e) {}
            
            if( field.getProperty('type') == 'text' && !( $defined( field.getParent('.matrix') ) ) ) { /* matrix */
                currentValue = field.value;
                
            } else if( field.getProperty('type') == 'radio' || field.getProperty('type') == 'checkbox' ) { /* radios and checkboxes */
                
                currentValue = ( field.checked ) ? true : false;
            
            } else if( field.get('tag') == 'select' ) { /* select boxes */
            
                currentValue = field.selectedIndex;
            
            } else if( field.get('tag') == 'textarea' ) { /* text areas */
                
                // will have to pull editor value dynamically
                if( field.hasClass('dijitEditor') ) {
                    currentValue = dijit.byId(field.id).getValue(false);
                } else {
                    currentValue = field.value;
                }
                
            } else if( field.getProperty('type') == 'hidden' && ( field.getProperty('id').substr(0,2) == 'RG' ) ) { /* graphPad */
                
                var graphPadId = 'graphpad_swf_' + field.getProperty('id');
                
                var graphPadValue = ( $defined( $(graphPadId) ) ) ? $(graphPadId).getXMLfromFlash().toString() : '';
                
                currentValue = this.getGraphPadData( graphPadValue );
                
            } else if( field.getProperty('type') == 'hidden' && this.padCheck( field ) == 'calcpad' ) { /* calcPadPad */
                
                var calcPadId = 'calcpad_obj_' + field.getProperty('id');
                var calcPadValue = ( $defined($(calcPadId)) ) ? $(calcPadId).getXML().toString() : '';
                currentValue = calcPadValue;
                
            } else if( field.getProperty('type') == 'hidden' && this.padCheck( field ) == 'mathpad' ) { /* mathPad */
                
                var mathPadId = 'mathpad_obj_' + field.getProperty('id');
                var mathPadValue = ( $defined($(mathPadId)) ) ? $(mathPadId).getXML().toString() : '';    
                currentValue = mathPadValue;

            } else if( field.getProperty('type') == 'hidden' && ( field.getProperty('id').substr(0,2) == 'RL' ) ) { /* pencilPad */
                
                var pencilPadId = 'pencilpad_swf_' + field.getProperty('id');
                var pencilPadValue = ( $defined($(pencilPadId).getXMLfromFlash()) ) ? $(pencilPadId).getXMLfromFlash().toString() : '';
                currentValue = pencilPadValue;
                
            } else if( field.getProperty('type') == 'hidden' && ( field.getProperty('id').substr(0,2) == 'RR' ) ) { /* numberLine */
                        
                currentValue = field.value;
                
            } else if( field.getProperty('type') == 'hidden' && prevMatrix ) { /* matrix */
                // initially the value doesn't have [] but it will... yay inconsistant data!!!!!
                currentValue = field.value.replace('[', '').replace(']', '');    
                
            } else if( field.getProperty('type') == 'hidden' && ( field.getProperty('id').substr(0,2) == 'RJ'  ) 
                                                                     && field.getProperty('class') == 'jmeStorage' ) { /* JME */
                currentValue = field.value;
            } else {
              
                if( this.deBug ) {
                    console.log('Unexpected input in ' + qid);
                    console.log(field);
                    console.log(currentValue);
                }
                return;
                
            }
            
            try {
               this.boxIndex[qid][inputId].current = currentValue;
            }
            catch(e){
                if( this.deBug ) {
                    console.log('Box at ' + qid + ', ' + inputId + ' was never initialized');
                }
            }
        }
    },
    unsavedDataCheck: function(qid) {
        // first thing push the previous QID in to
        // the unsaved storage array whether it has
        // unsaved changes or not. We will erase it
        // later if it turns out there are no unsaved
        // changes.

        if( ( this.unsavedQids.indexOf(qid) == -1 ) ) {
            this.unsavedQids.push(qid);
            this.unsavedQnums.push(qid.replace('question',''));
        }

        // 
        var unsavedLength = this.unsavedQids.length;
        for( var i = unsavedLength; --i >= 0; ) {
            var question = this.boxIndex[this.unsavedQids[i]];
        
            for( var j in question ) {
                var input = question[j];
                var unsaved = false;
                                
                if( ( input.original != input.current ) && $defined(input.current) ) {
                    unsaved = true;
                    
                    // we break out if we find unsaved data
                    break;    
                }
            }
            
            // if we make it through the unsaved check clean,
            // remove the previous QIDS fromt he unsaved array
            if( !unsaved ) {
                // remove qNum from array
                this.unsavedQnums.erase(this.unsavedQids[i].replace('question',''));
                
                // set unsaved flag to false because we didn't find 
                // unsaved data
                this.boxIndex[this.unsavedQids[i]].unsavedData = false;
                
                // finally remove the QID from the unsaved array completely
                this.unsavedQids.erase(this.unsavedQids[i]);
                
            } else {
            
                // set unsaved flag for QID to true since we found
                // unsaved data
                this.boxIndex[this.unsavedQids[i]].unsavedData = true;
            }
            
        }
        

        // array for display
        // copying qNum array for display purposes
        // no array manipulation on persistant class array
        // this.unsavedQnums
        var qNumClone = $A(this.unsavedQnums);

        // sort it for nicer display, perhaps bubble sort method deserving
        qNumClone.sort( this.numSort );
        var unsavedQnumsMinusCurrent = qNumClone.erase( this.currentQID.replace('question','') )
           
        // if there are unsaved question numbers get them ready for display
        if( unsavedQnumsMinusCurrent.length > 0 ) {
            // show save/submit all
            $$('.submitSaveAll .saved').removeClass('saved').addClass('unsaved');
            
            // hide standard buttons
            $$('.submitSaveStandard .saved').removeClass('saved').addClass('unsaved');
            
            // join list for display
            $$('.questionCount').set('html', unsavedQnumsMinusCurrent.join(', '));
            
        } else {
            // hide save/submit all
            $$('.submitSaveAll .unsaved').removeClass('unsaved').addClass('saved');
            
            // show standard buttons
            $$('.submitSaveStandard .unsaved').removeClass('unsaved').addClass('saved');
        }
    },
	onBeforeUnload: function() {
        var rtn = false;
        var l = this._unload_events.length;
        for( var i = 0; i < l; i++ ) {
            var msg = this._unload_events[i]();
            if( msg ) rtn = msg;
        } 
        if( rtn ) return rtn;
        else return;
    },
    inlineJsCleanUp: function(method) {
        if( !this.hasOwnProperty('_unload_events') ) {
            this._unload_events = [];
            if( this.IE ) {
                // IE is terrible, therefore if a link has inline javascript as the HREF, ie triggers
                // an onbeforeunload which results in possibly 2 onbeforeunloads.  One when the link is clicked
                // and another when the javascript actually tries to leave the page.  To get around this ugliness
                // we kill the href property and use add a click event to fire off the code 
                var anchors = $$('a[href^=javascript]');
                for( var i = anchors.length; --i >= 0; ) {
                    var href = anchors[i].href;
                    if( href.match( /^\s*javascript\s*:/ ) ) {
                        href = href.substr( href.indexOf(':') + 1 );
                        anchors[i].removeAttribute('href');
                        anchors[i].store('jscode', href);
                        anchors[i].addEvent('click', this.runJSHRef.bindWithEvent(this, [anchors[i]]));
                    }
                }
                
                // onclicks in assignment summary need event propogation stopped or IE errors on 'cancel'
                // of onbeforeunload events
                var tds = $$('td.questionNumber');
                for( var i = tds.length; --i >= 0; ) {
                    var onclick = tds[i].getProperty('onclick');
                    if( onclick ) {
                        tds[i].removeAttribute('onclick');
                        tds[i].store('jscode', onclick);
                        tds[i].addEvent('click', this.runJSHRef.bindWithEvent(this, [tds[i]]));
                    }
                }
            }
            try {
            	window.onbeforeunload = this.onBeforeUnload.bind(this);
        	} catch(e) {}
        }
        this._unload_events.push( method );
    },
    runJSHRef: function( e, anchor ) {
        e.stop();

        var code = anchor.retrieve('jscode');
        
        if( code && code.length ) {
            // for whatever reason IE throws an error when a user chooses to cancel the onbeforeunload alert
            // if it was triggered by an anchor with an href containing javascript.  To catch the error a try-catch
            // has to be evaled along with the rest of the code.
            $exec('try{' + code + '} catch(e){}');
        }
    },
    getGraphPadData: function( value ) {
    	// trying to extract relevant change data disregarding core 
      	// xml structure which can vary depending on graphPad init
	  	var graphPadValue = '';
	  	
	  	if( value != '' ) {
	  	
	  	    var osIndex = value.indexOf(this.graphPadXML[0]);
	  	    var fsIndex = value.indexOf(this.graphPadXML[1]);
	  	
	  	    if( osIndex == -1 && fsIndex != -1 ) { // <fs> found and no <os> found
	  	        
	  	        graphPadValue = value.substr(fsIndex);
	  	        
	  	    } else if( fsIndex == -1 && osIndex != -1 ) { // <os> found and no <fs> found
	  	    
	  	        graphPadValue = value.substr(osIndex);
	  	    
	  	    } else if( fsIndex != -1 && osIndex != -1 ) { // both <os> and <fs> found
	  	        if( fsIndex > osIndex ) {
	  	        	graphPadValue = value.substr(osIndex);
	  	        } else {
	  	        	graphPadValue = value.substr(fsIndex);
	  	        }
	  	    }
	  	}
	  	
	  	return graphPadValue;
    }
});

/*-----------------------------------------------------------------------------
expand Class

version:	1.0
author:		iq
libs:		mootools 1.2.4

description: a modular expand/collapse tool for WebAssign content.  Through use
of a properly constructed unordered list, you can apply this script to make the
list elements collapsable.

notes: In this class you will notice that we target elements by walking through
the dom quite a bit and never saving them to a variable.  This is intentional
to adapt to how the Tip class works because it creates and destroys elements
quite frequently.

Basic html structure...

<ul class='waExpand container'>
	<li class='first container'>
        <div class="titleBar container">
        	...
        </div>
        <div class="element init container">
            ...
        </div>
    </li>
    ...
</ul>

CSS styles are redefinable
-----------------------------------------------------------------------------*/
expand = new Class({
        Implements: [Options, Events],
        options: {
				lists: [],
				state: '',
				trigger: 0, /* 0: icon trigger, 1: header trigger (includes icon) */
				buttons: false,
				cookie: true,
				cookiePath: '/',
                onShow: function() {},
                onHide: function() {},
				deBug: false
        },
        initialize: function(options){
                this.setOptions(options);
             	
             	this.cookie = this.options.cookie ? this.options.cookie : false;
             	this.cookiePath = this.options.cookiePath;
             	this.cList = new Object;
             	this.obj = new Object;
				this.lists = this.options.lists;
				this.state = this.options.state ? this.options.state : false;
				this.trigger = this.options.trigger;
				this.buttons = this.options.buttons ? this.options.buttons : false;
				this.listPrefix = 'wa-list';
				this.list = [];
				this.deBug = this.options.deBug ? this.options.deBug : false;
				
				if( this.cookie ) {
				    this.initCookie();
				}
				
				this.toggle();
							
        },
        // FIXME:  this is not working, at least not on the Template Manager page
        initCookie: function() {
            var lists = [];
            // always make sure list is in array format
	        // even if just 1 list exists
			if( $type(this.lists) != 'array' ) {
				lists.push(this.lists);
			} else {
				lists = this.lists;
            }
            
            this.listCountLength = this.lists.length;
            var count = 1;
            lists.each(function(list) {
                this.cList[this.listPrefix + count] = list;
                this.cList[this.listPrefix + count].state = [];

                var lis = list.getElements('li.first');
                var lisLength = lis.length;

                var state = [];
                var liCount = 0;
                lis.each(function(li) {
                    li.store('count', liCount);
                    
                    // check for stored state, otherwise have them default closed
                    if( Cookie.read(this.listPrefix + count) ) {
                        
                        var cookieVal = Cookie.read(this.listPrefix + count);
                        var stateAray = cookieVal.split(',');
                        
                        this.cList[this.listPrefix + count].state.push(stateAray[liCount]);
                    } else {
                        this.cList[this.listPrefix + count].state.push('close');
                    }

                    liCount++;
                }.bind(this));
                
                // create initial cookie for each list
                this.waSgNavCookie = Cookie.write(
                    this.listPrefix + count, 
                    this.cList[this.listPrefix + count].state.join(','), {
                    duration: 100,
                    path: this.cookiePath }
                );

                count++;
            }.bind(this));
        },
        toggle: function(start) {
        	var expand_instance = this;
			var lists = [];
	
	        // always make sure list is in array format
	        // even if just 1 list exists
			if( $type(this.lists) != 'array' ) {
				lists.push(this.lists);
			} else {
				lists = this.lists;
			}

			if(lists) {	
			    
			    listCount = 1;
			    lists.each(function(list) {
			        
			        // store reference to object in ul
			        list.store(this.listPrefix, this);
					list.store('listName', this.listPrefix + listCount);
					
			    	// the ul elements should be styled to display:none
			    	// initially so progressive content doesnt display 
			    	// on page load and then collapsing them
			    	list.setStyle('display','block');
			    
			    	var lis = list.getElements('li.first');
			    	
			    	var count = 0;
			    	lis.each(function(li) {
                        this.li = li;

			    		// the title element next to the expand icon
			    		var titleBar = li.getFirst('.titleBar');

			    		// the icon +/- element
			    		var icon = titleBar.getFirst();
			    		
			    		// contains expand/collapse content
			    		var content = li.getFirst().getNext();
    		
			    		// slide event to show progressive content disclosure
			    		var slide = new Fx.Slide(li.getFirst().getNext(), {
			    		    duration: 200,
			    		    transition: Fx.Transitions.Pow.easeOut,
                            onStart: function() {
                                if(this.open) {
                                    content.getParent().setStyle('overflow', 'hidden');
                                    content.getParent().setStyle('position', 'relative');
                                }
                            },
			    		    onComplete: function() {
			    		    	expand_instance.setState(li);
			    		    	// if height isn't zero then it is showing
                                if (this.open) {
                                    expand_instance.fireEvent('show');
                                    // so content inside the div can grow in size
                                    content.getParent().setStyle('height', 'auto');
			    		    	} else { // if height is zero it is hidden
                                    expand_instance.fireEvent('hide');
			    		    	}
			    		    }
			    		});
			    		
			    		var element = li.getFirst().getNext().getFirst();

						// init states depending on object constructor settings
			    		this.initState(li, slide, expand_instance.state, count, listCount);
			    		
			    		// set up what triggers the expand/collapse
			    		//this.triggerInit();
			    		
			    		
			    		if( this.trigger == 0 ) {
			    			this.triggerInit(icon, element, slide, content);
			    			titleBar.setStyle('cursor', 'auto');
			    			icon.setStyle('cursor', 'pointer');
			    		} else {
			    			this.triggerInit(titleBar, element, slide, content);
			    		}
			    			    		
			    		// see if content contains buttons to trigger collapse
			    		if( this.buttons ) {
			    			var buttons = content.getElements('input.collapse')
			    		
			    			if( buttons ) {
			    				buttons.addEvent('click',function() {
			    					this.setState(li);
			    					slide.slideOut();
			    				});
			    			}
						}
			    		count++;					
			    	}.bind(this));
			    	listCount++;	
			    }.bind(this));
			}
        },
        triggerInit: function(trigger, element, slide, content) {
			if( trigger ) {
			    var opener = function(e) {
                    if( e ) e.stop();
                    element.removeClass('init');    
                    slide.toggle();
                };
			    trigger.addEvent('click',opener);
			    trigger.store('expand:toggle', opener);
			} else {
				if( this.deBug ) {
			    	alert("It appears your markup isn't well-formed for the expand script.");
			    }
			}
        },
		setState: function(el) {
            var parentListEl = el.getParent();
            
            var listName = parentListEl.retrieve('listName');
            var liIndex = el.retrieve('count');
            
            var currentState = this.cList[listName].state;

			var content = el.getNext();
			var expand = el.getFirst().getFirst();
	    	if( el.hasClass('open') || expand.hasClass('open') ) {
	    		el.removeClass('open');
	    		expand.removeClass('open');
	    		expand.addClass('close');
	    		
	    		// set expand state
	    		currentState[liIndex] = 'close';
	    		el.store('expand:open', false);
	    	} else {
	    		el.removeClass('close');
	    		expand.removeClass('close');
	    		expand.addClass('open');
	    		
	    		// set expand state
	    		currentState[liIndex] = 'open';
	    		el.store('expand:open', true);
	    	}

	    	// set current state internally
	    	this.cList[listName].state = currentState;

	    	// create initial cookie for each list
	    	if( this.cookie ) {
                Cookie.write(
                    listName, 
                    currentState.join(','), {
                    duration: 100,
                    path: this.cookiePath }
                );
            }
	    	
		}, 
		initState: function(el, slide, state, liCount, listCount) {
			var content = el.getFirst().getNext();
			var expand = el.getFirst().getFirst();
	    	var height = content.getStyle('height').toInt();
	    	var element = el.getFirst().getNext().getFirst();
	    	
	    	// check if state is set manually or by a cookie
	    	if(this.state || ( this.cookie && Cookie.read(this.listPrefix + listCount) )) {

	    		var stateArray = ( this.cookie && Cookie.read(this.listPrefix + listCount) ) 
	    		               ? Cookie.read(this.listPrefix + listCount).split(',') 
	    		               : this.state.split(',');
	    		
	    		if( !this.cList[this.listPrefix + listCount] ) {
	    		    // store current state in list ul
	    		    this.cList[this.listPrefix + listCount] = {};
	    		    this.cList[this.listPrefix + listCount].state = stateArray[liCount];
	    		}
	    		
	    		// set classes which set icon and state
	    		if( stateArray[liCount] == 'open' ) {
	    			expand.removeClass('close');
	    			expand.addClass('open');
	    			element.removeClass('init');
	    			this.fireEvent.delay(1, this, ['show']);
	    		} else {
	    			expand.removeClass('open');
	    			expand.addClass('close');
                    slide.element.getParent().setStyle('overflow', 'hidden');
                    slide.element.getParent().setStyle('position', 'relative');
	    			slide.hide();
	    			this.fireEvent.delay(1, this, ['hide']);
	    		}
	    	} else {
	    		if( el.hasClass('open') ) {
	    			expand.removeClass('close');
	    			expand.addClass('open');
	    			element.removeClass('init');
	    			slide.show();
	    		} else {
	    			expand.removeClass('open');
	    			expand.addClass('close');
                    slide.element.getParent().setStyle('overflow', 'hidden');
                    slide.element.getParent().setStyle('position', 'relative');
	    			slide.hide();
	    		}
	    	
	    	}
	    	el.store('expand:open', expand.hasClass('open'));
		},
		alertState: function(li) { // FIXME: broken...
			var lists;
			var currentStateArray = new Array();
		
			if( $type(this.lists) != 'array' ) {
				lists.push(this.lists);
			} else {
				lists = this.lists;
			}

			if(lists) {	
			    lists.each(function(list) {
			    	list.each()
					currentStateArray.push();
				});
			}
			
		}
});

/*-----------------------------------------------------------------------------
spinner waOverlay

version:    1.1
author:     iq
libs:       mootools 1.2+

notes: New overlay class.  Will eventually be used to replace modal scripts.
-----------------------------------------------------------------------------*/
var waOverlay = new Class({
    
    Implements: [Options,Events],
    
    options:  {
        id: 'overlay',
        color: '#fff',
        duration: 500,
        opacity: 0.4,
        zIndex: 999999,
        spinner: false,
        spinnerContainer: 'waSpinner',
        placement: 'bottom',
        position: 'absolute',
		styles: {}
    },
    
    initialize: function(container,options) {
        this.setOptions(options);
        this.container        = $(container);
        this.id               = this.options.id;
        this.color            = this.options.color;
        this.opacity          = this.options.opacity;
        this.duration         = this.options.duration;
        this.spinnerContainer = this.options.spinnerContainer;
        this.placement        = this.options.placement;
        this.position         = this.options.position;
        this.styles           = this.options.styles;
        
        // 
        this.overlay = new Element('div',{
            id: this.id,
            opacity: 0,
            styles: {
                position: this.position,
                background: this.color,
                left: 0,
                top: 0,
                'z-index': this.options.zIndex
            },
            events: {
                click: function() {
                    this.fireEvent('click');
                }.bind(this)
            }
        });
        
        // set option styles
        this.overlay.setStyles(this.styles);
		
		// inject into the overlay container
		this.overlay.inject(this.container);

        this.tween = new Fx.Tween(this.overlay,{ 
            duration: this.options.duration,
            link: 'cancel',
            property: 'opacity',
            onStart: function() {
                var size = this.container.getScrollSize();
                this.overlay.setStyles({
                    width: '100%',
                    height: '100%' 
                });
            }.bind(this),
            onComplete: function() {
                this.fireEvent(this.overlay.get('opacity') == this.options.opacity ? 'show' : 'hide');
            }.bind(this)
        });
    },
    open: function() {
        this.fireEvent('open');
        this.tween.start(this.options.opacity);
        this.spinner('open');
    },
    close: function() {
        this.fireEvent('close');
        this.tween.start(0);
        this.spinner('close');
    },
    spinner: function(option) {
        if( this.options.spinner && option == 'open' ) {
            
            // create a spinner object
            // FIXME: Figure out how to completely fold spinner
            // function() inside Moo spinner class
            this.waspin = new waspinner(this.spinnerContainer);
            
            // FIXME: Raphael can't initialize inside of a Moo
            //        created DOM element (well this only breaks in IE, who knew?)
            $('waSpinner').setStyles({
                'margin':'0',
                'width':'70px',
                'top':'50%',
                'left':'50%',
                'position':'fixed'
            });
            
            this.waspin.show();
        } else if( this.options.spinner && option == 'close' ) {
            this.waspin.remove();
        }
    }
});

/*----------------------------------------------------------------------------
meeToolLoader

version:    1.1
author:     Mike Morella, Ian Quattlebaum
libs:       mootools 1.2+

notes: Was originally a custom extension of the overlay class, but has been
       repurposed as a loading script for dynamic javascript tools such as
       the meeTool.  Hopefully this can be abstracted out to be a loader for 
       more dynamic tools for student side.  We need to start thinking about 
       the 100+ scenario for student side and thats what this is an attempt 
       at.
------------------------------------------------------------------------------*/
var meeToolLoader = new Class({                        
    Extends: waOverlay,        
                              
    options: {               
        text: '',
        styles: {}
    },           
                
    initialize: function(container, options) {
        this.setOptions(options);             
        this.parent(container, options);     
                                                                 
        this.text = this.options.text;                          
        this.overlay.setStyles({
            visibility : 'visible',
            opacity    : 100,   
            border     : '1px solid #ccc',
            top        : '-1px',
            left       : '-1px',
            overflow   : 'auto',
            cursor     : 'text'
        });
        this.textDiv = new Element('p', {                      
            styles: {                                         
                background:this.color,                       
                padding:'10px',
                left: 0,                                    
                top: 0,                                    
                'z-index' : this.options.zIndex           
                                                        
            }                                           
        }).inject(this.overlay);
        
        this.textDiv.set('html', this.text);   
    },
    
    setHTML: function( value ) {
        this.textDiv.set('text', value); 
    },
    
    getId: function() {
        return this.container.get('id') + '_overlay';
    }
   
});        
    

/*-----------------------------------------------------------------------------
spinner Class

version:    1.0
author:     iq
libs:       mootools 1.2, 1.3(comp)
            raphael 1.5

notes: Ajax spinner uses a vector lib to create a fluid cross browser/cross
       platform experience.  Methods for adding and removing spinner from 
       the page for Ajax requests.
-----------------------------------------------------------------------------*/
var waspinner = new Class({
        Implements: [Options, Events, Class.Occlude],
        options: {
            innerRadius: 17,
            outerRadius: 27,
            strokeCount: 12,
            strokeWidth: 7,
            strokeColor: '#666'
        },
        initialize: function(container, options) {
            this.setOptions(options);
            this.container = ( container ) ? document.id(container) : $(document.body);
            this.innerRadius = this.options.innerRadius.toInt();
            this.outerRadius = this.options.outerRadius.toInt();
            this.strokeCount = this.options.strokeCount.toInt();
            this.strokeWidth = this.options.strokeWidth.toInt();
            this.strokeColor = this.options.strokeColor;
            this.opacity = [];
            this.sectors = [];
            this.sectorsCount;
            this.tick;
            this.r;

            // use occlude class to check and make
            // sure object isn't already instantiated
            if ( this.occlude('spinny', this.container) ) {
                return this.occluded;
            }

            // store instance reference in element
            this.container.store('spinny', this);
                    
        },
        
        show: function() {
            this.waSpin = spinner(this.container, this.innerRadius, this.outerRadius, this.strokeCount, this.strokeWidth, this.strokeColor);
        },
        
        remove: function() {
            this.waSpin.clear();
        }   
});

// spinner function, needs to be rolled into moo spinner class, but
// im still learning how to play with moo and raphael together - iq
function spinner(holderid, R1, R2, count, stroke_width, colour) {

    var sectorsCount = count || 12,
        color = colour || "#fff",
        width = stroke_width || 15,
        r1 = Math.min(R1, R2) || 35,
        r2 = Math.max(R1, R2) || 60,
        cx = r2 + width,
        cy = r2 + width,
        r = Raphael(holderid, r2 * 2 + width * 2, r2 * 2 + width * 2),
        
        sectors = [],
        opacity = [],
        beta = 2 * Math.PI / sectorsCount,

        pathParams = {stroke: color, "stroke-width": width, "stroke-linecap": "round"};
        Raphael.getColor.reset();
    
    for (var i = 0; i < sectorsCount; i++) {
        var alpha = beta * i - Math.PI / 2,
            cos = Math.cos(alpha),
            sin = Math.sin(alpha);
        opacity[i] = 1 / sectorsCount * i;
        sectors[i] = r.path([["M", cx + r1 * cos, cy + r1 * sin], ["L", cx + r2 * cos, cy + r2 * sin]]).attr(pathParams);
        if (color == "rainbow") {
            sectors[i].attr("stroke", Raphael.getColor());
        }
    }
    
    var tick;
    
    (function ticker() {
        opacity.unshift(opacity.pop());
        for (var i = 0; i < sectorsCount; i++) {
            sectors[i].attr("opacity", opacity[i]);
        }
        r.safari();
        tick = setTimeout(ticker, 1000 / sectorsCount);
    })();
    
    return r;
}


/* example
window.addEvent('domready', function() {
    var overlay = new waOverlay(document.body,{
        spinner: true,
        onClick: function() {
            this.close();
        }
    });
    
    overlay.open();
}); */


/*-----------------------------------------------------------------------------
tip Class

version:    1.1
author:     iq
libs:       mootools 1.2

notes: In this class you will notice that we target elements by walking through
the dom quite a bit and never saving them to a variable.  This is intentional
to adapt to how the Tip class works because it creates and destroys elements
quite frequently.
-----------------------------------------------------------------------------*/
tip = new Class({
        Implements: [Options, Events, Class.Occlude],
        options: {
            container: '',
            content: '',
            contentClass: '',
            scroll: false,
            drag: false,
            title: null,
            maxHeight: null,
            deBug: false,
            onShow: function() {},
            onHide: function() {}
        },
        initialize: function(options){
            this.setOptions(options);
            this.tip_instance = {};
            this.haveCloseEvents = false;    
            this.container = ( this.options.container ) ? $(this.options.container) : null;
            this.content = ( this.options.content ) ? $(this.options.content) : null;
            this.drag = ( this.options.drag ) ? this.options.drag : null;
            this.title = this.options.title;
            this.maxHeight = ( this.options.maxHeight ) ? this.options.maxHeight : null ;
            this.contentClass = this.options.contentClass;
            this.titleCreated = false;
            this.titleText = ( this.title ) ? this.title : '';
            this.bubble = null;
            this.caret = null;
            this.sw = null;
            this.scroll = this.options.scroll;
            this.caretPos = ( this.options.caretPos ) ? this.options.caretPos : 'n';
            this.isToolTip = ( this.options.isToolTip ) ? true : false;
            this.first = true;
            // safari does not trigger mouseup/click on select elements so we have to use mouseup
            this.triggerEvent = ( this.options.triggerEvent )
                ? this.options.triggerEvent
                : ( this.isToolTip )
                    ? 'mouseover'
                    : ( Browser.Engine.webkit && $(this.container).get('tag') == 'select' )
                        ? 'mousedown'
                        : 'click';
            
            // we need to store a reference to the method created by 'bind'
            // so that we can remove the event handler later in closeAllBubbles
            this.closer = this.closeAllBubbles.bind(this);
            
            // use occlude class to check and make
            // sure object isn't already instantiated
            if ( this.occlude('tip', this.container) ) {
                return this.occluded;
            }
            
            // store instance reference in element
            this.container.store('tip', this);
            
            
            // add the tip
            this.add();
            this.trigger();
        },
        trigger: function() {
            var instance = this;
            var xo = -46;
            var yo = 28;
            var content = this.content;
            var trigger = this.container;
                        
            // check for what size tip needed
            var alpha = new RegExp(/[a-z]/gi);
            var className = $chk(content) ? content.getProperty('class') : '';
            
            var individualClasses = className.split(' ');
            
            if( className != 'bContent') {
                var specs = individualClasses[1].split('|');
            }
            
            // default these if not present in bContent class property
            var width = ( className != 'bContent') ? specs[0].replace(alpha, '') : 250;
            var xOffset = ( className != 'bContent') ? specs[1].replace(alpha, '') : 0;
            xOffset = xOffset.toInt();
            var yOffset = ( className != 'bContent') ? specs[2].replace(alpha, '') : 0;
            yOffset = yOffset.toInt();
            
            // caret position
            this.caretPos = ( className != 'bContent') ? specs[3] : 'n';
            
            // west caret starting position
            if( this.caretPos == 'w' ) {
                xo = 10;
                yo = -24;                
            } else if( this.caretPos == 'nr' ) {
                xo = -319 - ( width - 377 );
                yo = 28;
            } else if( this.caretPos == 'sr' ) {
                xo = -319 - ( width - 377 );
                yo = 11;
            }
            
            var currentWidth = this.currentWidth;
            
            // reposition and resize
            this.repositionAndResize = function() {
                this.addCloseEvents();
                
                var pageCoords = trigger.getPosition();    
                var bookScrollsYPosition = 0;
                var x = pageCoords.x + xOffset + xo;
                                
                if(instance.scroll && Browser.Engine.trident) {
                    var bookScroll = $('bookScrollWrap');
                    var bookScrolls = bookScroll.getScroll();
                    bookScrollsYPosition = bookScrolls.y;
                }
                
                var y = pageCoords.y + yOffset + bookScrollsYPosition + yo;
                
                // apply width first for height calculation purposes
                content.getParent('.y-tip').setStyles({
                    width: width + 'px'
                })
                
                // if a southern right caret
                if( instance.caretPos == 'sr' ) {
                    var bSize = content.getParent('.y-tip').getSize();
                    y -= bSize.y; 
                }
                
                content.getParent('.y-tip').setStyles({
                    top: y + 'px',
                    left : x + 'px'
                })
                
                // if maxHeight set
                if( this.maxHeight ) {
                    this.applyHeight();
                }
            }.bind(this);
            
            
            // if target is a input box
            if( trigger.get('tag') == 'input' ) {
                trigger.addEvent('focus', function() {
                
                    var pageCoords = trigger.getPosition();    
                    var bookScrollsYPosition = 0;
                    var x = pageCoords.x + xOffset + xo;
                                    
                    if(instance.scroll && Browser.Engine.trident) {
                        var bookScroll = $('bookScrollWrap');
                        var bookScrolls = bookScroll.getScroll();
                        bookScrollsYPosition = bookScrolls.y;
                       }
                       
                       var y = pageCoords.y + yOffset + bookScrollsYPosition + yo;
                    
                    content.getParent().getParent().getParent().setStyles({
                        width: width + 'px',
                        top: y + 'px',
                        left : x + 'px'
                    })
                });
            }
        },
        add: function() {
            var instance    = this;
            var trigger     = this.container;
            var content     = this.content;
            var bCloseLinks = content.getElements('.bClose');
            var buttons     = content.getElements('.sButton');
            
            trigger.setStyle('cursor','pointer');
            
            // setting content to be grabbed by Tips class
            trigger.store('tip:text', content);
            
            // removing title content for this class
            trigger.store('tip:title', ''); 

            // set up our tip and give it a very long hide delay
            // so we can manipulate the styles on and off via click event
            this.tip_instance = new Tips(trigger, {
                className: 'y-tip',
                fixed: true,
                hideDelay: ( 24 * 60 * 60 ),
                showDelay: 0,
                onShow: function() {
                    content.getParent('.y-tip').setStyle('display','none');
                },
                onHide: function() {
                    content.getParent('.y-tip').setStyle('display','none');
                }
            });
            
            trigger.addEvent(this.triggerEvent, function(event) {
                if( this.first ) {
                    setTimeout( function() {
                        this.updateStyles( event );
                        this.repositionAndResize( event );
                    }.bind(this), 1 );
                } else {
                    this.updateStyles( event );
                    this.repositionAndResize( event );
                }
            }.bind(this));
            
            // giving any buttons in the content a click event to close bubble
            if( buttons ) {
                buttons.addEvent('click', function(e) {
                    this.closeAllBubbles();
                }.bind(this));
            }
            
            // giving any inputs blur close events
            if( this.options.killOnBlur !== false 
                && trigger.get('tag') == 'input' 
                && trigger.getProperty('type') == 'text' ) {
                
                trigger.addEvent('blur', function() {
                    this.closeAllBubbles();
                }.bind(this));
            }
            
            // giving any buttons in the content a click event to close bubble
            if( bCloseLinks ) {
                bCloseLinks.each(function(link) {
                    link.addEvent('click', function() {
                        this.closeAllBubbles();
                        try { link.focus(); }
                        catch(e) { /* let it silently die */ }
                                                 
                        return false;
                    }.bind(this));
                }.bind(this));
            }            
        },
        updateStyles: function( event ) {
            this.closeAllBubbles();
            this.addCloseEvents();
            // extend target
            var target = $(event.target);
            
            if( this.isToolTip ) {
                var killerHandler = function(e) {
                    this.container.removeEvent('mouseout', killerHandler)
                    this.closeAllBubbles();
                }.bind( this );
                this.container.addEvent('mouseout', killerHandler);
            }
            
            if(this.first) {
                this.buildBubble();
                this.first = false;
            }
            
            if( this.container !== target ) {
                if( this.content.getStyle('display') == 'block' ) {
                    this.content.setStyle('display','none');
                    this.content.getParent().getParent().getParent().setStyle('display','none');
                    this.content.getParent().getParent().getParent().setStyle('visibility','hidden');
                    
                    // I'm doing this in order to fix the hover repositioning 
                    // that tips does when you dont have enough viewport space for the tip
                    this.tip_instance.attach(trigger);
                } 
            }
            
            // if trigger equals the event target or content is currently hidden
            if( ( this.content.getStyle('display') == 'none' ) || ( this.container === target ) ) {
            
                this.fireEvent('show');
            
                this.content.setStyle('display','block');
                this.content.getParent().getParent().getParent().setStyle('display','block');
                this.content.getParent().getParent().getParent().setStyle('visibility','visible');
                
                // I'm doing this in order to fix the hover repositioning 
                // that tips does when you dont have enough viewport space for the tip 
                this.tip_instance.detach(this.container);
                
                if( this.scroll ) {
                    // giving body a scroll event to close bubble
                    $('bookScrollWrap').addEvent('scroll', function() {
                        this.closeAllBubbles();
                        $('bookScrollWrap').removeEvents('scroll');
                    }.bind(this));
                }
            }
        
            // return false to the link so url isn't executed
            // should be a link to a page version of the action
            if( this.container.get('tag') == 'a' ) return false;
            return true;
        },
        applyHeight: function() { 
                    
            // calculate content height
            var contentHeight = this.content.getParent().getSize().y;
            var maxHeightInt = this.maxHeight.toInt();
            
            // if contentHeight is greater than setMaxHeight make scrolling content
            if( contentHeight > maxHeightInt ) {
                this.content.setStyles({
                    'height': this.maxHeight,
                    'overflow': 'auto'
                });
            } else {
                this.content.setStyles({
                    'height': 'auto',
                    'overflow': 'visible'
                });
            }
        }, 
        buildBubble: function(caretPos) {
        
            // tip divs
            var text = this.content.getParent();
            var tipDiv = this.content.getParent().getParent();
            var top = this.content.getParent().getParent().getPrevious();
            var bottom = this.content.getParent().getParent().getNext();
            this.bubble = this.content.getParent().getParent().getParent();
            
            // containerize content container
            text.addClass('container');
            tipDiv.addClass('container');
            
            // elements that need to be injected for bubble layout
            var nw = new Element('div', {
                'class': 'nw',
                'html': "<div class='n'></div>"
            });
        
            // caret position
            if( this.caretPos == 'w' ) {
                this.caret = new Element('div', {
                    'class': 'wCarrot'
                });
            } else if( this.caretPos == 'nr' ) {
                this.caret = new Element('div', {
                    'class': 'nrCarrot'
                });
            } else if( this.caretPos == 'sr' ) {
                this.caret = new Element('div', {
                    'class': 'srCarrot'
                });
            } else {
                this.caret = new Element('div', {
                    'class': 'nCarrot'
                });
                this.caret.left = '36px';
            }
            
            // create markup structure we need for a more versatile bubble
            this.caret.inject(this.bubble);
            nw.inject(top);
            
            // add element to create south west and south images for bubble 
            // container
            if( this.sw == null ) {
                this.sw = new Element('div', {
                    'class': 'sw',
                    'html': "<div class='s'></div>"
                });
            
                this.sw.inject(bottom);
            }
            
            // make this bubble draggable
            if( this.drag ) {
                this.freeBubble();
            }
            
            // add a content container class to add external styling
            if( this.contentClass ) {
                this.content.addClass( this.contentClass );
            }
            
        },
        
        freeBubble: function() {

            var caret = this.caret;
            
            this.handle = new Element('div', {
                'class': 'bHandle'
            });
            
            this.shim = new Element('div', {
                'class': 'bShim'
            });
            
            // giving drag bubble close event
            if( this.drag ) {
                this.shim.addEvent('click', function() {
                    this.closeAllBubbles();
                }.bind(this));
            }
            
            // place the handle inside the bubble container
            this.shim.inject(this.bubble);
            this.handle.inject(this.bubble);
            
            var bubble_instance = this;
            
            // create a drab object for the bubble
            this.drag = new Drag(this.bubble, {
                snap: 0,
                handle: this.handle,
                onStart:function() {
                    caret.dispose();
                    
                    bubble_instance.freeBubbleTitle();
                }
            });
        },
        
        freeBubbleTitle: function() {
            // if title is set and the element has not been created yet
            if( this.title && !this.titleCreated ) {
            
                this.title = new Element('div', {
                    'html': '<p>' + this.titleText + '</p>',
                    'class': 'bTitle',
                    'styles':  {
                        // left: this.caret.left
                    }
                });

                // place element into the bubble
                this.title.inject(this.bubble);
                
                // element created, don't create again
                this.titleCreated = true;
            } else if ( this.title && this.titleCreated ) {
                this.title.inject(this.bubble);
            }
        },
        
        closeAllBubbles: function(e, instance) {

            // if triggered by an event
            if( e ) {
                var element = $(e.target);
                // check the target, if the click was from within a bubble, keep the bubble open
                // element *may* be an <object> or <embed> tag which IE does not support extending with mootools methods
                // therefore we have to use Element.getParent/hasClass rather than e.getParent
                var object = Element.getParent( element, '.y-tip' );
                if( object || Element.hasClass( element, 'bLink' ) ) {
                    return;
                }
            }
                
            $$('div.y-tip').setStyle('display','none');
        
            // if there are added tip events to the page close tips and remove events
            if( this.haveCloseEvents ) {
                $$('body').removeEvent('mousedown', this.closer);
                window.removeEvent('resize', this.closer);
                 
                // close events have been removed
                // set this in the bubble object
                this.haveCloseEvents = false; 
                  
                // fire hide events
                this.fireEvent('hide');
                
                // place caret back
                if( this.caret ) {
                    // place the caret inside the bubble
                    this.caret.inject(this.bubble);
                }
                
                // if we haven't assigned a title don't dispose of it
                if( this.titleCreated ) {
                        this.title.dispose();
                }
            }
        },

        addCloseEvents: function() {// when a bubble is shown, add listeners for events that will trigger a close
            var instance = this;
        
            if( !this.haveCloseEvents ) {
                this.haveCloseEvents = true;
                // kill tips on mousedowns in the body
                $$('body').addEvent('mousedown', this.closer);
                // making resize close bubbles
                window.addEvent('resize', this.closer);
            }
        }
});

