// 
//  op.js
//  htdocs
//  
//  Created by Sean on 2008-10-01.
//  Copyright 2008 Sean. All rights reserved.
// 

/**
 * Constructer for the OP namespace object.
 **/
  
var OP = (function(){
	return{
		widget: {} // init the widget object
	}
})();

/**
 *  Object for managing events and helper functions
 */
OP.utility = (function(){
	var queue = []; // Queue of events waiting to be fired onLoad.
	return{
		/**
		 * Handle event observers.  ATTN: Currently limited to one callback per element & behavior.
		 * Support for multiple callbacks needs to be implemented
		 *
		 * @param elem - object to observe
		 * @param behavior - string.  i.e. 'click' or 'mouseover'
		 * @param callback - reference to callback function
		 *
		 * @return void
		 */
		observe: function(elem,behavior,callback){
			elem['on'+behavior] = callback; // Pass string
		},
		
		/**
		 * Add load proceesses to queue
		 * This was implemented as temporary solution so we could support multiple callback for onLoad event
		 *
		 * @param func - reference to callback function
		 *
	 	 **/
		addLoadProcess: function(func){
			queue.push(func);
		},
		
		/**
		 *
		 * Handle page load.  Fire off all callbacks waiting for this event
		 *
		 *
		 **/
		onLoad: function(){
			for(var i=0; i < queue.length; i++){
				queue[i].call();
			}

			// WordPress theme preview will have offsetHeight set to 0 by the time this code is executed..
			if(!document.getElementById('content_s')) return;
			if(document.getElementById('content_s').offsetHeight == 0){
				OP.utility.previewObserver = OP.periodicalExecuter.call();
				OP.utility.previewObserver.init(function(){
					if(document.getElementById('content_s').offsetHeight != 0){
						OP.utility.setupDisplay();
						OP.utility.previewObserver.stop();
					}
				},.5);
			}
			else{
				OP.utility.setupDisplay();
			}
		},

		// Adjust floated div heights to max sibling
		/*
		 * args passed to getMaxAndAdjust are assumed by OnePress
		 * These assumptions are:
		 *	1.) #main, #sidebar, and #sidebar2 will be wrapped by #content_s
		 *  2.) div.column's are contained within #column_wrap
		 */		
		setupDisplay: function(){
				OP.utility.getMaxAndAdjust(document.getElementById('content_s'));
				OP.utility.getMaxAndAdjust(document.getElementById('column_wrap'));
				//Use javascript to dynamically assign content padding-bottom
				// This line can be commented out and left defined in style.css if footer's height will not change
				document.getElementById('content_s').style.paddingBottom = document.getElementById('footer').offsetHeight + 'px';			
		},

		/**
		 *
		 * JS helper method to handle adjusting the height of nested floated divs.
		 * We do this so all divs in a "row" can be the same height.  Helps much with the CSS
		 *
		 **/ 		
		getMaxAndAdjust: function(elem){
			var max=0, // variable used to track max height among elements
				notWhitespace = /\S/, // expression for testing whitespace
				i, j // for loops
				
			if(!elem) return;
			for(i=0; i < elem.childNodes.length; i++){
				// Browser DOM traversing cannot be trusted.  Check nodeType and nodeValue for whitespace before using
				// If the node is garbage, remove it and move index back.
				if ((elem.childNodes[i].nodeType == 3)&&(!notWhitespace.test(elem.childNodes[i].nodeValue))) {
					elem.removeChild(elem.childNodes[i]);
					i--;
				}
				else{
					max = Math.max(elem.childNodes[i].offsetHeight, max);
				}
			}
			for(j=0; j < elem.childNodes.length; j++){
				// Extra divs and spans cause problems.  We use span.forclear as a utility to clear floats
				// This check is to make sure that we do not apply the height to span.forclear
				if(elem.childNodes[j].className != 'forclear'){
					elem.childNodes[j].style.height = (max+1) + 'px'; // set element height to max
				}
			}
		}
	}
})();

OP.widget.collection = (function(){
	var _toload = [], // Store object of each widget meta
		_widgets = []; // Store objects
		
	return {
		/**
		 *	@param string observe_class: string name of the class to observe, widget.widgetname.construct() should use accept this as an argument.
		 *  @param object widget: reference to the object that represents a widget instance, construct should return _widget_id
		 *
		 */
		add: function(widget,observe_class){
			_toload.push({	'widget':widget, 'observe_class':observe_class	});
		},
		
	    load: function(){
	    	var elements, // object of all <div>'s in document
	    		h, i, // for loops
	    		tmp, // tmp instance of widget
	    		id; // id return from OP.widget.widgetname.construct
	    	
	    	elements = document.getElementsByTagName("div");
	    	for(h=0; h < _toload.length; h++){
			    for(i = 0; i < elements.length;i++){
		            if(elements[i].className == _toload[h].observe_class){ // must match class name of div containing tab li's
	                    tmp = _toload[h].widget.call(); // Get an instance of the widget
	                    
	                    id = tmp.construct(elements[i]);
	                    _widgets[id] = tmp; // store the instance using widget_id as the key.  This will make it easy to get it later.
	     	       }
	        	}
    		}
    		
        },
    
        
        /**
         * Public accessor method
         */
        getWidgetInstance: function(id){
        	return _widgets[id];
        }
	}
}());

/**
 *
 */
OP.periodicalExecuter = (function(){
	var _freq, _doaction;
	
	return{
		callback: null,
		
		init: function(callback,freq){
			this.callback = callback;
			_freq = freq;
			this.execute();
		},
		
		stop: function(){
			clearInterval(_doaction);
		},
		
		execute: function(){
			var _this = this;
			_doaction = setInterval(function(){ _this.callback.call(_this); }, _freq * 1000);
		},
		
		getCallback: function(){
			return _callback;
		}
	}
});

OP.utility.addLoadProcess(OP.widget.collection.load);
//  Fire off onLoad callbacks
OP.utility.observe(window,'load',OP.utility.onLoad);