/*
 * TriMM Modular Progressive Enhancement Framework 
 * Copyright TriMM Interactive Media.
 * @Author: Michiel Kikkert 
 * @Version: 1.1
 * @Rev: 1706 
 * @Date: March 2011
 * Needs the jQuery Library.
 * Expects a modules file containing _TriMM.module objects to parse.
 * 
 *********
 * USAGE *
 *********
 * Include TriMM-Framework.js
 * Include Customername-Modules.js
 * Include jQuery library
 * Include module dependent jQuery plugins
 * 
 * Starting the framework
 * 
 * //Create an [optional] overwrite config object (overwrites _TriMM.config items): 
 * var oOptions = {moduleOrder:['mainNav','productTree','carousel'],debug:true};
 * 
 * //Create an new Framework instance, passing in the [optional] overwrite object and an [optional] jQuery object (required if other libs like Prototype also exist on the page)
 * var application = new _TriMM.app(oOptions,jQuery.noConflict());
 * 
 * //Boot the framework: 
 * application.boot();
 * 
 * PLEASE NOTE:
 * Inside the _TriMM namespace object (including its modules), jQuery has been abstracted as __$ (underscore, underscore, dollar). 
 * 
 */


/* 
 * Global Error and Trace handling
 */

(function(){
	window.trace = []; 
	window.onerror = function(msg,url,line){
		window.trace.push(msg + " - Line:"+line);
		
		if(typeof _TriMM.utils.trace !='undefined'){
			var err = {};
			err.msg = msg; err.url = url; err.line = line;
			_TriMM.utils.trace("WINDOW.ONERROR","warn");
			_TriMM.utils.trace(err,"error");
		}
		
		return true;
	};
	
	
	// determine if "showTrace" exists in the URL:
	if(document.location.href.indexOf("showTrace") > 0){
		window.showTrace = true;
		
		window.onload = function(){	
			if(window.trace.length > 0){
				if(typeof window.console == 'undefined'){
					alert(window.trace.join("\n"));
				} else {
					window.console.dir(window.trace);
				}
			}
		}
		 
	}
})();

if(typeof _TriMM != 'object'){
	var _TriMM = {};
}

