﻿// JavaScript Document

/*-------------------------- Reflection Utility --------------------------*/
var JavaScript = (function() {
	function findScriptElement(identifier) {
		return $A(document.getElementsByTagName('script')).find(function(script) {
			return script.src && script.src.indexOf(identifier) >= 0;
		});
	}
	
	return {
		getPath: function(identifier) { return findScriptElement(identifier).src; }
	}
})();


/*-------------------------- Core Extension --------------------------*/
Object.extend(String.prototype, {
	addQueryParam: function(key, value, preserveChars) {
		var app = preserveChars 
			? key + '=' +  + (typeof value === 'undefined' ? '' : '=' + value) 
			: encodeURI(key) + (typeof value === 'undefined' ? '' : '=' + encodeURI(value));
		
		return this + (this.indexOf('?') >= 0 ? '&' : '?') + app;
	},
	getDirectoryPath: function() {
		var slashx = this.lastIndexOf('/');
		return slashx ?  this.substring(0, slashx) : '';
	},
	rest: function(sequence) {
		var index = this.indexOf(sequence);
		return index >= 0 ? this.substring(index + sequence.length) : null;
	}
});


Array.prototype.sum = function() {
	var sum = 0;
	for(var i = 0; i < this.length; i++)
		sum += this[i];
	return sum;
}


window.location.getQueryParams = function() {
	return window.location.search.substring(1).toQueryParams();
}


/*-------------------------- Matchers --------------------------*/
var Matchers = {
	hasAttribute: function(attribute) {
		return function(element){
			return Object.isElement(element) 
				&& element.hasAttribute(attribute);
		};
	},
	tagName: function(tagName) {
		return function(element) {
			return element.nodeType == Node.ELEMENT_NODE 
				&& element.tagName == tagName.toUpperCase(); 
		}
	}
};


/*-------------------------- Foundation classes --------------------------*/


var Stylesheet = (function() {
	function createStylesheet() {
    var element = document.createElement('style');
    document.getElementsByTagName('head')[0].appendChild(element);
    return Prototype.Browser.IE ? element.styleSheet : element.sheet;
	}
	
	function stylesheet(stylesheet) {
		this.stylesheet = stylesheet || createStylesheet();
	}
	
	stylesheet.prototype = {
		addRule: function(selector, styles) {
			if(this.stylesheet.insertRule) {
				this.stylesheet.insertRule(selector + '{' + styles + '}', this.stylesheet.cssRules.length);
				return this.stylesheet.cssRules.length - 1;			
			} else {				
				this.stylesheet.addRule(selector, styles);
				return this.stylesheet.rules.length - 1;													
			}
		},
		removeRule: function(index) {
			if(this.stylesheet.deleteRule) {
				this.stylesheet.deleteRule(index);
			} else {
				this.stylesheet.removeRule(index);																	
			}
		},
		addRules: function(map) {
			for(var key in map)
				this.addRule(key, map[key]);
		}
	};
	
	stylesheet.instance = new stylesheet(document.styleSheets.length 
																			 ? document.styleSheets[0] : createStylesheet());
	return stylesheet;
})();


