/************************
 * Patterns (patterns.js)
 */
Ss = window.Ss || {};
Ss.patterns = {};


/* SUBJECT */
Ss.patterns.Subject = Class.create({
	initialize: function() {
		this.observers = [];
	},
	subscribe: function(f) {
		this.observers.push(f);	
	},
	unsubscribe: function(f) {
		this.observers = this.observers.without(f);
	},
	notify: function(data) {
		this.observers.each(function(fn) {
			fn(data);
		});
	}
});


/* ABSTRACT MODEL */
Ss.patterns.Model = Class.create(Ss.patterns.Subject, {
	initialize: function($super, data) {
		$super();
		this.data = data;
	},
	set: function(data) {
		this.data = data;
		this.notify(data);
	}
});


/* ABSTRACT VIEWS */
Ss.patterns.View = Class.create({
	initialize: function(element, model, controller) {
		this.element = element;
		this.model = model;
		this.controller = controller;
		
		this.model.subscribe(this.update.bind(this));
		this._events();
	},
	observe: function(eType) {
		this.element.observe(eType, this.controller[eType]);
	},
	update: function(data) { 

		this.element.update(data);
	},
	_events: function() { /*abstract*/ }
});

Ss.patterns.CompositeView = Class.create(Ss.patterns.View, {
	initialize: function($super, element, model, controller) {
		$super(element, model, controller);
		this._views = [];
	},
	update: function() {
		this._views.invoke('update');
	},
	add: function(view) {
		this._views.push(view);
	},
	remove: function(view) {
		this._views = this.views.reject(function(v){return view == v});
	},
	get: function(index) {
		return this.children[index];
	}
});


/* ABSTRACT CONTROLLERS */
Ss.patterns.Controller = Class.create({
	initialize: function(model) {
		this.model = model;
	},
	click: function(event) { /*abstract*/ }
});
/*********************
 * Utilities (util.js)
 */
Ss = window.Ss || {};
Ss.util = {};
Ss.util.disableTextSelection = function(element) {
	element.onselectstart = function() { return false; };
	element.unselectable = "on";
	element.style.MozUserSelect = "none";
};
Ss.util.getElementText = function(element) {
	var ret = $A(element.childNodes).collect(function(c){
		if(c.nodeType != 8){
			return (c.nodeType != 1 ? c.nodeValue: Ss.util.getElementText(c))
		}
	}).join('');
	return ret.strip();
};
Ss.util.isIE6 = function() {
	return (Prototype.Browser.IE &&
		parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5))==6);
};

/*
 * creates hidden inputs in "form" for all form elements that
 * are a decendent of "node"
 * and creates onchange handlers to syncronize the values between
 * the decendent elements of "node" and the hiddne elements
 * created in "form"
 */
Ss.util.mirrorInForm = function(node, form, opts){
    var accept = {SELECT:true, INPUT:true, TEXTAREA:true, RADIO:true};
    var felems = node.descendants().findAll(function(x){
        return accept[x.nodeName];
    });

	var events = opts && opts.events || ['change'];

    var radios = {};
    var trans = {
        radio: {
            duplicate: function(elem, form){
                if(!radios[elem.name]){
                    var e = new Element('input', {type:'hidden'});
                    e.setAttribute('name', elem.name); // using set attribute for name since IE9 doesn't like object properties named 'name'
                    form.appendChild(e);
                    radios[elem.name] = e
                }
                if(elem.checked){
                    radios[elem.name].value = elem.value;
                }
                return radios[elem.name];
            },
            set: function(elem, clone){
                if( elem.checked ){
                    clone.value = elem.value;
                }
            }
        },
		def: {
            duplicate: function(elem, form){
                var e = new Element('input', {type:'hidden', value:elem.value});
                e.setAttribute('name', elem.name); // using set attribute for name since IE9 doesn't like object properties named 'name'
                form.appendChild(e);
                return e;
            },
            set: function(elem, clone){
                clone.value = elem.value;
            }
        }
    }

    var ident = function(elem){
        return (elem.type.toLowerCase() == 'radio' ? trans.radio : trans.def);
    }
	felems.each(function(x){
        var p = ident(x);
        var e = p.duplicate(x, form);
		var set = opts.set || p.set;
		var o = opts.observe_elem && opts.observe_elem(x) || x;
		for(var i = 0, l = events.length; i < l; i++){
			o.observe(events[i],function(evt){
				set(x, e);
			});
		}
    });
} 
/***********
 * Pulldown
 */