_TriMM = {
		// Here is where we set 'global' variables.
		config: { // Set default config options. Can be overwritten by passing in an options object to 'app'
			debug: false,
			benchmarkFile: '/js/benchmark.js',
			ajaxConfig: {
				dataType: 	"json",
				type:		"POST",
				success: 	function(data){_TriMM.core.ajax.globalSuccess(data)},
				fail: 		function(){_TriMM.utils.trace("AJAX REQUEST FAILED", "warn");}
			},
			minjQueryVersion: "1.4.2",
			moduleOrder: []
		},
		metaData:[],
		modules: {},
		fn: {},
		registeredFunctions: [],
		registeredEvents: [],
		NS: '_TriMM',
		framework:{
			ready: false,
			timings: {},
			env: {}
		},
		path: [],
		
		app: function(options,jQueryObj){ 	// Main Constructor
			
			if(typeof jQueryObj == 'undefined'){ _TriMM.utils.trace("jQuery object not passed in, trying to assign default $");jQueryObj = window.$;}
			if(typeof options.minjQueryVersion !="undefined"){_TriMM.config.minjQueryVersion = options.minjQueryVersion};
			if(typeof options.debug !="undefined"){_TriMM.config.debug = options.debug};
			if(!_TriMM.jQueryAssign(jQueryObj)){return {boot:function(){_TriMM.utils.trace("TriMM Framework failed to boot. jQuery missing","warn");}};};
			__$.extend(true, _TriMM.config, options);
			return _TriMM;
		},
		
		jQueryAssign: function(jQuery){
			try{
				// Test if jQuery passed in is really jQuery and if the version is correct:
				if(typeof jQuery == 'function'){
					// Could still be prototype
					if(typeof jQuery().jquery == "string"){
						if(!this.compareVersions(jQuery().jquery, _TriMM.config.minjQueryVersion)){
							throw("jQuery version is too old. The TriMM framework needs at least version "+ _TriMM.config.minjQueryVersion);
						} else {
							_TriMM.utils.trace("Found jQuery version "+jQuery().jquery);
							window['__$'] = jQuery;
							return true;
						}
					} else {
						throw("You passed something in the app constructor, but it wasn't jQuery!");
					}
				} else {
					throw("jQuery object not found. Please pass in a jQuery object into the app constructor");
				}
			} catch(e){
				_TriMM.utils.trace("FRAMEWORK ERROR","warn");
				_TriMM.utils.trace(e,"warn");
				return false;
			}
			return false;
		},
		
		compareVersions: function(dottedUsed,dottedRequired){
			var usedVersion = this.calcVersionNumbers(dottedUsed);
			var requiredVersion = this.calcVersionNumbers(dottedRequired);
			return (usedVersion >= requiredVersion);
		},
		
		calcVersionNumbers: function(dottedVersion){
			var aVersionParts = dottedVersion.split(".");
			var totalCount = 0;
			var start = 1;
			var base = 100;
			for(var i=aVersionParts.length;i > 0 ; i--){
				totalCount += (Math.pow(base,start)) * aVersionParts[i-1] ;
				++start;
			}
			return totalCount;
		},
		
		setEnv: function(){
			for(var browser in __$.browser){
				if(__$.browser[browser] == true){
					_TriMM.framework.env.browser = browser;
				} else {
					_TriMM.framework.env.browser_version= __$.browser[browser];
				}
			}
			var os="Unknown OS";
			var appv = navigator.appVersion;
			if (appv.indexOf("Win")!=-1) os="Windows";
			if (appv.indexOf("Mac")!=-1) os="MacOS";
			if (appv.indexOf("X11")!=-1) os="UNIX";
			if (appv.indexOf("Linux")!=-1) os="Linux";
			_TriMM.framework.env.os = os;
			if(typeof _TriMM.metaData['version'] !='undefined'){
				_TriMM.utils.trace("VERSION: "+_TriMM.metaData['version']);
				_TriMM.framework.version = _TriMM.metaData['version'];
			}
			if(typeof _TriMM.metaData['buildnumber'] !='undefined'){
				_TriMM.utils.trace("BUILD: "+_TriMM.metaData['buildnumber']);
				_TriMM.framework.build = _TriMM.metaData['buildnumber'];
			}
			
		},
		
		baseModule: {
				
				_construct: function(){
					_TriMM.utils.trace("MODULE:" + this.id + "::_construct() start");
					this.elements = __$(this.PEClass);
					if(this.elements.length == 0){
						_TriMM.utils.trace("MODULE:" + this.id + ":Not Applicable");
						return;
					}
					_TriMM.core.timer("Module_"+this.id,true);
					_TriMM.core.attachData(this.elements,"module");
					_TriMM.core.attachController(this,"controller");
					var module = _TriMM.modules[this.id];
					try{
						this.init();
					} catch(e){
						_TriMM.utils.trace("Could not run the init method on module:" + this.id,"warn")
						_TriMM.utils.trace(e,"warn");
					}
					_TriMM.core.timer("Module_"+this.id,false);
				}
		},
		
		event: {
			trigger: function(customEvent){
			_TriMM.registeredEvents.push(customEvent);
				__$(document).trigger(_TriMM.NS+"_"+customEvent);
			},
			bind: function(event,fn){
				_TriMM.utils.trace('EVENT:BIND:: '+_TriMM.NS+'_'+event);
				__$(document).bind(_TriMM.NS+'_'+event,fn);
			}
		},
		
		registerFunction: function(fn,event){
			try{
				_TriMM.registeredFunctions.push(fn+" -> "+event);
				_TriMM.event.bind(event,_TriMM.fn[fn]);
			} catch(e){
				_TriMM.utils.trace("Could not bind custom function "+fn,"warn");
			}
		},
		
		boot: function(){
			_TriMM.utils.trace("_TriMM.boot()");
			__$(document).ready(function(){
				// Bind Framework events
				_TriMM.event.bind('onFrameworkReady',function(e){
					_TriMM.framework.ready = true;
					_TriMM.utils.trace("EVENTS:REGISTERED " + _TriMM.registeredEvents.join(" :: "));
					_TriMM.utils.trace("FUNCTIONS:REGISTERED "+_TriMM.registeredFunctions.join(" :: "));
				})
				_TriMM.event.trigger('onBeforeFrameworkInit');
				_TriMM.core.init();
				_TriMM.event.trigger('onFrameworkReady');
			});
			
		}
};
/*
 * 
 * _TriMM core
 * 
 */

