var TabManager = new $H();
var TabManager = {	};
TabManager.Manager = Class.create();
TabManager.Manager.prototype = {
	id: Math.random(),
	currentIndex: 0,
	listeners: new $A(),
	ignoreNonWindowKeyDowns : false,
	initialize: function() {
		// add an onload function to go thru and register the key listeners
		Event.observe(window, 'load', this.registerKeyListeners.bindAsEventListener(this), false);
	},	
	registerKeyListeners: function() {
		this.listeners.each(
			(function(listener){
				if( listener.tmElementRegistered != true ) {
					listener.tmElementRegistered = this.registerKeyListener(listener.tmElement);
				}
			}).bind(this)
		);
	},
	registerKeyListener: function(element) {
		Logger.logDebug('registering keyListener:'+element);
		if( this.keyObserver == null ) {		
			this.keyObserver = this.keyListener.bindAsEventListener(this);
		}
		if( $(element) != null ) {
			Event.observe(element, 'keydown', this.keyObserver, false);
			return true;
		}
		return false;
	},
	unregisterKeyListener: function(element) {		
		Logger.logDebug('unregistering keyListener');
		
		if( this.keyObserver != null ) {
			Event.stopObserving(element, 'keydown', this.keyObserver);
			this.keyObserver = null;
		}
	},	
	keyListener: function(event){
		if(event.keyCode == Event.KEY_TAB) {
			// move to next editor in list
			var shiftKeyPressed = event.modifiers & Event.SHIFT_MASK || event.shiftKey;
			var incr = shiftKeyPressed ? -1 : 1; 
			this.openNextListener(incr);
			Event.stop(event);
			return false;
		}		
		return true;
	},
	registerTabListener: function(tabListener) {
		if( this.listeners.indexOf(tabListener) == -1 ) {
			Logger.logDebug('register new tab listener');
			var idx = this.listeners.length;
			this.listeners.push(tabListener);
			tabListener.tmIndex = idx;
		}
		// register the listener
		if( tabListener.tmElementRegistered != true ) {
			tabListener.tmElementRegistered = this.registerKeyListener(tabListener.tmElement);
		}
	},
	unregister: function(tabListener) {
		//TODO implement me!
	},
	listenerActivated: function(tabListener) {
		this.currentIndex = tabListener.tmIndex;
	},
	openNextListener: function(incr) {	
		Logger.logDebug('openNextListener: '+incr+', cur='+this.currentIndex+', len='+this.listeners.length);
		var nextListenerIdx = this.currentIndex + incr;
		if( nextListenerIdx >= this.listeners.length ) {
			nextListenerIdx = 0;
		}
		if( nextListenerIdx < 0 ) {
			nextListenerIdx = this.listeners.length - 1;
		}
		Logger.logDebug('nextIdx='+nextListenerIdx);
		var nextListener = this.listeners[nextListenerIdx];
		var currentListener = this.listeners[this.currentIndex];
		
		setTimeout((function() {
			if( currentListener != null ) {
				currentListener.tmDeactivate();
			}
			if( nextListener != null ) {
				nextListener.tmActivate();
			}
		}).bind(this), 10);
		this.currentIndex = nextListenerIdx;		
		return false;
    },
	suspend: function() {
		Logger.logDebug('suspend tab control');
	},
	resume: function() {
		Logger.logDebug('resume tab control');
	}
	
}

if( window.tabManager == null ) {
	window.tabManager = new TabManager.Manager();
}


// InPlaceEditor.Local

// hold a list of in place editors in page for tabbing purposes
var _inPlaceEditors = new $A();