Ss = window.Ss || {};
Ss.Pulldown = Class.create(Ss.patterns.Subject, {
	initialize: function($super, element, trigger, content, contentContainer, titleBar) {
		$super();
		this.element = element
		this.trigger = trigger;
		this.content = content;
		this.contentContainer = contentContainer;
		this.titleBar = titleBar;
		this.state = "";
		this.collapse();
		this._events();
		this._tId;
		Ss.Pulldown.INSTANCES.set(this.element.identify(), this);
	},
	/* change state/collaspe/expand */
	collapse: function() {
		this._setState(Ss.Pulldown.STATES.collapsed);
	},
	expand: function() {
		this._setState(Ss.Pulldown.STATES.expanded);
		

		this.titleBar.update(this.trigger.cloneNode(true));
		this.titleBar.insert('<a class="close_btn_small"></a>');
		

		var width = this.contentContainer.getDimensions().width,
		    left =  this.contentContainer.cumulativeOffset().left,
		    vpWidth = document.viewport.getDimensions().width;
		
		if(width + left > vpWidth) {
			var edgeDistance = width + left - vpWidth + 20;
			this.contentContainer.setStyle({
				marginLeft: -edgeDistance + 'px'
			});
		}
	},
	/* change content */
	appendJson: function(jsonArr) {
		jsonArr = (Object.isArray(jsonArr) ? jsonArr : [jsonArr]);
		Ss.Pulldown.jsonToItems(jsonArr).each(
			function(item) {
				this._appendItem(item);		
			}, this);
	},
	appendContent: function(content) {
		$(this.content).insert(content);
	},
	replaceContent: function(content) {
		$(this.content).update(content);
	},
	clearContent: function(){
		this.replaceContent('');
	},
	/*change trigger*/
	appendToTrigger: function(content) {
		$(this.trigger).insert(content);
	},
	replaceTrigger: function(trigger) {
		$(this.trigger).update(trigger);
		$(this.trigger).insert('<span class="pulldown_open_icon">&#9660;</span>');
	},
	/* disable/enable automatic collapse on mouseout*/	
	disableAutoClose: function() {
		$(this.element).stopObserving('mouseout');
	},
	enableAutoClose: function() {
		$(this.element).observe('mouseout', this._mouseout.bind(this));
	},
	/* show/hide loading state */
	showLoading: function() {
		$(this.element).addClassName(Ss.Pulldown.COMPONENTS.loading);
	},
	hideLoading: function() {
		$(this.element).removeClassName(Ss.Pulldown.COMPONENTS.loading);
	},
	getElement: function() {
		return this.element;
	},
	destroy: function() {
		$(this.element, this.trigger, this.content, this.contentContainer).invoke('stopObserving');
		try { $(this.element).remove(); }
		catch(e) { }
	},
	
	/* private */
	_appendItem: function(item) {
		if(!$(this.content).select("UL").size() > 0) {
			$(this.content).insert(new Element("UL"));
		}
		$(this.content).select("UL").pop().insert(item);
	},
	_toggleState: function() {
		this.state == Ss.Pulldown.STATES.expanded ? this.collapse() : this.expand();
	},
	_events: function() {
		this.enableAutoClose();
		$(this.trigger).observe('click', this._click.bind(this));
		$(this.element).observe('mouseover', this._mouseover.bind(this));
		$(this.titleBar).observe('click', this.collapse.bind(this));
		Ss.util.disableTextSelection(this.trigger);
	},
	_click: function(event) {
		this._toggleState();
	},
	_mouseout: function(event) {
		if( !Position.within($(this.element), Event.pointerX(event), Event.pointerY(event)) &&
			!Position.within($(this.contentContainer), Event.pointerX(event), Event.pointerY(event)) &&
			this.state != Ss.Pulldown.STATES.collapsed)
		{
			this._tId = (function() {
				this.collapse();
			}).bind(this).delay(Ss.Pulldown.COLLAPSE_DELAY_SECONDS);
		}
	},
	_mouseover: function(event) {
		if(this._tId)
			window.clearTimeout(this._tId);
	},
	_reset: function() {
		$H(Ss.Pulldown.STATES).values().each(function(state) {
			$(this.element).removeClassName(state);
		}, this);
		this.contentContainer.setStyle({
				marginLeft: ''
		});
	},

	_setState: function(state) {
		this._reset();
		this.state = state;
		$(this.element).addClassName(state);
		this.notify({state: state});
	}
	
});