var LayoutManager = (function() {
	Stylesheet.instance.addRule('.X-Has-Layout', 'position:relative;');
	var layouts = {};
	
	function getIntStyle(element, str) {
		var value = $(element).getStyle(str);
		return !value ? 0 : (parseInt(value) || 0);
	}
	function acquireExtraWidth(element, where) {
		element = $(element);
		switch(where) {
			case 'width': return $w('border-left-width border-right-width margin-left margin-right padding-left padding-right')
				.map(getIntStyle.curry(element)).sum();
			case 'height': return $w('border-top-width border-bottom-width margin-top margin-bottom padding-top padding-bottom')
				.map(getIntStyle.curry(element)).sum();
		}
	}
	
	function getMargins(element) {
		return {
			width: acquireExtraWidth(element, 'width'),
			height: acquireExtraWidth(element, 'height')
		};
	}
	
	function findRecursively(element, matcher) {
		while(element) {
			if(!!matcher(element))
				return element;
			element = element.parentNode;
		}
	}
	
	function setStyle(element, style) {
		for(var key in style)
			if(typeof style[key] === 'undefined')
				delete style[key];
		element.setStyle(style);
	}
	
	
	function manager(element) {
		this.element = $(element).addClassName('X-Has-Layout');
		this.panes = [];
		this.sublayouts = [];
		this.identity = this.element.identify();
		layouts[this.identity] = this;
		
		var parent = findRecursively(this.element, Matchers.hasAttribute('X-Layout-Derived-From'));
		if(parent) 
			manager.get(parent.readAttribute('X-Layout-Derived-From')).sublayouts.push(this.identity);
	}	
	
	
	manager.prototype = {
		addPane: function(element, options) {
			element = $(element);
			options = options || {};
			this.panes.push({
				element:element,
				width:options.width,
				height:options.height,
				from: options.from
			});
			element.writeAttribute('X-Layout-Derived-From', this.identity);
			element.setStyle({'position': 'absolute', 'overflow':'auto'});
			return this;
		},
		doLayoutAndRegisterWindowListener: function() {
			this.doLayout();
			Event.observe(window, 'resize', this.doLayout.bind(this));
		},
		doLayout: function() {
			this.relocate();
			this.sublayouts.map(manager.get).invoke('doLayout');
		},
		relocate: function() {
			var layout = this.element.getLayout();
			var dimensions = {
				width: layout.get('width'),
				height: layout.get('height')
			};
			var offsets = {left: 0, top: 0};
			var zIndex = this.panes.length;
			this.panes.each(function(pane) {
				var margins = getMargins(pane.element);
				
				pane.element.style.zIndex = zIndex--;
				if(pane.height != undefined) {
					var height = pane.height == 'auto'
						? pane.element.getHeight()
						: pane.height;
					
					if(pane.from == 'bottom') {
						setStyle(pane.element, {
							'width': (dimensions.width - margins.width) + 'px',
							'height': (height - margins.height) + 'px',
							'top': (offsets.top + dimensions.height - (pane.height + margins.height)) + 'px',
							'left': offsets.left + 'px'
						});
						dimensions.height -= height + margins.height;
					}
					else {
						setStyle(pane.element, {
							'width': (dimensions.width - margins.width) + 'px',
							'height': (height - margins.height) + 'px',
							'top': offsets.top + 'px',
							'left': offsets.left + 'px'
						});
						offsets.top += height + margins.height;
						dimensions.height -= height + margins.height;
					}
				}
				else if(pane.width != undefined) {
					setStyle(pane.element, {
						'width': (pane.width - margins.width) + 'px',
						'height': (dimensions.height - margins.height) + 'px',
						'top': offsets.top + 'px',
						'left': offsets.left + 'px'
					});
					offsets.left += pane.width + margins.width;
					dimensions.width -= pane.width + margins.width;
				}
				else {
					setStyle(pane.element, {
						'width': (dimensions.width - margins.width) + 'px',
						'height': (dimensions.height - margins.height) + 'px',
						'top': offsets.top + 'px',
						'left': offsets.left + 'px'
					});
					offsets.left = 0;
					dimensions.width = 0;
				}
			}, this);
		}
	}
	
	Object.extend(manager, {
		get: function(identity) {
			return layouts[identity];
		},
		hasLayout: function(identity) {
			return !!layouts[identity];
		}
	});
	return manager;
})();



var TreeView = (function(){
	function treeview(element) {
		this.element = $(element);
		this.element.on('click', function(e) {
			var src = e.element();
			if(src.hasClassName('expando'))
				this.onToggle(src);
			else if (typeof src.readAttribute('path') !== 'undefined')
				this.onClick(src);
		}.bind(this));
	}
	
	treeview.prototype = {
		onToggle: function(expando) {
			var element = $(expando.parentNode);
			var pathAnchor = element.down('A[path]');
			var subtree = element.childElements().find(Matchers.tagName('ul'));
			if(subtree) {
				element.toggleClassName('open');
			} else {
				if(!element.hasClassName('no-subtree')) {
					var path = pathAnchor.readAttribute('path');
					this.onMissingChildren(path, function(populations){
						if(populations && populations.length) {
							var children = new Element('ul');
							populations.each(function(p){
								var line = new Element('li');							
								var expando = new Element('a', {'class': 'expando'});
								var label = new Element('a', {'path': path + '/' + p.path});
								label.innerHTML = p.path.split('/').last();
								line.appendChild(expando);
								line.appendChild(label);
								children.appendChild(line);
							});
							element.appendChild(children);
							element.toggleClassName('open');
						} else {
							element.addClassName('no-subtree');
						}
					}.bind(this));					
				}
			}
		},
		onMissingChildren: Prototype.emptyFunction,
		onClick: Prototype.K
	};
	return treeview;
})();