var InPlaceEditor = {};
InPlaceEditor.Local = Class.create();
InPlaceEditor.Local.prototype = {
	inputShowing: false,
	okToBlur: false,
	initialize: function(divId, inputAttributes, options) {
		this.baseInitialize(divId, inputAttributes, options);
	},
	baseInitialize : function(divId, inputAttributes, options) {
		_inPlaceEditors.push(this);
		this.editorIndex = _inPlaceEditors.length-1;
	
		this.divId = divId;
		this.options = new $H(options);
		
		this.onShowInputCallback = this.options['onShowInputCallback'] || function(){};
		this.onShowDivCallback = this.options['onShowDivCallback'] || function(){};
		
		if(inputAttributes != null ){
			this.inputAttributes = inputAttributes;
		} else {
			this.inputAttributes = $H();
		}
		this.inputId = divId+'_input';
		
		this.registerForEvents();	
						
		this.setupInitialValue();

		// if only the input then don't need any of the listeners
		if( this.isShowInputOnly() ) return;

		this.tmElement = this.inputId;		
		tabManager.registerTabListener(this);
	},
	registerForEvents: function() {
		if( this.isShowInputOnly() ) return;
		Event.observe(this.divId, 'click', this.observerCallback.bind(this), true);
	},
	observerCallback: function(event){
		this.showInput(true);
		tabManager.listenerActivated(this);
	},
	unregisterForEvents: function() {
		Event.stopObserving(this.divId, 'click', this.observerCallback);
	},
	setupInitialValue: function() {
		if(this._isInitialValue() ) {
			Element.update(this.divId, this.options['initialValue']);	
			this.originalFgColor = $(this.divId).style.color;
		} else {
			this._updateToChangedClass();
		}
	},
	_isInitialValue: function() {
		var initialValue = this.options['initialValue'];
		var value = $EV(this.divId);
		return( (initialValue != null) && (value == '' || value == initialValue) );
	},
	insertAndBindTag: function(newInputTag) {
		var attrs = new $H();
		
		// copy over the default attributes
		Object.extend(attrs, this.inputAttributes);
		attrs.each( (function(attrEntry) {
			var name = attrEntry.key;
			var value = attrEntry.value;
			if( name == 'style' ) {
				Element.setStyle(newInputTag, value);
			} else {
				newInputTag[name] = value;
			}
		}).bind(this));
		var clazz = this.inputAttributes['className'];
		if( clazz != null ) {
	 	    Element.classNames(newInputTag).add(clazz);	 	    
 	    }
		
		this.formId = this.divId+'_form';		
		this.form = this.buildForm(this.formId);
		if( clazz != null ) {
	 	    Element.classNames(this.form).add(clazz);
 	    }
		
		this.insertForm(this.divId, this.form);
		$(this.formId).appendChild(newInputTag);
		

		// if only the input then don't need any of the listeners
		if( this.isShowInputOnly() ) return;

		this.bindKeyListener(newInputTag);
		 
	    Element.hide(this.inputId);		
		
		
		// add this after we've shown the input
		Event.observe(this.inputId, 'blur', (function(event) {
						if(!this.okToBlur) return;
						Event.stop(event);
						setTimeout( function() {
	  						this.showDiv(event);
							}.bind(this), 100);
		}).bind(this), true);

		// re-register with the tabListener so that it can attach to the input's events
		tabManager.registerTabListener(this);
	},
	buildForm: function(formId) {
		var form = document.createElement('form');
		form.style.display = 'inline';
		form.id = formId;
		form.onsubmit = (function() { 
			tabManager.openNextListener(1);
			return false; 
		}).bind(this);
		return form;
	},
	insertForm: function(divId, form) {
		var parent = $(divId).parentNode;
		parent.insertBefore(form, $(divId).nextSibling);
	},
	bindKeyListener: function(newInputTag) {
		Event.observe(newInputTag, 'keypress', this.handleKeys.bind(this), false);
	},
	handleKeys: function(event){
		if(this.inputAttributes['maxlength'] != null ) {
			// enforce maxlength
			var maxlength = this.inputAttributes['maxlength'];
			var target = event.target || event.srcElement;
			var value = target.value;
			if( value != null && value.length >= maxlength ) {
				setTimeout( function() { target.value = value.substring(0, maxlength-1); }, 1);
				return false;
			}
		}
    },
	buildTag: function(inputAttributes) {
		var tagname = 'input';
		if( inputAttributes['cols'] != null || inputAttributes['rows'] != null ) {
			tagname = 'textarea';
		}
		
		var newTag = document.createElement(tagname);
		newTag.id = this.inputId;
		newTag.name = this.inputId;
		newTag.style.display = 'none';		

		if( tagname == 'input' ) { 
			newTag.type = 'text';
		}
						
		return newTag;
	},
	showInput: function(event) {
		if(this.inputShowing) return;

		// position the tag over the div
		this._showInputInternal(event);
		this.onShowInputCallback();
		this.sendTrackingEvent();	
		setTimeout( function() {
			this.okToBlur = true;
		}.bind(this), 500);
	},
	showDiv : function(event) {	
		if( this.isShowInputOnly() ) return;
		this._showDivInternal();
		this.onShowDivCallback();
		this.okToBlur = false;
	},
	isShowInputOnly: function() {
		return (this.options['showInputOnly'] == true);
	},
	_showInputInternal : function(event) {	
		this.inputShowing = true;
		this.divShowing = false;
		if(this.inputTag == null) {
			this.inputTag = this.buildTag(this.inputAttributes);
			this.insertAndBindTag(this.inputTag);
		}
		
		this._hideDivTag();
		this._showInputTag();
		if( this.options['inline'] ) {
			$(this.inputId).style.display = 'inline';
		}
		var value = $EV(this.divId);
		if( encodeURIComponent(value) == '%C2%A0' ) value = '';
		if( value == '&nbsp;' ) value = '';
		this.setInputValue(value);		
	},
	_hideDivTag: function() {
		Element.hide(this.divId);			
	},
	_showInputTag: function() {
		if( $(this.inputId) != null ) {
			Element.show(this.inputId);
			if( Element.visible(this.inputId) ) {
				Field.focus(this.inputId);
			}
		}	
	},
	setInputValue : function(value) {	
		$(this.inputId).value = value;	
	},
	_showDivInternal : function(event) {
		this.divShowing = true;
		this.inputShowing = false;
		Element.show(this.divId);
		if( this.options['inline'] ) {
			$(this.divId).style.display = 'inline';		
		}
		
		Element.hide(this.inputId);
		$(this.divId).innerHTML = this.padNulls($F(this.inputId));
		this._notifyChange($F(this.inputId));
	},
	padNulls : function(str) {
		var regex = /^ *$/;
		if(str == null || regex.test(str)) {
			str = '&nbsp;';
		}
		return str;
	},
	getValue: function() {		
		if(	this.inputShowing ) {
			return $F(this.inputId);
		} else {
			var value = $EV(this.divId);
			return value;
		}
	},
	setValue: function(value) {	
		if(	this.inputShowing ) {
			$(this.inputId).value = value;
		} else {
			Element.update(this.divId,value);
		}
	},
	_notifyChange : function(newValue) {
		if(!this._isInitialValue() ) { 
			this._updateToChangedClass();
			
			if( this.originalFgColor != null ) {
				$(this.divId).style.color = this.originalFgColor;
			}
		}
		var callback = this.options['onChangeCallback'];
		if( callback != null ) {
//			setTimeout( function() {
			callback(newValue);
//			}.bind(this), 10);
		}

	},
	_updateToChangedClass: function() {
		var changedClass = this.options['changedClass'];		

		if( changedClass != null ) { 
			Element.classNames(this.divId).set(changedClass);
		}
	},
	destroy: function() {
		this.unregisterForEvents();
		Element.remove(this.inputId);
	},
	// TabListener implementation
	tmActivate: function() {
		this.showInput(true);
	},
	tmDeactivate: function() {
		this.showDiv();
	},
	// click tracking stuff
	sendTrackingEvent: function() {
				if( !this.sentTrackingInfo ) {
			 Tracker.sendTrackingEvent(this.divId);
			 this.sentTrackingInfo = true;
		}
	}
}
//
// InlaceCollectionEditor is an inline editor for multiple constrained values, ie dropdowns.
//
var InPlaceCollectionEditor = {};
InPlaceCollectionEditor.Local = Class.create();
Object.extend(InPlaceCollectionEditor.Local.prototype, InPlaceEditor.Local.prototype);
Object.extend(InPlaceCollectionEditor.Local.prototype, {
	initialize: function(divId, collection, inputAttributes, options) {
		this.collection = new $H(collection);
		this.baseInitialize(divId, inputAttributes, options);

		// pre-create the input
		setTimeout( (function() {
			this.inputTag = this.buildTag(this.inputAttributes);
			this.insertAndBindTag(this.inputTag);
		}).bind(this), 10);
	},
	buildTag: function() {
	   var selectTag = document.createElement("select");
	   this.selectTag = selectTag;
	   selectTag.id = this.inputId;
	   selectTag.name = this.inputId;	   
	   this.fillOptions(this.collection);
	   var size = this.options['size'];
	   if( size == null ) {
		   size = (this.collection.keys().length <= 5) ? this.collection.keys().length : 5;
	   }
	   selectTag.size = size;	
	   
	   var leftOffset = $(this.divId).offsetWidth + 5;
	   if( leftOffset ){
	   	   selectTag.style.left = 0-leftOffset+'px';
	   }

	   this.registerClickListener(selectTag);
	   
	   return selectTag;
	},
	registerClickListener: function(tag) {
	   if( this.isShowInputOnly() ) return;
	   // register for click events so we can close it afterward
	   Event.observe(tag, 'click', (function(event) {
					Event.stop(event);
					setTimeout( (function() {
						this.showDiv(event);
					}).bind(this), 100)
   				}).bind(this),
   			true
		);	   
	   
	},
	fillOptions: function(collection) {
	   this.collection = collection || new $H();
       var selectedValue = $EV(this.divId);
	   var selectedKey = (selectedValue != null) ? this.reverseLookup(selectedValue, collection) : null;
	   var optionTag;
	   this.collection.each(function(e) {
	        optionTag = document.createElement("option");
	        optionTag.value = (e.key instanceof Array) ? e.key[0] : e.key;
	        if( selectedKey == optionTag.value) optionTag.selected = true;
	        optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
	        this.selectTag.appendChild(optionTag);
	   }.bind(this));
	},
	updateOptions: function(collection) {
	   this.collection = collection;
	   // first clear out any existing options
	   if( $(this.selectTag) == null ) {
			Element.remove(this.selectTag);
	   }
	   this.inputTag = this.buildTag(this.inputAttributes);
	   this.insertAndBindTag(this.inputTag);	
	},
	setInputValue: function(value) {
		this.setSelectedKey(value);
	},
	_showDivInternal : function() {
		if( this.divShowing ) return;
		this.divShowing = true;
		this.inputShowing = false;
		Element.show(this.divId);
		if( this.options['inline'] ) {
			$(this.divId).style.display = 'inline';
		}
		
		Element.hide(this.inputId);
		var newVal = this.collection[$F(this.inputId)] || '&nbsp;';
		$(this.divId).innerHTML = newVal;
		this._notifyChange(this.collection[$F(this.inputId)]);
	},
	reverseLookup: function(value, map) {
	   var foundKey = null;
	   map.each(function(e) {
	   		if( e.value == value) {
	   			foundKey = e.key;
	   		}
	   });
	   return foundKey;
	},
	setSelectedKey: function(key) {
		if( key == null || key == '') return;
		if( this.collection[key] == null ) return;
		if( $(this.inputId) != null ) {
			$(this.inputId).value = key;			
		} 
		$(this.divId).innerHTML = this.collection[key];
	},
	// returns the dropdown's selected key
	getSelectedKey: function() {
		if( $(this.inputId) != null ) {
			return $F(this.inputId);
		} else {
			return $EV(this.divId);
		}
	},
	_hideDivTag: function() {
		// don't ever hide the div with this one
	}
});