_TriMM.core = {
		/*
		 * _TriMM.core.init() method
		 * Determines main execution order of the framework.
		 * init() is called by $(document).ready which in turn
		 * is bound by _TriMM.boot();
		 * 
		 */
		init: function(){
			try{
				_TriMM.core.timer('Application',true);
				_TriMM.utils.trace("_TriMM.core.init() START");
				_TriMM.core.setMetaData();
				_TriMM.core.setPath();
				_TriMM.setEnv();
				_TriMM.core.tweakInterface();
				_TriMM.core.ajax.init();
				_TriMM.core.startModules();
				__$(window).scrollTop(0); // Fix for IE to prevent scrolling to halfway page.
				_TriMM.core.timer('Application',false,true);
				_TriMM.utils.trace("FRAMEWORK EXECUTION DONE in "+_TriMM.framework.timings.Application.total + " milliseconds");
			} catch(e){
				_TriMM.utils.trace("_TriMM.core.init() FAILED", "warn");
				_TriMM.utils.trace(e);
			}
		},
		
		startModules: function(){
			_TriMM.event.trigger('onBeforeModuleStart');
			try{
				if(_TriMM.modules){
					// First run modules defined in the moduleOrder array:
					__$.each(_TriMM.config.moduleOrder,function(index,obj){
						if(typeof _TriMM.modules[obj] == 'object'){
							var currentMod = _TriMM.modules[obj];
							__$.extend(currentMod,_TriMM.baseModule);
							currentMod.done = true;
							currentMod._construct();
						}
					});
					// Run the rest
					__$.each(_TriMM.modules, function(index, obj){
						if(!this.done){
							__$.extend(this,_TriMM.baseModule);
							this._construct();
						}
					});
				}
			} catch (e){
				_TriMM.utils.trace(e)
			}
			_TriMM.event.trigger('onModulesReady');
		},
			
		attachData: function(elements,type){
			try{
				__$.each(elements, function(index, obj){
					var oData = _TriMM.core.parseClassData(obj.className);
					__$(this).data(type,{data:oData});
				});
			} catch(e) {
				_TriMM.utils.trace("Error caught in _TriMM.core.attachData ", "warn");
				_TriMM.utils.trace(e);
			}
		},
		
		attachController: function(element,type){
			try{
				var elements = element.elements;
				__$.each(elements, function(index, obj){
					__$(this).data(type,{controller:element});
				});
			} catch(e) {
				_TriMM.utils.trace("Error caught in _TriMM.core.attachController", "warn");
				_TriMM.utils.trace(e);
			}
		},
		
		getModuleFromView: function(selector){
			
			if(typeof __$(selector).data("controller") !='undefined'){
				return __$(selector).data("controller").controller;
			} else {
				return false;
			}
		},
		
		addFormData: function(form,key,value){
			var existing = form.find('input[name*='+key+']');
			if(existing.length > 0){
				existing.val(value);
			} else {
				var input = __$('<input></input>');
				input.attr('type','hidden');
				input.attr('name',key);
				input.attr('value',value);
				input.appendTo(form);
			}
		},
		
		parseClassData: function(sData){
			var myregexp = /data=(\{.+\})/i;
			var match = myregexp.exec(sData);
			if (match != null) {
				result = match[1];
				try{
					if(eval("typeof "+result+" == 'object'")){
						return eval("("+result+")");
					}
				} catch(e){
					_TriMM.utils.trace("There is a problem with your 'data' attribute: "+ result,"warn");
					_TriMM.utils.trace(e, "warn");
				}
			} 
			return {};
		},
		
		setMetaData: function(){
			if(typeof window.metaData == 'object' || typeof window.metaData == 'array'){
				_TriMM.metaData = window.metaData;
			}
			_TriMM.event.trigger('onSetMetaDataReady');
		},
		
		setPath: function(){
			if(typeof _TriMM.metaData['path'] !='undefined'){
				_TriMM.path = _TriMM.core.parsePath(_TriMM.metaData['path']);
			} else {
				_TriMM.path = [];
			}
			_TriMM.event.trigger('onSetPathReady');
		},
		
		tweakInterface: function(){
			_TriMM.event.trigger('onBeforeTweakInterface');
			/* Attach browser name to html element class */
			__$('html').addClass(_TriMM.framework.env.browser);
			/* Open download links in new window */
			__$("._blank").attr("target","_blank");
			__$(".no-autocomplete").attr("autocomplete","off");
			_TriMM.event.trigger('onTweakInterfaceReady');
		},
		
		timer: function(item,start,done){
			var date = new Date();
			if(start){
				_TriMM.framework.timings[item] = new Array();
				_TriMM.framework.timings[item].start = date.getTime();
			} else {
				_TriMM.framework.timings[item].stop = date.getTime();
				_TriMM.framework.timings[item].total = _TriMM.framework.timings[item].stop - _TriMM.framework.timings[item].start;
			}
			if(done){
				//_TriMM.utils.trace("Processing Times:");
				//_TriMM.utils.trace(_TriMM.framework.timings);
			}
		},
		
		benchmark: function(data){
			if(!data){
				_TriMM.core.timer("_Benchmark",true);
				_TriMM.core.ajax.send({type:'get', url:_TriMM.config.benchmarkFile, data:{}, success:_TriMM.core.benchmark});
			} else {
				_TriMM.core.timer("_Benchmark",false);
				_TriMM.utils.trace(_TriMM.utils.trace(_TriMM.framework.timings._Benchmark));
			}
		},
		
		ajax: {
			
			init: function(){
				__$.ajaxSetup(_TriMM.config.ajaxConfig);
			},
			
			send: function(options){
				__$.ajax(options);
			},
			
			globalFail: function(){
				_TriMM.utils.trace("Ajax Fail", "warn")
			},
			
			globalSuccess: function(data){
				_TriMM.utils.trace("Ajax Success");
				_TriMM.utils.trace(data);
			}
			
		},
		
		parsePath: function(path){
			
			var arrPath = path.split("/");
			var arrTemp = [];
			
			for(var i=0;i<arrPath.length;i++){
				if(arrPath[i] !=""){arrTemp.push(arrPath[i])}
			}
			
			if(arrTemp.length == 0){arrTemp.push("/");}
			
			return arrTemp;	
		}	
}

/*
 * 
 *  _TriMM Utils 
 *  
*/

_TriMM.utils = {
		
	trace: function(trace, level){
		try{
			if(!window.trace) window.trace = []; window.trace.push(trace);
			if(_TriMM.config.debug || window.showTrace){
				if(typeof console =='object') {
					if(typeof trace =='string' || typeof trace=="number" || typeof console.dir !='function'){
						var d = new Date();
						if(level && console[level]){
							console[level]("Trace: ["+d.getTime()+"] "+trace);
						} else {
						console.info("Trace: ["+d.getTime()+"] "+trace);
						}
					}else {
						console.dir(trace);
					}
				}
			}
		} catch (e) {}
		
	}
};