if(Prototype.Browser.IE) {
	Ss.Pulldown.addMethods({
		_showIframe: function() {
			if(!this._iframe) {
				var iframe = new Element('IFRAME', {
					frameborder: 0
				});
				var dim = $(this.contentContainer).getDimensions();
				iframe.setStyle({
					position: 'absolute',
					top: '0',
					left: '0',
					height: dim.height + 'px',
					width: dim.width + 'px',
					zIndex: '-1'
				});
				this.contentContainer.insert({top: iframe});
				this._iframe = iframe;
			}
			$(this._iframe).show();
		},
		_hideIframe: function() {
			if(this._iframe) {
				$(this._iframe).hide();
			}
		}
	});
}

/****************
 * Ajax Pulldown
 */
Ss.AjaxPulldown = Class.create(Ss.Pulldown, {
	initialize: function($super, element, trigger, content, contentContainer, titleBar, url, parameters, callback) {
		$super(element, trigger, content, contentContainer, titleBar);
		this.url = url;
		this.parameters = parameters;
		this.callback = callback;
		this.isLoaded = false;
	},
	load: function() {
		this.showLoading();
		this.isLoaded = true;
		var ap = this;
		var xhr = new Ajax.Request(ap.url, {
			parameters: ap.parameters,
			onSuccess: function(response) {
				ap.appendContent(response.responseText);
				response.responseText.evalScripts();
				ap._doCallback(response);
				ap.expand();
				ap.hideLoading();
			}
		});			
	},
	setCallback: function(f) {
		this.callback = f;
	},
	_doCallback: function(response) {
		if(Object.isFunction(this.callback)) {
			this.callback.call(this, response);
		}		
	},
	_click: function(event) {
		this[ this.isLoaded ? '_toggleState' : 'load']();
	}
});


/******************
 * Select Pulldown
 */
Ss.SelectPulldown = Class.create(Ss.Pulldown, {
	initialize: function($super, element, trigger, content, contentContainer, titleBar, charLimit) {
		$super(element, trigger, content, contentContainer, titleBar);
		this.charLimit = charLimit;
	},
	items: function() {
		return $(this.element).select('LI');
	},
	select: function(key) {
		var item = this.find(key);
		if(item) {
			return this._select(item);
		}
		throw("Item '" + key + "' not found");
	},
	find: function(key) {
		if(Object.isNumber(key) 
			&& this.items().length > key) 
		{
			return this.items()[key];
		}
		if(Object.isString(key)) {
			return this.items().find(function(item) {
				return (Ss.util.getElementText(item).toLowerCase() == key.toLowerCase())
			});
		}
		return false;
	},
	_appendItem: function($super, item) {
		this._registerItem(item);
		$super(item);
	},
	_select: function(item) {
		this.items().invoke("show");
		$(item).hide();
		this.replaceTrigger(this._toTrigger(item));
		this.collapse();
		this.notify({state: this.state, selected: item});
	},
	_toTrigger : function(item) {
		var triggerText = Ss.util.getElementText(item).truncate(this.charLimit);
		var icon = $(item).select("IMG").shift();
		var trigger = new Element('SPAN').update(triggerText);
		if(icon) {
			trigger.insert({top: icon.cloneNode(true)});
		}
		return trigger;
	},
	_events: function($super) {
		$super();
		this._registerItems();
	},
	_registerItems: function() {
		this.items().each(function(item) {
			this._registerItem(item);
		}, this);
	},
	_registerItem: function(item) {
		$(item).observe('click', (function(event) {
			this._select(item);
		}).bind(this));
	}
});