Ajax.VWMAutocompleter = Class.create();
Object.extend(Object.extend(Ajax.VWMAutocompleter.prototype, Ajax.Autocompleter.prototype), {
  initialize: function(element, update, moduleId, methodName, options) {
  	this.moduleId = moduleId;
  	this.methodName = methodName;
    this.baseInitialize(element, update, options);
    this.onComplete = this.renderChoicesList.bind(this);
  },

  getUpdatedChoices: function() {
  	var params = this.options.requestParams || new $H();
  	params['token'] = this.getToken();

	moduleManager.call(null, this.moduleId, this.methodName, {
		onComplete: this.onComplete, 
		requestParams: params, 
		validationModuleIds: new Array()
	});
  },
  renderChoicesList: function(choicesList) {
  	 var list = '<ul>';
  	
  	 choicesList.each( function(item) {
  	 	list += '<li>'+item+'</li>';
  	 } );
  	 
  	 list += '</ul>';  	 
  	 
  	 this.updateChoices(list);
  }
});

var ComboBox = {};
ComboBox.Local = Class.create();
Object.extend(ComboBox.Local.prototype, Autocompleter.Local.prototype);
Object.extend(ComboBox.Local.prototype, {
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },
  
  toggleShow: function() {
  	if( !this.showing ) {
        this.startIndicator();
        this.getUpdatedChoices();
    	this.changed = true;
	    this.hasFocus = true;
		
    	if(this.observer) clearTimeout(this.observer);
	      this.observer = 
        	setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
		this.show();
		this.showing = true;		
	} else {
		this.hide();
		this.showing = false;
	}				  
  } ,

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector : function(instance) {
        var ret       = []; 
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&  
          ret.length < instance.options.choices ; i++) { 

          var elem = instance.options.array[i];
          ret.push("<li>" + elem + "</li>");
        }
        return "<ul>" + ret.join('') + "</ul>";
      }
    }, options || {});
  }
});