/***********************
 * Ajax Select Pulldown
 */
Ss.AjaxSelectPulldown = Class.create(Ss.SelectPulldown);
Ss.AjaxSelectPulldown.addMethods({
	initialize: function($super, element, trigger, content, contentContainer, titleBar, charLimit, url, parameters, callback) {
		$super(element, trigger, content, contentContainer, titleBar, charLimit);
		this.url = url;
		this.parameters = parameters;
		this.callback = callback;
		this.isLoaded = false;
	},
	load: Ss.AjaxPulldown.prototype.load,
	setCallback: Ss.AjaxPulldown.prototype.setCallback,
	_click: Ss.AjaxPulldown.prototype._click,
	_doCallback: function(response) {
		this._registerItems();
		Ss.AjaxPulldown.prototype._doCallback.apply(this);
	}
});


/**************************
 * Class Properties/Methods
 */
Ss.Pulldown.INSTANCES = new Hash();
Ss.Pulldown.COLLAPSE_DELAY_SECONDS = .5;
Ss.Pulldown.STATES = { //CSS class names applied/removed when current state changes
	collapsed: 'collapsed',
	expanded:  'expanded'
};
Ss.Pulldown.COMPONENTS = { //CSS class names used for various elements/subcomponents
	element:            'pulldown',
	trigger:            'pulldown_trigger',
	contentContainer:   'pulldown_content_container',
	content:            'pulldown_content',
	loading:            'pulldown_loading',
	openIcon:           'pulldown_open_icon',
	titleBar:           'pulldown_title_bar'
};

Ss.Pulldown.get = function(id) {
	return Ss.Pulldown.INSTANCES.get(id);
};

Ss.Pulldown.create = function(id) {
	return Ss.Pulldown.construct(Ss.Pulldown.createElement(id));
};

Ss.Pulldown.construct = function(element) {
	return new Ss.Pulldown(element, 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.trigger), 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.content),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.contentContainer),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.titleBar));
};
Ss.AjaxPulldown.construct = function(element, url, parameters, callback) {
	return new Ss.AjaxPulldown(element,
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.trigger), 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.content),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.contentContainer),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.titleBar),
		url, parameters, callback);
};
Ss.SelectPulldown.construct = function(element, charLimit) {
	return new Ss.SelectPulldown(element, 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.trigger), 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.content),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.contentContainer),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.titleBar),
		charLimit);
};
Ss.AjaxSelectPulldown.construct = function(element, charLimit, url, parameters, callback) {
	return new Ss.AjaxSelectPulldown(element,
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.trigger), 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.content),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.contentContainer),	
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.titleBar),
		charLimit, url, parameters, callback);
};

Ss.Pulldown.createElement = function(id) {
	var t = new Template('<div class="#{trigger}"><a class="#{openIcon}">&#9660;</a></div><div class="#{contentContainer}"><div class="#{content}"></div><div class="#{titleBar}"><a>&#215;</a></div></div>');
	var element = new Element("DIV", {"class": Ss.Pulldown.COMPONENTS.element}).insert(t.evaluate(Ss.Pulldown.COMPONENTS));
	return ( id ? element.writeAttribute({"id":id}) : element );
};

Ss.Pulldown.getComponent = function(element, type) {
	return $(element).select("." + type).shift();
};

Ss.Pulldown.jsonToItem = function(obj) {
	var anchor = new Element('A').update(obj.name);
	var item = new Element('LI').update(anchor);	
	if(obj.icon_src) {
		anchor.insert({top: new Element('IMG', {src: obj.icon_src})});
	}
	if(Object.isString(obj.onclick)) {
		item.observe('click', function() {eval(obj.onclick);})
	}
	else if(Object.isFunction(obj.onclick)) {
		item.observe('click', obj.onclick);
	}
	return item;
};
Ss.Pulldown.jsonToItems = function(jsonArr) {
	var items = [];
	jsonArr.each(function(obj) {
		items.push(Ss.Pulldown.jsonToItem(obj));
	});
	return items;
};


Ss.Pulldown.convert = function(select, id, charLimit) {
	var sOptions = select.select('option');
	var sPulldown = Ss.SelectPulldown.construct(Ss.Pulldown.createElement(id), charLimit);
	var optionsJson = sOptions.collect(function(option, i){return {name: option.text, index: i};});
	sPulldown.appendJson(optionsJson);
	sPulldown.select(select.options[select.selectedIndex].text);
	sPulldown.subscribe(function(state){
		var selectedItem = state.selected;
		if(selectedItem){
			var selectedText = Ss.util.getElementText(selectedItem);
			var itemObj = optionsJson.find(function(obj){return obj.name == selectedText});
			select.selectedIndex = itemObj.index;
		}
	});
	select.insert({after: sPulldown.element});
	select.hide();
	return sPulldown;
};
Ss = window.Ss || {};
Ss.share = {
	pulldowns: [],
	translatedToError: "",
	translatedFromError: "",
	translatedAddressError: "",
	
	closeEmailForm: function(e) {
		$(e).down('.network-form').reset();
		$(e).down('.message-area').hide();
		$(e).down('.email-form-container').hide();
		$(e).down('.network-errors').update();
		$(e).down('.social_network_list').show();
	},
	
	openEmailForm: function(e) {
		$(e).up('.social_network_list').hide();
		$(e).up('.share_interface_content').down('.email-form-container').show();
	},
	

	addPulldown: function(pulldown) {
		this.pulldowns.push(pulldown);
	},
	
	getPulldown: function(e){
		return this.pulldowns.find(function(p){ return e.descendantOf(p.getElement()); });
	},
	
	toggleNetworkEmail: function(e) {		
		this.openEmailForm(e);
		

		var pulldown = this.getPulldown(e);
		

		pulldown.reconfigure();
		

		pulldown.disableAutoClose();
		

		pulldown.subscribe( function(data) {
			if(data.state == Ss.Pulldown.STATES.collapsed) {
				pulldown.enableAutoClose();
				Ss.share.closeEmailForm(pulldown.getElement());
				pulldown.unsubscribe(arguments.callee);
			}
		});
	},
	
	sendNetworkEmail: function(t) {
		var container = $(t).up('.share_interface_content');
		if (this.validateAddresses($(t).up('form'))) {
			$(t).up('form').request({
				onFailure: function(){ 
					alert('Sorry, your message was not sent.');
				}
			});
			container.down('.email-form-container').hide();
			container.down('.message-area').show();
		}
	},
	
	validateAddresses: function(form) {
		var addressList = new Array();
		var errors      = new Array();
		var fromAddress = form.getInputs('text', 'from')[0].getValue();
	
		if (!fromAddress) {
			errors.push(this.translatedFromError);
		}
	
		var toAddresses = form.getInputs('text', 'to')[0].getValue().split(/\s*\,\s*/);
	
		if (toAddresses && !toAddresses[0]) {
			errors.push(this.translatedToError);
		}
	
		var allAddressesValid = true;
		var translatedAddressError = this.translatedAddressError;
		$A([[fromAddress], toAddresses]).flatten().each(function(address) {
			if (!address.match(/[\w.-]+\@[\w.-]+\.[\w]{2,5}/)) {
				allAddressesValid = false;
				errors.push( translatedAddressError + " '" + address + "'");
			}
		});
	
		if (!allAddressesValid) { 
			var errorMessage = errors.shift();
			form.previous().update(errorMessage);
		}
		return allAddressesValid;
	},
	
	open: function(path) {
		window.open(path);
	},
	
	close: function(e) {
		this.getPulldown(e).collapse();
	}
};





